Skip to content

[security][HIGH] webhook live binary wires the mock verifier; implement real HMAC + fail-closed #58

Description

@AojdevStudio

Source

Independent Forge (GPT-5.5) cross-vendor authority audit of the platform spine at main (commit 2be0a02). Verdict: CONCERNS.

Severity: HIGH (live binary trusts a constant webhook signature)

What

services/api/src/webhook.rs:130-136 (real StripeWebhookVerifier::verify is a stub that rejects everything), :93-100 (MockWebhookVerifier accepts the constant "mock_valid_signature", :42), wired into the live app() at services/api/src/lib.rs:95 and :117-123.

The default app() (the binary's router via main.rs:build_router) mounts MockWebhookVerifier, which accepts a hardcoded signature present in the source. The real StripeWebhookVerifier::verify returns Err(InvalidSignature) unconditionally and is wired nowhere.

Why it is a defect

As shipped, the running binary's webhook endpoint trusts a known constant, so anyone who reads the source can POST a forged checkout.session.completed and grant any account_id an active paid plan (apply_action -> set_account_state). The structure is honest (the real verifier fails closed, never silently accepts) and is the documented issue #32 deferral, but the live wiring is the mock.

Fix

Implement the real t/v1 HMAC-SHA256 plus constant-time compare in StripeWebhookVerifier::verify, and make the binary's app() select the real verifier whenever STRIPE_WEBHOOK_SECRET is present (mirror the /license/refresh env-gating so a configured deploy can never fall back to the mock). Add a fixture test with a real whsec_ secret. Until then the webhook reconcile path must be considered unauthenticated.

Note

Idempotency itself was verified correct (same event id twice = one effect). Security-sensitive webhook code: requires human review before merge.

Metadata

Metadata

Assignees

No one assigned

    Labels

    model:opus-4.8Route this task to Claude Opus 4.8ready-for-humanRequires human implementation

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions