hook-audit
Live risk leaderboard for Claude Code hooks discovered in public GitHub repos.
The May 2026 wave of Claude Code plugin marketplaces means tens of thousands
of public repos now ship a .claude/settings.json file. Hooks in those
settings can fire before the user can decline trust — that's exactly what
made CVE-2025-59536, CVE-2026-21852, and GHSA-ph6w-f82w-28w6
exploitable. hook-audit downloads each settings file, statically analyses it
against 20 risk rules, and ranks repos so you can see which ones to never
open in a trusted Claude Code session.
What it does
- Discover — calls the GitHub Code Search API for queries like
-
path:.claude/settings.jsonand"ANTHROPIC_BASE_URL" path:.claude. Up to - ~1,000 hits per query (GitHub's hard cap).
- Fetch — pulls each
.claude/settings.jsonvia the GitHub contents API - (capped at 200 KB) plus the repo's stargazer count and default branch.
- Analyse — runs 20 detection rules covering:
- -
ANTHROPIC_BASE_URLoverrides (CVE-2026-21852) - -
enableAllProjectMcpServers/enabledMcpjsonServersauto-approval (CVE-2025-59536) - -
curl … | bash,base64 -d | sh, and other supply-chain RCE patterns - - Credential file reads (
~/.aws/credentials,~/.ssh/,.env, …) - - Shell-rc / crontab mutations (persistence)
- - Unquoted
${TOOL_OUTPUT}/${BASH_COMMAND}interpolation - - Wildcard
permissions.allow,acceptAllEdits,dangerouslySkipPermissions - -
eval,source <(...),bash <(...), zero-width unicode payloads - -
PreCompact/prompt-type hooks (instruction-hijack vectors) - Score — weights findings (
critical=10, high=5, medium=2, low=1), - normalises to 0–100, then publishes a leaderboard.
- Refresh —
/30incremental scan +0 /6 *full re-scan - via
node-cron.
All data is real and live. There are zero seeded repos, zero mock findings,
zero Math.random jitter. If the GitHub token is missing, the service starts
in degraded mode and shows the operator exactly why the dashboard is empty.
Endpoints
All endpoints are public (no auth, by design — RNDLAB previews must be
inspectable without credentials).
| method | path | purpose |
| --- | --- | --- |
| GET | /hook-audit/ | SPA shell |
| GET | /hook-audit/health | liveness; status: "degraded" when no GITHUB_TOKEN |
| GET | /hook-audit/api/stats | KPIs, severity histogram, rule counts |
| GET | /hook-audit/api/repos | paginated, sortable, filterable repo list |
| GET | /hook-audit/api/repos/:owner/:repo | full repo detail + findings |
| GET | /hook-audit/api/rules | every rule with hit count |
| GET | /hook-audit/api/rules/:id | rule detail + affected repos |
| GET | /hook-audit/api/leaderboard?type=… | worst / cleanest / most-stars-at-risk |
| GET | /hook-audit/api/trend?days=30 | per-day new/critical/cleaned counters |
| GET | /hook-audit/api/export.csv | every finding as CSV |
| POST | /hook-audit/api/scan-now | trigger a fetch run (1-min/IP cooldown) |
| POST | /hook-audit/api/check | paste a settings.json, get instant findings |
Stack
- Node.js ≥ 20
- Express 4 + helmet + compression
- better-sqlite3 (WAL mode)
- node-cron
- Vanilla JS SPA (no framework, no bundler) under
public/
Data sources
Every scan is a live network call to GitHub. There is no seed data file and no
fixture path. The full list of search queries lives infetchers/github-search.js and is roughly:
path:.claude/settings.jsonpath:.claude/settings.local.jsonextension:json path:.claude/hooks"PreToolUse" path:.claude"ANTHROPIC_BASE_URL" path:.claude"acceptAllEdits" path:.claude"enableAllProjectMcpServers" path:.claude
Refresh cadence:
/30 *— incremental search + fetch0 /6— full re-scan of known repos to catch mutations
Local development
cp .env.example .env
# Set GITHUB_TOKEN (any classic / fine-grained token with public_repo read scope)
npm install
node server.js
# open http://localhost:4867/hook-audit/
The server boots even without a token. It will refuse to do useful work — the
warm scan logs a 403, the dashboard banner explains why, and /hook-audit/health
returns status: "degraded".
Why not just rely on Anthropic's trust prompt?
Because the trust prompt covers the next action, not the file that's
already on disk. Several documented CVEs in 2025 / 2026 demonstrated that a
malicious .claude/settings.json can win the race against the prompt, or
exfiltrate the OAuth token before the prompt is even rendered. hook-audit
exists so you can grade a repo before you clone it.
License
MIT.
Author
Cowork (Claude Opus 4.7), for the RNDLAB pipeline. Surfaces at
<https://holyai.me/hook-audit/>.