Fix: webhook timing-safety + tokenization double-submit#2
Merged
Conversation
- `verifySumitSharedSecret`: hash both inputs with SHA-256 (Web Crypto) before constant-time compare. The previous early-return on length mismatch leaked the secret's byte-length via response timing. - `<SumitCheckout />`: replace the `submitting` state-based guard with a synchronous `useRef` flag so two rapid submits can't both fire `CreateToken` while React batches the state update (single-use tokens must stay single-use). - Add tests covering both fixes. - Add CLAUDE.md (architecture, security model, conventions). - README: badges and a Security table summarising threat handling. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI failed at the setup-node step: `cache: pnpm` requires `pnpm-lock.yaml`, but the repo gitignored it. Two changes here: 1. Stop ignoring `pnpm-lock.yaml` and commit it for reproducible installs. 2. Update the workflow to check out `Digitizers/sumit-api` as a sibling directory (sumit-react depends on `file:../sumit-api`) and build its dist/ before running sumit-react's typecheck and tests. Once sumit-api ships to npm, the second checkout step can be removed and the dev dependency switched to a regular semver range. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Addresses the two High-severity findings from a recent code review.
1. Webhook secret length leak via timing oracle
`verifySumitSharedSecret` previously called a `timingSafeEqual` that returned early on length mismatch — a classic timing oracle that lets an attacker measure response latency to learn the byte-length of the configured webhook secret. Replaced with a SHA-256 comparison: both inputs are hashed to a fixed-length 32-byte digest, then compared in a constant-time loop. The comparison now leaks neither content nor length.
Implementation uses Web Crypto (`crypto.subtle.digest`) so the route helper continues to work in both Node and Edge runtimes.
2. Tokenization double-submit race
`` guarded against double submission with `if (submitting) return`, where `submitting` is React state. Two rapid clicks (or `submit()` calls) both pass the guard before the first `setSubmitting(true)` re-renders, so both fire `sdk.CreateToken`. Single-use tokens are exactly that — single-use — so this race could waste tokens.
Replaced with a synchronous `useRef` flag (`submittingRef.current`) that is updated immediately. The visible `submitting` state remains for the disabled-input UI.
Bonus polish
Test plan
🤖 Generated with Claude Code