
Security News
6 AppSec CTOs Debate Open Source Supply Chain Security at Black Hat
Socket CTO Ahmad Nassri joins AppSec leaders at Black Hat to discuss active malware, package manager risks, and software supply chain defense.
hookwarden
Advanced tools
Webhook security audit CLI — finds signature-verification bugs in JavaScript, TypeScript, Python, PHP, and Go codebases. Local, deterministic, zero-network. 230 rules across 21 providers (Stripe, GitHub, Shopify, Slack, Twilio, Square, Sentry, Zendesk, Do
The only scanner laser-focused on webhook signature verification.
Local. Deterministic. Zero-network. JS/TS + Python + PHP + Go. Five minutes from npx to fix.
npx hookwarden scan ./your-app
No traffic leaves your machine. No telemetry. No SaaS sign-up required.
Every dollar of fraud loss that flows through a webhook starts with a verification bug — and verification bugs hide in plain sight.
A handler that accepts an unsigned payload, compares HMACs with ==, or skips the signature check on a ?test=true path will silently route attacker traffic into your business logic. The bug is one line of code in a 50K-line app, and the code looks plausible — not the shape general-purpose SAST tools are tuned to flag. They were built to catch SQL injection and prototype pollution; webhook verification falls between their default rule packs.
Hookwarden does one thing. It walks your repo, parses every webhook handler across 15 frameworks (Express, Hono, Fastify, Next.js, Flask, FastAPI, Django, Laravel, Symfony, Slim, vanilla-PHP, and Go's net/http, chi, gin, echo), and labels each one verified, not-verified, or manual-review — with the exact file, line, and a fix drawn verbatim from provider documentation. The provider catalog (Stripe, GitHub, Shopify, Slack, Twilio, Square — and growing) encodes signature-format quirks no generic scanner has the surface area to know: Stripe uses HMAC-SHA256 with a 5-minute timestamp tolerance; Slack uses v0:${ts}:${body} not raw-body; Twilio is the SHA1 outlier the rest of the catalog has to accommodate.
The three-state verdict is not a hedge. manual-review is what you get when hookwarden can't prove safety or unsafety from the source alone — a handler inside a middleware chain that the analyzer couldn't fully unroll, for example. It's how the false-positive rate stays honest. A tool that reports every gray area as a bug is not a security tool; it's noise.
npx hookwarden scan . # works everywhere, no install
Reading this on npm? Ignore the auto-generated
npm i hookwardenin the install box at the top of the page — that adds hookwarden as a local project dependency (npm shows it for every package and it can't be changed). hookwarden is a CLI, not a library: run it withnpx hookwarden scan ., or install it globally withnpm i -g hookwarden.
Or install natively via your OS package manager:
| OS | Recommended | Alternates |
|---|---|---|
| Linux | brew install Hookwarden/tap/hookwarden | npm i -g hookwarden · pip install hookwarden · direct binary |
| macOS | brew install Hookwarden/tap/hookwarden | npm i -g hookwarden · npx hookwarden (no install) |
| Windows | scoop bucket add hookwarden https://github.com/Hookwarden/scoop-bucket && scoop install hookwarden | npm i -g hookwarden · pip install hookwarden |
Same
brew installon macOS and Linux. The Linux formula pulls the bun-compiled standalone binary; the macOS formula installs the published npm tarball underlibexecand symlinks thehookwardencommand into yourPATH— same scan engine, same rule pack, same outputs. The macOS path addsnodeas a runtime dep (brew handles transparently); no signed darwin binary ships, so direct-download from a GitHub release will trigger Gatekeeper — install viabrewornpxinstead.Windows users downloading the
.exedirectly from a GitHub release will see a SmartScreen "Windows protected your PC" warning on first launch — hookwarden binaries ship intentionally unsigned. ClickMore info → Run anyway, or use Scoop / WinGet / npm / pip instead — each verifies the artifact by SHA-256 before exec, no SmartScreen friction.
Node 22+ is required for the npm/npx path and for the macOS brew install. The standalone binaries (Linux x64/arm64, Windows x64) bundle the Node runtime.
First use — no install required:
npx hookwarden scan ./your-app
Make CI fail on high+ findings:
npx hookwarden scan ./your-app --fail-on high --format json
# Exit codes: 0 clean · 1 findings at threshold · 2 engine error · 3 config error · 4 parse coverage below floor.
# JSON envelope is byte-stable — diff-safe between PRs.
PR-scoped scan + CI gate (default branch = origin/main):
npx hookwarden scan ./your-app \
--diff-only \
--diff-base origin/main \
--fail-on high
Upload to GitHub Code Scanning (findings show in the Security tab):
npx hookwarden scan ./your-app --format sarif > findings.sarif
gh api -X POST /repos/$REPO/code-scanning/sarifs \
-F sarif=@findings.sarif -F ref=$GITHUB_REF
# SARIF round-trips through GitHub Code Scanning; re-upload deduplicates
# via `partialFingerprints` so the same finding doesn't surface twice.
Non-greenfield adoption — accept existing findings, gate only NEW ones:
# One-time: capture the current state as a baseline (written to .hookwarden.baseline.json).
npx hookwarden scan ./your-app --baseline write
git add .hookwarden.baseline.json && git commit -m "chore: hookwarden baseline"
# All subsequent scans auto-read the baseline; only new findings are reported.
npx hookwarden scan ./your-app --fail-on high
List every detected webhook handler (no rule evaluation):
npx hookwarden inventory ./your-app
# Useful for compliance audits — "what webhook handlers exist in this codebase
# and which provider/framework does each route to?". No rules run; just inventory.
Repo-level config via hookwarden.config.yaml:
# Auto-discovered when placed at the repo root; or pass explicitly:
npx hookwarden scan ./your-app --config ./hookwarden.config.yaml
# Precedence: CLI flag > HOOKWARDEN_<KEY> env var > config file > built-in default.
Phased rollout — gate CI on one provider at a time:
npx hookwarden scan ./your-app --provider stripe --fail-on high
# Only Stripe rules run; GitHub / Shopify / Slack / Twilio / Square findings stay quiet.
# Comma-separated for multiple: --provider stripe,github
# Useful when a security team rolls out coverage stage-by-stage across teams —
# adopters don't get a 100-finding PR on day one.
Monorepo scoping — exclude / include paths from the command line:
# Skip legacy + vendor trees (layered on top of .gitignore + default test exclusions):
npx hookwarden scan ./your-app --exclude 'packages/legacy/**,vendor/**'
# Scope to one workspace only:
npx hookwarden scan ./your-app --include 'packages/api/**'
# Both flags compose — include narrows first, then exclude removes. Gitignore-style globs.
Strict suppressions (compliance teams):
npx hookwarden scan ./your-app --strict-suppressions
# Stale inline `// hookwarden-ignore-next-line` suppressions promote to ERRORS
# instead of warnings. Forces audit hygiene: a fix that removes the bug must also
# remove its suppression — otherwise CI breaks.
Tune parse-coverage floor (for noisy / generated-code repos):
npx hookwarden scan ./your-app --min-parse-coverage 0.85
# Default 0.95. Lower for monorepos with generated TS / JSX edge cases where
# the parser occasionally bails. Below this floor, the scanner exits 4 — by design.
Git history scan — find secrets that were committed then deleted (--history):
npx hookwarden scan --history # last 1000 commits
npx hookwarden scan --history --since v1.0.0 # since a tag/ref
npx hookwarden scan --history --since 2026-01-01
By default scan only inspects the working tree. --history also walks the git
history — including files deleted before HEAD — so a secret that was
force-pushed away is still found (closing the trufflehog/gitleaks parity gap).
Off by default, bounded to the last 1000 commits unless --since narrows it, and
fully open-source — it never needs a token.
Live verification — is the leaked key still active? (--verify-secrets):
# Mint a token from the dashboard (Settings → CLI tokens) on a team workspace:
HOOKWARDEN_TOKEN=hw_… npx hookwarden scan --verify-secrets
--verify-secrets checks whether a leaked API-key-class credential (Stripe
rk_/sk_, GitHub ghs_) is still alive by making a single read-only call to
the secret's own provider, straight from your machine — hookwarden never sees
the secret. A live leak is escalated to critical; a dead (rotated/revoked)
one is downgraded to info. It's paid (team tier), off by default, and
explicit opt-in only.
whsec_, GitHub webhook secret) is always
reported unverified — no provider can confirm a signing secret's liveness.HOOKWARDEN_TOKEN, findings are reported unverified and no
provider call is made.See Install for permanent install via npm, Homebrew, Scoop, or PyPI. Full flag reference: npx hookwarden --help.
hookwarden doesn't just tell you the fix — it applies it. The fix subcommand mechanically rewrites the safety: safe subset of findings across JS/TS, Python, PHP, and Go.
# Dry-run (default) — prints a unified diff, writes nothing
npx hookwarden fix ./your-app
# Apply via atomic-staging path, re-scan touched files, then rename into place
npx hookwarden fix ./your-app --write
Three safety modes:
--mode safe (default) — only applies rules marked safety: safe in the rule pack (10 rules: timing-unsafe comparisons across all 3 langs, raw-body misuse across JS/TS + PHP). Constant-time crypto.timingSafeEqual / hmac.compare_digest / hash_equals replacements; req.body → req.rawBody; $_POST / Input::all → file_get_contents("php://input").--mode all — applies safe plus unsafe rules. Refuses in CI/non-TTY unless --accept-unsafe is also passed.--mode manual-only-explain — emits the per-finding fix prose for rules where mechanical rewrite isn't safe (missing-signature-verification, wrong-hmac-algorithm, etc. — 188 of 230 rules ship as manual-only in v0.7.2; 42 ship safety: safe with mechanical codegen).Safety contract:
.hookwarden-fix-staging/<run-id>/ first; the original files are renamed into place only after a re-scan confirms zero new findings. On any failure, the staging dir persists for inspection.--only <rule-id> recipe so you can apply them one at a time.--format json emits a machine-readable diff against https://hookwarden.dev/schemas/fix-output.v1.json for IDE-plugin and CI tooling.# Get the JSON diff for tooling
npx hookwarden fix ./your-app --format json
Use hookwarden scan for the read path, hookwarden fix for the write path — never scan --fix (rejected at parse time per the dry-run-as-default safety boundary).
Output below is captured verbatim from hookwarden v0.7.2 — each line is what
you'll see in your terminal, not a stylised mockup.
Clean scan — exits 0:
$ hookwarden scan ./your-app
No findings.
────────────
Found 0 critical · 0 high · 0 medium · 0 low · 0 info · 0 manual-review — 0 webhook handlers across 0 files
Scanned in 0.0 s · 1 / 1 candidates parsed (100.0% coverage) · engine v0.7.2 · rules v0.7.2
Scan with the canonical Express middleware-ordering bug — exits 1:
$ hookwarden scan ./your-app
× critical server.js:10:1 stripe/express-middleware-ordering not-verified
Express webhook handler for Stripe has `express.json()` (or `body-parser.json()`) registered before the
webhook route. JSON middleware consumes the request body; by the time the Stripe handler runs, the raw bytes
used for HMAC are gone and `stripe.webhooks.constructEvent` cannot reproduce the signature.
fix › register `express.json()` AFTER the webhook route, OR mount `express.raw({ type: 'application/json' })`
only on the webhook path.
docs › https://stripe.com/docs/webhooks/signatures
refs › https://expressjs.com/en/guide/using-middleware.html
https://www.svix.com/blog/common-failure-modes-for-webhook-signatures/
× critical server.js:10:1 stripe/missing-signature-verification not-verified
Stripe webhook handler does not appear to verify the signature header before processing the event. Neither
`stripe.webhooks.constructEvent` (Node SDK) nor `stripe.Webhook.construct_event` (Python SDK) is reachable
from this handler within 3 hops, and no manual HMAC path was detected either. Stripe docs: "You should
always verify events by checking the signature".
fix › pass the raw request body, the `Stripe-Signature` header, and your `STRIPE_WEBHOOK_SECRET` to
`stripe.webhooks.constructEvent` (Node) or `stripe.Webhook.construct_event` (Python) at the very top
of the handler.
docs › https://stripe.com/docs/webhooks
refs › https://www.svix.com/blog/common-failure-modes-for-webhook-signatures/
https://hookdeck.com/webhooks/guides/webhook-security-vulnerabilities-guide
× critical server.js:10:1 stripe/raw-body-misuse not-verified
Stripe webhook handler reads the signature header (or calls the SDK verify) but does not appear to receive
the request body as raw bytes. Stripe's HMAC is computed over the raw payload — once `express.json()` (or
any other JSON middleware) parses the body, the bytes used for the HMAC differ from what was sent and
verification fails on every webhook.
docs › https://stripe.com/docs/webhooks/signatures
refs › https://www.svix.com/blog/common-failure-modes-for-webhook-signatures/
https://docs.stripe.com/webhooks#verify-events
────────────
Found 3 critical · 0 high · 0 medium · 0 low · 0 info · 0 manual-review — 1 webhook handler across 1 file
Scanned in 0.0 s · 1 / 1 candidates parsed (100.0% coverage) · engine v0.7.2 · rules v0.7.2
Notice: one Express middleware bug produces three findings — middleware-ordering, missing-signature-verification, and raw-body-misuse — because that single mistake violates three distinct invariants. Fixing one (re-ordering the middleware) clears all three at once. The rule-pack isn't double-counting; it's giving you three lenses on the same root cause so any one of them can be the entry point in code review.
PHP scan (v0.4 third-language support) — strcmp() instead of hash_equals():
$ hookwarden scan ./your-php-app
× critical index.php:10:9 stripe/timing-unsafe-comparison not-verified
Stripe webhook handler computes an HMAC manually but does not call `crypto.timingSafeEqual` (Node) or
`hmac.compare_digest` (Python) for signature comparison. Plain `==` / `===` against an HMAC buffer leaks
timing information and is exploitable on a fast network.
fix › replace `expected === provided` with `crypto.timingSafeEqual(Buffer.from(expected),
Buffer.from(provided))` in Node, or `hmac.compare_digest(expected, provided)` in Python.
docs › https://stripe.com/docs/webhooks/signatures
refs › https://codahale.com/a-lesson-in-timing-attacks/
https://cwe.mitre.org/data/definitions/208.html
────────────
Found 1 critical · 0 high · 0 medium · 0 low · 0 info · 0 manual-review — 1 webhook handler across 1 file
Scanned in 0.0 s · 1 / 1 candidates parsed (100.0% coverage) · engine v0.7.2 · rules v0.7.2
PHP's strcmp() (and === / ==) are not constant-time; the equivalent safe call is hash_equals($expected, $sig). The fix prose currently quotes the Node/Python equivalents — PHP-specific copy lands in a follow-up.
Output legend:
| Glyph | Severity | SARIF level | Exit-code contribution |
|---|---|---|---|
× | critical | error | counted toward --fail-on threshold |
× | high | error | counted toward --fail-on threshold |
! | medium | warning | counted toward --fail-on threshold |
· | low | note | counted toward --fail-on threshold |
· | info | note | informational (e.g. library-verified) — does not fail CI |
The state column (right of the rule id) is the architectural three-state verdict: not-verified, verified, or manual-review. The footer surfaces total counts plus parse-coverage so you can spot a --min-parse-coverage regression at a glance.
JSON envelope shape:
{
"schema_version": "1.0",
"engine": { "version": "0.4.0", "commit_sha": null },
"rule_pack": { "version": "0.4.0", "content_hash": "51c219..." },
"scan": {
"counts": {
"active": { "critical": 1, "high": 1, "medium": 0, "low": 0, "info": 0 },
"suppressed": { "critical": 0, "high": 0, "medium": 0, "low": 0, "info": 0 }
},
"findings": [
{
"rule_id": "stripe/express-middleware-ordering",
"severity": "critical",
"state": "not-verified",
"provider": "stripe",
"file_path": "server.js",
"location": { "line": 10, "col": 1 },
"finding_id": "stripe/express-middleware-ordering@d603a04...",
"primary_location_line_hash": "d603a04...",
"message": "Express webhook handler for Stripe has...",
"redacted_snippet": "app.use(express.json())\napp.post('/webhook', ...",
"suppressed": null
}
],
"scanned_at": "2026-05-05T18:31:33.653Z",
"parsed_files_count": 1,
"parse_candidates_count": 1
},
"suppressions": { "applied": [], "stale": [] }
}
Sorted keys, schema-versioned, byte-stable across runs (modulo scanned_at). SARIF output round-trips through GitHub Code Scanning and deduplicates via partialFingerprints on re-upload.
4 languages, 15 frameworks, 1 codebase walker. PHP, Python, and Go use tree-sitter; JS/TS use Babel. Single-file vanilla-PHP and net/http Go handlers are detected heuristically; everything else routes through framework-specific adapters.
| Language | Frameworks | Parser |
|---|---|---|
| JavaScript / TypeScript | Express · Hono · Fastify · Next.js | @babel/parser |
| Python | Flask · FastAPI · Django | tree-sitter-python (WASM) |
| PHP (v0.4) | Laravel · Symfony · Slim · vanilla-PHP single-file | tree-sitter-php (WASM) |
| Go (v0.9) | net/http · chi · gin · echo | tree-sitter-go (WASM) |
PHP 8.0+ syntax floor. Python 3.10+ recommended. TypeScript: strict + non-strict both supported.
230 rules across 21 providers, each applicable across the relevant subset of the 11 frameworks above. Every rule carries fix guidance quoted verbatim from the provider's canonical security documentation.
| Provider | Rules | Detection types | Custom predicate |
|---|---|---|---|
| Stripe | 9 | missing-sig-verif, timing-unsafe, raw-body, missing-timestamp, wrong-hmac, unreachable-verif, hardcoded-secret (whsec_), library-verified | — |
| GitHub | 9 | missing-sig-verif, timing-unsafe, raw-body, missing-timestamp, wrong-hmac, unreachable-verif, hardcoded-secret (ghs_, github_pat_), library-verified | — |
| Shopify | 7 | missing-sig-verif, timing-unsafe, raw-body, missing-timestamp (info), wrong-hmac, unreachable-verif, library-verified | — |
| Slack | 7 | missing-sig-verif, timing-unsafe, raw-body, missing-timestamp (high), wrong-hmac, unreachable-verif, library-verified | Parameterized timestamp_dot_body recipe |
| Twilio | 7 | missing-sig-verif, timing-unsafe, raw-body, missing-timestamp (info), wrong-hmac, unreachable-verif, library-verified | predicates/custom/twilio-signing.ts — URL+sorted-params canonical-string + HMAC-SHA1 |
| Square | 6 | missing-sig-verif, timing-unsafe, raw-body, wrong-hmac, unreachable-verif, library-verified | Parameterized custom_field_tuple recipe |
Full per-rule applicability matrix: docs/rule-coverage.md.
# .github/workflows/hookwarden.yml
name: hookwarden
on: [pull_request, push]
permissions:
contents: read
pull-requests: write
security-events: write
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- uses: Hookwarden/hookwarden-action@v1
with:
fail-on: high
Uploads SARIF to Code Scanning automatically. Findings appear as PR annotations.
- uses: actions/setup-node@v4
with: { node-version: '22' }
- run: npx hookwarden scan . --format sarif > hookwarden.sarif
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: hookwarden.sarif
SARIF severity mapping: critical/high → error · medium → warning · low/info → note.
@hookwarden/mcp is a Model Context Protocol server that gives AI coding agents the scan_handler tool. Paste in any webhook handler and get back the same 3-state verdict (verified / not-verified / manual-review) the CLI emits — fully local, deterministic, no traffic leaves the agent's machine.
# Auto-detects Claude Code / Claude Desktop / Cursor / Continue and writes the MCP config:
npx @hookwarden/mcp init
After init, restart your agent and ask it to scan a handler — e.g. "scan this Stripe webhook for signature-verification bugs". The rule pack is bundled inline, content-hashed, and version-pinned to the engine, so scan_handler returns the exact same verdict as hookwarden scan and fails loudly on drift. See the @hookwarden/mcp README for the full tool reference.
hookwarden is a pnpm monorepo with three load-bearing packages and a strict dependency boundary enforced in CI.
Your repo source
│
▼
Walker (file allowlist + test-path filter)
│
▼
@hookwarden/engine ◀── @hookwarden/rules
(pure-functional, (YAML rule packs +
no I/O) parameterized predicates)
│
▼
Three-state verdict
├─ reachable + safe → verified
├─ reachable + unsafe → not-verified
└─ unprovable → manual-review
│
▼
CLI renderer → text · JSON · SARIF 2.1.0
The verdict-state machine is the architectural contract — every finding lives in exactly one of these states, and the false-positive rate stays honest by routing analysis-defeated cases to manual-review rather than guessing:
[discovered]
│ (handler reached by walker)
▼
[reachability check] ──── signature-verify reachable ≤ 3 hops ──→ verified (exit 0)
│
├─ no verify call reachable ──→ not-verified (exit 1)
│
└─ analyzer defeated (dynamic dispatch, middleware unroll,
parse error) ──→ manual-review (exit 0, non-blocking)
| Package | Purpose | License |
|---|---|---|
@hookwarden/engine | Webhook handler discovery, reachability analysis, evidence collection. Pure-functional, browser-safe — no I/O, no filesystem, no network. | Apache 2.0 |
@hookwarden/rules | Provider catalog, YAML rule packs, parameterized predicate factories. | Apache 2.0 |
hookwarden | CLI binary. Reads config, drives the engine, renders text/JSON/SARIF. | Apache 2.0 |
The engine's I/O boundary is the architectural load-bearing constraint. The same engine runs in the CLI, in CI, and — eventually — in a browser playground without modification. dependency-cruiser enforces the boundary in every PR.
Hookwarden is specialized on purpose. Webhook signature verification is the only thing it does, and that's why it does it better than tools whose surface area covers everything. The general-purpose scanners below are excellent at what they do — they're just not in this fight.
| Tool | What it does well | Webhook verification coverage |
|---|---|---|
| semgrep | General-purpose SAST; flexible rule authoring | Low signal — generic pattern matching misses body-parsing ordering, timing-safe comparison paths, and SDK-specific verification flows |
| snyk Code | Broad vulnerability detection in paid SaaS | No webhook-specific rules; doesn't model HMAC reachability |
| GitGuardian | Secret leak detection in git history and CI | Finds hardcoded secrets; does not audit whether verification logic is correct |
| TruffleHog | Secret scanning across sources | Same as GitGuardian — leak focus, not logic focus |
| Datadog Static Analysis | Broad SAST; good AWS/cloud signal | No webhook verification specialization; generic SAST rules produce low-signal findings for this class of bug |
| hookwarden | Webhook verification logic only | 230 rules (100% cited), 21 providers, three-state verdicts, <5% FP on a 200-repo corpus |
If you're already running semgrep or snyk: hookwarden is additive, not a replacement. It finds the class of bug those tools were not built to find.
Three suppression mechanisms, in order of preference:
Inline — best for one-off cases; the comment is grep-able evidence in code review:
// hookwarden-disable-next-line stripe/missing-signature-verification
app.post('/webhook', rawBodyHandler);
.hookwardenignore — gitignore syntax; best for path-scoped suppression:
__tests__/
fixtures/**/*.spec.ts
mocks/
Baseline — best for adopting on a non-greenfield codebase without failing CI on day one:
# Capture current state
hookwarden scan . --baseline write
# Subsequent runs suppress baselined findings; new findings still fail
hookwarden scan .
--format json reports each finding's suppression source (inline / ignorefile / baseline) so suppressions are auditable.
| Code | Meaning |
|---|---|
0 | Clean — no findings at or above the configured --fail-on threshold |
1 | Findings at or above threshold |
2 | Engine error (parser crash, unreadable input) |
3 | Config error (malformed hookwarden.config.yaml) |
4 | Parse coverage below parse_coverage_min |
Precedence: 3 > 2 > 4 > 1 > 0. The highest applicable code wins; use this for branching logic in CI pipelines.
Drop a hookwarden.config.yaml at your project root (or any ancestor directory):
schema_version: '1.0'
fail_on: high # critical | high | medium | low | info
parse_coverage_min: 0.9 # fail if < 90% of candidates parsed
baseline:
enabled: true
path: .hookwarden.baseline.json
Precedence: CLI flag > hookwarden.config.yaml > built-in defaults.
Inventory mode (lists detected handlers without running rules):
hookwarden inventory ./your-app
| hookwarden severity | SARIF level | GitHub Code Scanning |
|---|---|---|
critical | error | Blocks PR merge (if branch protection configured) |
high | error | Blocks PR merge |
medium | warning | Visible annotation, non-blocking |
low | note | Visible annotation |
info | note | Visible annotation |
Re-uploading the same scan deduplicates via SARIF partialFingerprints. Full mapping table: packages/cli/docs/sarif-severity-mapping.md.
Recently shipped (v0.3)
pre-commit hook · Homebrew tap · Scoop/WinGet manifests · standalone binaries (Linux x64/arm64, Windows x64). macOS users install via npx hookwarden or npm i -g hookwarden.
Recently shipped (v0.4) — PHP language support + --provider filter. Laravel, Symfony, Slim, and vanilla-PHP single-file handlers via tree-sitter-php WASM. PHP variants of every applicable rule across the six v1 providers (hash_equals as the safe-compare predicate, php://input / ->getContent() / $_POST as recognised raw-body shapes, \Stripe\Webhook::constructEvent and equivalent FQNs in the SDK-reach catalog). New --provider stripe,github flag for phased rollout — security teams adopt one provider at a time without flooding PRs.
v0.4.1 — monorepo scoping. --exclude GLOB / --include GLOB CLI flags so monorepos can skip apps/legacy/**, vendor trees, etc. from the command line (today only the YAML config supports this).
v0.4.2 — hookwarden explain <rule-id>. Terminal-side rule documentation: rationale, catalog entry, positive/negative fixture excerpts. Lowers the support burden on the rule-pack maintainer; deepens developer trust without leaving the terminal.
✅ v0.5 — hookwarden fix auto-remediation. The mechanical fixes (=== → crypto.timingSafeEqual, == → hash_equals, etc.) applied with a real AST-rewrite engine. Safe / unsafe / manual-only classification per rule. Three-state verdicts already classified which fixes are safe to auto-apply — hookwarden fix makes that machine-actionable. See Auto-fix above.
✅ v0.6 — Rule-pack expansion. 15 new provider rule packs (Zendesk, DocuSign, PagerDuty, Bitbucket, Notion, Calendly, Zoom, …) plus the Standard Webhooks spec sweep covering Clerk, Resend, Mux, Lob. CVE-2026-41432 Stripe empty-secret detector landed here.
✅ v0.7 — Rule Depth. 5 new rule classes (VAS / BYP / ERS / LEAK / RPL families) across all 21 providers; rule pack grew 142 → 230 YAML rules.
✅ v0.7.1 — Rule-pack polish. References backfilled on 142 grandfathered rules → 230 cited (CWE / RFC / Svix / Stripe spec). Test-path severity overrides on 219 rules — fixtures in **/{test,tests,__tests__,spec,specs}/** and *.{test,spec}.* no longer false-fire as critical.
✅ v0.7.2 — References visible in output. Text renderer gains a refs › block (one line per citation, hyperlinked when URLs); JSON envelope gains scan.findings[].references: string[]. v0.7.1 had cited every rule but the renderer never read the field — citations were live in the YAML but invisible to scan consumers. v0.7.2 closes that loop.
✅ v0.8 — @hookwarden/mcp developer preview. Model Context Protocol server exposing scan_handler to Claude Code, Cursor, Continue, and the Anthropic Agent SDK. Paste any webhook handler into your AI coding agent → 3-state verdict back, fully local. → @hookwarden/mcp on npm.
Rule-pack PRs are the highest-value contribution. Adding a new provider is a catalog edit plus N rule YAMLs — the factory architecture means most providers ship without any new TypeScript. See the existing 21 providers in packages/rules/rules/ as worked examples.
Bug reports and feature requests: open an issue.
Local development:
pnpm install
pnpm -r build
pnpm -r test
More: hookwarden.dev · rule coverage matrix · GitHub Action docs.
Apache 2.0 — see LICENSE. The CLI, engine, and rule packs in this repo are open source and will remain so. A separate closed-source SaaS tier handles continuous monitoring, secret leak scanning, automated rotation, and SOC 2 evidence export — hookwarden.dev.
Brand assets live at assets/brand/.
FAQs
Webhook security audit CLI — finds signature-verification bugs in JavaScript, TypeScript, Python, PHP, and Go codebases. Local, deterministic, zero-network. 230 rules across 21 providers (Stripe, GitHub, Shopify, Slack, Twilio, Square, Sentry, Zendesk, Do
The npm package hookwarden receives a total of 18 weekly downloads. As such, hookwarden popularity was classified as not popular.
We found that hookwarden demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
Socket CTO Ahmad Nassri joins AppSec leaders at Black Hat to discuss active malware, package manager risks, and software supply chain defense.

Research
/Security News
Thirteen malicious Packagist themes expose visitors on unpatched iPhones to a WebKit-to-kernel exploit chain that steals device data and wallet seeds.

Research
/Security News
Ten malicious OpenAPI React Query Codegen versions were published to npm in the Mini Shai-Hulud attack, all with valid provenance.