fix(billing): verify Stripe webhook raw-body signatures - #520
fix(billing): verify Stripe webhook raw-body signatures#520seonghobae wants to merge 21 commits into
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai review |
|
|
@opencode-agent Repair the verified current-stack regression on this existing branch only. Exact current head is |
|
Caution Review failedAn error occurred during the review process. Please try again later. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Hour-20 exact-head review request. Current head Keep HMAC successor. Do not restore unsigned plan upgrade (#589 stays closed). Do not self-approve. Do not --admin merge. Checks/reviews are not a reason to weaken fail-closed gates. |
|
Hour-21 exact-head review request. Current head @opencode-agent review |
|
Hour-22 exact-head review request. HMAC successor. Draft. Current head @opencode-agent review |
|
Hour-23: keep this HMAC successor. Draft head |
seonghobae
left a comment
There was a problem hiding this comment.
Exact-head re-verify 2026-09-02T09:44Z.
Current head ea6c2c4f1628e7402e442ea23553b9e76f33b03b. draft:true. mergeable_state clean. CLEAN is not two independent current-head APPROVEs. Zero reviews of any SHA on this PR before this COMMENT.
Keep this HMAC successor: server/stripe_webhook.mjs plus doctoring docs/doctoring/stripe-webhook-trust-boundary.md verify exact raw bytes and bounded body/signature/event before any delivery can move subscription or entitlement state. Do not restore unsigned plan upgrade. Do not treat a correctly signed delivery as authoritative until durable dedup and provider-state reconciliation exist. Do not un-draft to manufacture a merge.
This COMMENT is not an independent APPROVE. Do not merge without two independent current-head APPROVEs and exact-head Checks on this SHA. Ruleset 18156473. Copilot is a bot.
Buyer and security impact
This bounded stacked #488 slice establishes a strict Stripe webhook trust boundary before any webhook delivery can influence subscription or entitlement state. It verifies exact raw bytes, enforces bounded body/signature/event contracts, and makes even a correctly signed delivery non-authoritative until durable deduplication and provider-state reconciliation exist.
Exact current stack and scope
feat/stripe-checkout-reconciliation-ops-488@985747c4ed3dc369ee85df48f4923f5050c11209.9c72a3adfdfaeaf826f694e5a25699341c3358fe.a0c09334c58e5438c9b683ad381b0b30ad7eaf86and exact current parent985747c4ed3dc369ee85df48f4923f5050c11209.docs/doctoring/stripe-webhook-trust-boundary.md;package.json;server/app.mjs;server/application_routes.mjs;server/stripe_webhook.mjs;tests/api/stripe-webhook.test.mjs;tests/unit/coverage-script-contract.test.mjs;tests/unit/stripe-webhook-boundary.test.mjs.server/app.mjsis now a beginner-readable composition entry point: it restores protected/toast-state.cssdelivery first and mounts the webhook-hardened application route graph fromserver/application_routes.mjs. Both modules are registered in the owned-production coverage producer.No Clearfolio, attachment, general auth/session, dependency lockfile, workflow, scanner suppression, or branch-protection artifact is changed by the effective child diff.
Implemented trust boundary
Stripe-Signatureagainst the exact raw request bytes with HMAC-SHA-256 before JSON parsing;STRIPE_WEBHOOK_SECRETand a bounded five-minute timestamp tolerance;Cache-Control: no-storefrom/api/stripe/webhook;TDD and causal repair
The raw-body verifier and route regressions preceded the completed production implementation. The prior child inherited a real buyer-visible regression: its monolithic
server/app.mjsremoved the parent’s/toast-state.cssstatic allowlist entry. Predecessor Server Tests run32085643237,unit-and-apijob95557494897, reproduced the resulting404where the existing smoke contract required200.The current repair changes the causal ownership boundary instead of weakening the smoke test: the protected toast route is served by the public entry module before the child’s secure route graph is mounted. The monolithic child source is retained byte-for-byte as
application_routes.mjs, so the hardened webhook handler is not rewritten, and both entry and route modules remain included in exact owned-production coverage.Current exact-head evidence
Fresh repository-native workflows for exact child head
9c72a3adfdfaeaf826f694e5a25699341c3358feare queued and therefore non-passing:32271689983;32271690058; and32271690653.There are no submitted reviews or inline review threads on this exact head and no qualifying independent current-head approval. Predecessor successes and failures remain causal history only. Queued, pending, cancelled, skipped-required, neutral, absent, failed, stale, predecessor, status-only, synthetic-only, author-only, or model-only evidence is non-passing.
This PR remains Draft and must not integrate independently of #516, #511, #507, and #505.
Scope boundary
This PR does not implement durable webhook-event storage, event deduplication, authoritative subscription retrieval, out-of-order lifecycle reconciliation, or entitlement mutation. Those remain separate bounded #488 slices.
Merge gate
After the prerequisite stack reaches protected
develop, reconcile this exact bounded semantic diff onto the resulting live head and rerun every then-applicable CI, browser E2E, coverage/docstring, SAST, security, dependency, supply-chain, package/provenance, migration/recovery, resolved-thread, and qualifying independent-approval gate. No predecessor-head, author-only, skipped, neutral, status-only, synthetic, or model-only evidence transfers.Refs #488