Hookwarden/hookwarden

GitHub: Hookwarden/hookwarden

专注于 webhook 签名验证逻辑的静态分析工具,可检测未验证、验证错误或需人工审查的处理程序。

Stars: 0 | Forks: 0

hookwarden

唯一专注于 webhook 签名验证的扫描工具。
本地化。确定性。无网络。JS/TS + Python。从 `npx` 到修复只需五分钟。

npm version npm downloads License: Apache 2.0 Node 22+ CI GitHub stars TypeScript strict


``` npx hookwarden scan ./your-app ``` 没有任何流量离开您的机器。没有遥测数据。不需要注册 SaaS。 ## 目录 - [为什么选择 Hookwarden](#why) - [快速开始](#quickstart) - [实际输出](#real-output) - [提供商覆盖](#provider-coverage) - [CI 集成](#ci-integration) - [架构](#architecture) - [与其他工具对比](#vs-other-tools) - [高级用法](#advanced-usage) - [路线图](#roadmap) - [贡献指南](#contributing) - [许可证](#license) ## 为什么选择 Hookwarden **Snyk 和 Semgrep 能找到所有问题。Hookwarden 找到的是让攻击者重放您的 Stripe webhook 的那一个漏洞。** 大多数 webhook 漏洞不在于交付,而在于验证。接受未签名 payload 的处理程序、使用 `==` 比较 HMAC、或在 `?test=true` 路径上跳过签名检查的处理程序,会将攻击者流量静默路由到您的业务逻辑中。攻击面是真实存在的,漏洞很常见,而通用 SAST 工具并不是为这个问题设计的——它们会将高信号的 webhook 漏洞埋没在数百个中等优先级的通用问题中。 Hookwarden 只做一件事。它遍历您的代码库,解析 Express、Hono、Fastify、Next.js、Flask、FastAPI 和 Django 中的每个 webhook 处理程序,并将每个处理程序标记为 **已验证**、**未验证** 或 **需手动审查**——并提供确切的文件、行号以及直接从提供商文档中摘录的修复方法。这种专注就是关键所在。(Stripe、GitHub、Shopify、Slack、Twilio、Square——以及更多)提供商目录编码了任何通用扫描器都无法全面了解的签名格式细节:Stripe 使用 HMAC-SHA256 并有 5 分钟时间戳容差,Slack 使用 `v0:${ts}:${body}` 而非原始 body,Twilio 是 SHA1 异类,其他提供商都需要适应它。 **三态判定不是骑墙做法。** 当 hookwarden 无法仅从源代码证明安全或不安全时,就会得到 `manual-review`——例如,分析器无法完全展开的中间件链中的处理程序。这就是保持误报率真实的方式。将每个灰色地带都报告为漏洞的工具不是安全工具,而是噪音。 ## 🚀 快速开始 ``` # First use — no install required npx hookwarden scan ./your-app # JSON envelope for CI pipelines npx hookwarden scan ./your-app --format json # SARIF for GitHub Code Scanning npx hookwarden scan ./your-app --format sarif > findings.sarif # Scope to files changed in a PR npx hookwarden scan ./your-app --diff-only --diff-base origin/main # Snapshot pre-existing findings as a baseline (non-greenfield adoption) npx hookwarden scan ./your-app --baseline write ``` 或永久安装: ``` npm install -g hookwarden # global npm install --save-dev hookwarden # dev dependency (CI-pinnable) ``` ## 实际输出 **清洁扫描 — 退出码 0:** ``` hookwarden scan ./your-app ✓ Scanned 24 candidates · 24 parsed (100.0% coverage) · 0 findings ``` **发现漏洞的扫描 — 退出码 1:** ``` hookwarden scan ./your-app CRITICAL ──────── server.js:10:1 stripe/express-middleware-ordering [not-verified] Express webhook handler for Stripe has `express.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. Fix: register `express.json()` AFTER the webhook route, OR mount `express.raw({ type: 'application/json' })` only on the webhook path. ↳ https://stripe.com/docs/webhooks/signatures HIGH ──── handlers/slack.ts:34:1 slack/missing-timestamp-validation [not-verified] Slack handler does not enforce the 5-minute replay window. The `X-Slack-Request-Timestamp` header is present but not compared against the current time before signature verification proceeds. Fix: reject requests where abs(Date.now()/1000 - ts) > 300 before computing the `v0:${ts}:${body}` signing string. ↳ https://api.slack.com/authentication/verifying-requests-from-slack ──────────── Found 1 critical · 1 high · 0 medium · 0 low · 0 info · 0 manual-review Scanned in 0.3 s · 24 / 24 candidates parsed (100.0% coverage) ``` **JSON 包格式:** ``` { "schema_version": "1.0", "engine": { "version": "0.2.0", "commit_sha": null }, "rule_pack": { "version": "0.2.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": [] } } ``` 排序的键、带版本号的 schema、跨运行字节稳定(`scanned_at` 除外)。SARIF 输出通过 GitHub Code Scanning 往返,并在重新上传时通过 `partialFingerprints` 去重。 ## 🔐 提供商覆盖 v0.2 版本包含 6 个提供商共 45 条规则。每条规则都包含直接从提供商官方安全文档中摘录的修复指导。 | 提供商 | 规则数 | 检测类型 | 自定义谓词 | |---|---|---|---| | [**Stripe**](./packages/rules/rules/stripe) | 9 | 缺失签名验证、时序不安全、原始 body、缺失时间戳、错误 HMAC、不可达验证、硬编码密钥 (`whsec_`)、库已验证 | — | | [**GitHub**](./packages/rules/rules/github) | 9 | 缺失签名验证、时序不安全、原始 body、缺失时间戳、错误 HMAC、不可达验证、硬编码密钥 (`ghs_`、`github_pat_`)、库已验证 | — | | [**Shopify**](./packages/rules/rules/shopify) | 7 | 缺失签名验证、时序不安全、原始 body、缺失时间戳(信息级)、错误 HMAC、不可达验证、库已验证 | — | | [**Slack**](./packages/rules/rules/slack) | 7 | 缺失签名验证、时序不安全、原始 body、缺失时间戳(高级)、错误 HMAC、不可达验证、库已验证 | 参数化 `timestamp_dot_body` 规则 | | [**Twilio**](./packages/rules/rules/twilio) | 7 | 缺失签名验证、时序不安全、原始 body、缺失时间戳(信息级)、错误 HMAC、不可达验证、库已验证 | `predicates/custom/twilio-signing.ts` — URL+排序参数规范字符串 + HMAC-SHA1 | | [**Square**](./packages/rules/rules/square) | 6 | 缺失签名验证、时序不安全、原始 body、错误 HMAC、不可达验证、库已验证 | 参数化 `custom_field_tuple` 规则 | 完整的每规则适用性矩阵:[`docs/rule-coverage.md`]( hookwarden.sarif - uses: github/codeql-action/upload-sarif@v3 with: sarif_file: hookwarden.sarif ``` SARIF 严重性映射:`critical`/`high` → `error` · `medium` → `warning` · `low`/`info` → `note`。 ## 架构 hookwarden 是一个 pnpm monorepo,包含三个核心包,并在 CI 中强制执行严格的依赖边界。

hookwarden architecture: source AST and YAML rules feed the pure engine, which feeds the CLI; CLI emits text, JSON, and SARIF.

| 包 | 用途 | 许可证 | |---|---|---| | [`@hookwarden/engine`]( 抑制——内联、.hookwardenignore 和基线 三种机制,按优先级排序: **内联**——最适合一次性情况;注释是可 grep 的代码审查证据: ``` // hookwarden-disable-next-line stripe/missing-signature-verification app.post('/webhook', rawBodyHandler); ``` **`.hookwardenignore`**——gitignore 语法;最适合路径级别的抑制: ``` __tests__/ fixtures/**/*.spec.ts mocks/ ``` **基线**——最适合在非全新代码库上采用,而不会在第一天就让 CI 失败: ``` # Capture current state hookwarden scan . --baseline write # Subsequent runs suppress baselined findings; new findings still fail hookwarden scan . ``` `--format json` 报告每个发现的抑制来源(`inline` / `ignorefile` / `baseline`),以便审计抑制情况。
退出码矩阵 | 码 | 含义 | |------|---------| | `0` | 清洁——在配置的 `--fail-on` 阈值以上没有发现 | | `1` | 在阈值以上有发现 | | `2` | 引擎错误(解析器崩溃、输入不可读) | | `3` | 配置错误(格式错误的 `hookwarden.config.yaml`) | | `4` | 解析覆盖率低于 `parse_coverage_min` | 优先级:`3 > 2 > 4 > 1 > 0`。最高适用码获胜;在 CI 管道中使用此逻辑进行分支判断。
配置 schema 在项目根目录(或任何祖先目录)中放置 `hookwarden.config.yaml`: ``` 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 ``` 优先级:CLI 标志 > `hookwarden.config.yaml` > 内置默认值。 清单模式(列出检测到的处理程序而不运行规则): ``` hookwarden inventory ./your-app ```
SARIF 严重性映射 | hookwarden 严重性 | SARIF `level` | GitHub Code Scanning | |---|---|---| | `critical` | `error` | 阻止 PR 合并(如果配置了分支保护) | | `high` | `error` | 阻止 PR 合并 | | `medium` | `warning` | 可见注释,非阻塞 | | `low` | `note` | 可见注释 | | `info` | `note` | 可见注释 | 重新上传相同扫描会通过 SARIF `partialFingerprints` 去重。完整映射表:[`packages/cli/docs/sarif-severity-mapping.md`]( ## 路线图 **v0.3 — 分发。** pre-commit hook、Homebrew tap、Scoop/WinGet 清单、独立二进制程序(macOS arm64/x64、Linux x64/arm64、Windows x64)。 **v0.4 — 更多提供商。** Adyen、Zendesk、Mailgun、SendGrid——每个在发布前都会针对 200 个开源仓库回归语料库进行测试,并公布误报率。 **v0.5 — 语料库完整性。** `verify-changeset-delta`——每个 PR 的规则更改都会在完整语料库上运行,且 `findings_delta` 块必须与实际差异匹配才能合并。 ## 许可证 Apache 2.0——见 [`LICENSE`](
标签:CI集成, GNU通用公共许可证, MITM代理, Node.js, Python, SOC Prime, Stripe, TypeScript, webhook, 云安全监控, 子域名暴力破解, 安全扫描, 安全插件, 开发工具, 数据可视化, 无后门, 无网络流量, 时序注入, 本地运行, 漏洞修复, 签名验证, 网络安全培训, 自动化攻击, 自动化检测, 逆向工具, 静态分析