Static scanner · webhook signature verification

The code-level border
for webhooks.

The scanner laser-focused on webhook signature verification. Point it at your repo and it finds every handler a forged payload could slip past — JS, TS, Python, PHP, and Go. Local, deterministic, zero-network.

No account needed for the CLI — npx hookwarden scan runs fully offline. The hosted dashboard is launching soon.

app — hookwarden scan
➜~hookwarden scan ./your-app
✗ not-verifiedcritical
server.js:10:1stripe/express-middleware-ordering
express.json() runs before the route — the raw bytes are gone before the HMAC is computed.
fix › mount express.raw({ type: 'application/json' }) on the webhook path.
Found 1 critical · 0 high · 0 manual-review — 1 handler, 1 file
// Coverage today

24 providers · 30 rule classes · 15 frameworks · 5 languages · 5 install surfaces — one CLI that runs anywhere.

Stripe Stripe GitHub GitHub Shopify Shopify Slack Slack Twilio Twilio Square Square Sentry Sentry Zendesk Zendesk Notion Notion Auth0 Auth0 HubSpot HubSpot Intercom Intercom Linear Linear Calendly Calendly Zoom Zoom Bitbucket Bitbucket PagerDuty PagerDuty MailChimp Mailchimp n8n n8n DocuSign Postmark Clerk Clerk Resend Resend Discord Discord + Standard Webhooks Stripe Stripe GitHub GitHub Shopify Shopify Slack Slack Twilio Twilio Square Square Sentry Sentry Zendesk Zendesk Notion Notion Auth0 Auth0 HubSpot HubSpot Intercom Intercom Linear Linear Calendly Calendly Zoom Zoom Bitbucket Bitbucket PagerDuty PagerDuty MailChimp Mailchimp n8n n8n DocuSign Postmark Clerk Clerk Resend Resend Discord Discord + Standard Webhooks
Express Express Fastify Fastify Next.js Next.js Hono Hono Django Django Flask Flask FastAPI FastAPI Laravel Laravel Symfony Symfony net/http Gin Gin Echo Chi Express Express Fastify Fastify Next.js Next.js Hono Hono Django Django Flask Flask FastAPI FastAPI Laravel Laravel Symfony Symfony net/http Gin Gin Echo Chi
TypeScript TypeScript JavaScript JavaScript Python Python PHP PHP Go Go Node.js Node.js npm npm Homebrew Homebrew Docker Docker GitHub Actions GitHub Actions TypeScript TypeScript JavaScript JavaScript Python Python PHP PHP Go Go Node.js Node.js npm npm Homebrew Homebrew Docker Docker GitHub Actions GitHub Actions
The verdict model

Three verdicts. No guessing.

hookwarden labels every webhook handler in your codebase from the source alone, reporting only what it can prove. The third state — manual-review — is how it avoids guessing: anything it can't prove goes to a person instead of failing your build.

not-verified reachable + unsafe

A provable bug

The signature is never checked, compared with == instead of a timing-safe equal, or computed over an already-parsed body. Fix before you ship — and hookwarden fix can rewrite most of them for you.

// hookwarden scan
× critical  stripe/missing-signature-verificationnot-verified
× critical  stripe/raw-body-misusenot-verified
verified

Proven safe

Reachable and correct — a valid signature check sits on the handler's path. Nothing to fix.

manual-review

Honest about the gray area

Can't be proven safe or unsafe from the source alone — like a handler inside a middleware chain the analyzer couldn't fully unroll. Flagged for a human, never guessed.

Open source · Apache 2.0

Find it, then fix it.

scan finds every handler a forged payload could slip past; fix rewrites the unsafe ones with each provider's correct, timing-safe verification. Dry-run by default — --write applies.

app — hookwarden fix
➜appnpx hookwarden fix --write
✗ not-verifiedgithub.ts:42 · github/timing-unsafe-comparison
- if (sig === expected) return handle(req);
+ if (crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)))
+ return handle(req);
✓ 1 fix applied · safe rewrites only · re-run scan to confirm
Demo Live engine simulation

See the engine catch a bug. In two seconds.

Watch the AST passes run against a known-broken Stripe handler — same rule pack, same 3-state verdict the CLI ships. Toggle to the correct version to see a verified short-circuit.

demo · hookwarden engine
paste.ts TypeScript
app.post('/webhooks/stripe', async (req, res) => {
  const sig = req.headers['stripe-signature'];
  const event = JSON.parse(req.body);

  if (event.type === 'checkout.session.completed') {
    await fulfillOrder(event.data.object);
  }

  res.json({ received: true });
});
verdict stripe · express
NOT VERIFIED stripe/missing-signature-verification handler · line 1

The handler captures the stripe-signature header but never verifies it. No stripe.webhooks.constructEvent call is reachable within 4 hops and no manual HMAC path was detected — a forged payload would reach fulfillOrder.

Suggested Fix
- const event = JSON.parse(req.body);
+ const event = stripe.webhooks.constructEvent(req.body, sig, process.env.STRIPE_WEBHOOK_SECRET);
+ // also: mount express.raw({ type: 'application/json' }) on this route

See the Correct (Stripe) tab for a full working handler.

v0.11.0 · 24 Providers · Ready

Now run it on your whole codebase.

$ npx hookwarden scan

The CLI is free and local. The platform takes the same finding the rest of the way — watched in production, rotated automatically, handed to your auditor. No re-keying, no re-explaining.

SCANlocal · free
$ hookwarden scan .
× critical github.ts:42
stripe/timing-unsafe
not-verified
MONITORcontinuous
rot_8fa2 · stripe
● dual-secret window open
old + new both valid
✓ live delivery verified
PROVEaudit-ready
seq 1045 rotation.signed
hash e1d7fa · prev 9b0247
✓ KMS-signed chain
tamper-evident
The platform

Everything after the first fix.

Automated rotation

A dual-secret window on every rotation — verified against a live delivery before the old secret is retired. Delivery never drops.

rotation rot_8fa2 · stripe
✓ new secret issuedwhsec_••••a91c
● dual-secret window openold + new valid
✓ live delivery verifiedevt_1f4c · 200
→ old secret retired

Rotation runbooks

Providers without a rotation API get a guided, gated runbook — every step signed.

runbook · 5 gated steps
✓ issue new secret in dashboard
● verify a live deliverystep 3 / 5

Tamper-evident audit log

Append-only and hash-chained — every row links to the one before it.

seq 1043 rotation.verified a3f9c1
seq 1044 secret.retired ⛓ a3f9c1
seq 1045 evidence.export ⛓ 9b0247

Continuous monitoring

Every inbound delivery scored in real time — signature, timestamp, and sequence integrity — across all your providers.

monitor · prod · last 24h
✓ 14,902 deliveries verifiedstripe · github · slack
! 2 sequence gaps flaggedseq_1043 → 1045
× 0 signature failuresall clear
New · EU AI Act Annex III

Hand your auditor a pack they can verify themselves.

The same tamper-evident chain — now tagged with the EU AI Act Annex III high-risk classification, alongside your SOC 2 and ISO 27001 mappings. Real, KMS-signed, offline-verifiable.

eu_ai_act_annex_iii high_risk_ai_system
⛓ seq 3 · auditor_token_usedhash 9c2f… · prev a17b…
⛓ seq 2 · rotation_finalizedhash a17b… · prev ∅
✓ offline-verifiable · no AWS call
// Pricing

Free where it audits. Paid where it monitors.

The CLI catches every webhook bug today, locally, for free. The SaaS keeps watch tomorrow.

Free CLI

tier:free-cli
$0 forever · Apache 2.0

Find every webhook bug in your codebase. Today.

Languages
JS · TS · Python · PHP · Go
Frameworks
15
Providers
24
Output
JSON · SARIF · terminal
License
Apache 2.0
Install the CLI

Solo

tier:solo
$99 per month

Continuous monitoring for one small team.

Repos
Up to 3
Developers
Up to 5
Monitoring
Continuous
Leak scan
CloudWatch upload
Alerts
Slack + email
Retention
13 months
Coming soon

Hosted tier launching soon

Enterprise

tier:enterprise
Custom annual contract

Monitoring + rotation orchestration + custom rules + on-prem.

Everything in
Team
SLA
99.95% · 4h response
Custom rules
Per-vendor
Deployment
On-prem optional
Support
Dedicated
SSO
SAML · SCIM
Get a quote

All paid tiers · 30-day free trial · annual prepay ~15% off · EU-hosted (Frankfurt) · cancel anytime

Find every webhook bug in your codebase.
In five minutes.

Start with the open-source CLI. Upgrade when compliance comes knocking.

Start Building Free $ npx hookwarden scan