All notable changes to claude-guardrails are documented here. Format loosely follows Keep a Changelog.
Adds detection for two non-hex wallet private-key formats and hardens the BIP39 corpus. Until now the only crypto-key coverage was the 64-hex rule (which catches EVM keys incidentally) and the wordlist-based mnemonic scanner. Base58-encoded keys slipped through entirely. Both new rules lean on structured prefixes and fixed lengths, so they stay false-positive safe: xpub extended public keys, IPFS CIDs, P2PKH addresses, and non-5/K/L Base58 strings all pass clean.
patterns/secrets.json- two rules: Bitcoin WIF private key (Base58, leading5/K/L, 51-52 chars) and BIP-32 extended private key (xprv/yprv/zprv+ testnettprv/uprv/vprv, ~111 chars). Both apply everywhere the shared pattern set is used: prompts (scan-secrets.sh), commits (scan-commit.sh), and the personal-layer command/file checks.- New
wallet-key-regexCI scenario (9 assertions) - WIF + xprv block; xpub, IPFS CIDv0, P2PKH address, and non-5KLBase58 pass. Vectors are built at runtime from a non-hex Base58 filler so no literal key lands in the source. - Seven
bip39-scanCI cases - 24-word mnemonic and 4-per-line whitespace grid must block; two-sub-12 runs, a long-token break, and underscore-joined words must pass; the numbered-list mnemonic limitation is pinned as a documented pass; and a 10k-word ReDoS timing guard. CI: 11 scenarios / 105 assertions -> 12 scenarios / 121 assertions.
- Numbered-list mnemonics (
1. word 2. word ...) are a known, documented gap: digit+dot enumerators break the whitespace run. A future enumerator-stripping pass would close it but adds false-positive surface, so it is deferred. - Solana / raw Base58 keys are intentionally out of scope: no structural prefix means no false-positive-safe regex.
Re-run bash install.sh <variant> (or npx claude-guardrails install) to pick up the new patterns/secrets.json.
Hotfix for v0.3.7. The wordlist-based BIP39 detector shipped in #9 replaced one false-positive regime with another: it tokenised prompts with scan("\b[a-z]{3,8}\b"), flattening punctuation out before counting runs, so any prose with 12+ BIP39 vocabulary words in a row got blocked. BIP39 wordlists include common English words (what, else, you, need, feel, that, ready, then, okay, ...), so a single user-reported sentence, "question. What else do you need? If you feel that's ready, then that's ready, okay.", tokenised to 13 wordlist hits and tripped the threshold.
scan-secrets.sh(both variants) - the BIP39 pass now only flags runs of 12+ candidate tokens (3-8 lowercase letters) that are separated solely by whitespace, then confirms every token in the run is a wordlist member. Real mnemonics are space-separated in every wallet, so they still match; prose is broken up by punctuation, contractions, digits, or short words and no longer accumulates a run. Verified against the exact prompt the user reported: it now passes. Reported by @nntruonghan (#10).
- Two
bip39-scanCI cases - 12 wordlist words joined by", "must pass (was the failing case), newline-wrapped 12-word mnemonic must still block. CI: 11 scenarios / 103 assertions -> 11 scenarios / 105 assertions.
Re-run bash install.sh <variant> (or npx claude-guardrails install) to pick up the new scan-secrets.sh. If you hit a BIP39 block on ordinary prose under v0.3.7, upgrading to v0.3.8 resolves it.
Replaces the BIP39 mnemonic detector. The previous pattern was (?i)(^|\s)([a-z]{3,8}\s+){11}[a-z]{3,8}(\s|$) — "12 or more consecutive lowercase words of 3-8 characters." Ordinary English prose hits that pattern routinely (30-plus short-word runs are common in natural writing), so scan-secrets.sh was blocking legitimate prompts. The regex was never a real BIP39 check; it was a short-word run detector with the wrong name.
- Wordlist-based BIP39 detection -
scan-secrets.shnow loads the official 2048-word BIP39 English wordlist and blocks only when the prompt contains 12+ consecutive tokens all drawn from the list. BIP39 deliberately excludes common English stop-words ("the", "and", "for", "you", ...), so natural prose has near-zero probability of producing a false positive. Verified against the exact prose that previously tripped the regex: it now passes. - Removed the broken regex from
patterns/secrets.json. The remaining rules (AWS, GitHub, Anthropic, OpenAI, Google, Slack, Stripe, 1Password, PEM, 64-hex,API_KEY=value) are unchanged. The labeled-secret rule already catchesmnemonic: .../seed_phrase: ...assignments, so labeled mnemonic leaks are still covered. - Scoping: the wordlist check runs in
scan-secrets.sh(prompt scanner) only, not inscan-commit.sh. Running it on committed code hit too many false positives on legitimate content (documentation, test fixtures, the wordlist file itself). Commit-time leaks via labeled assignments are still caught by the labeled-secret regex.
patterns/bip39-english.txt- the 2048-word list fetched frombitcoin/bips. Installed to~/.claude/hooks/patterns/bip39-english.txtalongsidesecrets.json. Override path with$CLAUDE_GUARDRAILS_BIP39_WORDLISTfor tests.bip39-scanCI scenario - 7 assertions covering: prose with many short words allowed, real 12-word mnemonic blocked, 11-word near-miss allowed, missing wordlist fails open, AWS key still blocked alongside the BIP39 pass, plus install sanity on hook + wordlist files. Wired into the GitHub Actions matrix. CI: 10 scenarios / 96 assertions -> 11 scenarios / 103 assertions.
Re-run bash install.sh <variant> (or npx claude-guardrails install) to pick up the new scan-secrets.sh, scan-commit.sh, and patterns/bip39-english.txt. The merge is additive and preserves custom deny rules and hooks. Until you re-run the installer, the old inline-regex scanner keeps producing false positives on ordinary prose.
Follow-up to v0.3.5. The previous release fixed the $schema bug for anyone re-running the installer (the jq merge always prefers the new file's value), but the fix happened silently — a user who ran v0.3.6 install after months on a broken v0.3.0–v0.3.4 install had no way to know their guardrails had been inactive the whole time. This release surfaces that state so users understand the remediation.
- Install-time remediation notice — when
install.shdetects the pre-v0.3.5$schemaURL in an existing~/.claude/settings.json, it prints a clearly-borderedNOTICEafter the merge explaining (1) what the old value was, (2) that it silently disabled every guardrail in prior installs, (3) that it's been corrected, and (4) a pointer to the advisory in #6. Fresh installs and existing installs with the correct$schemastay quiet (no false positive). schema-remediationCI scenario — 4 assertions covering: broken schema triggers the notice; post-install$schemais corrected; fresh install is quiet; correct-existing install is quiet. Wired into the GitHub Actions matrix. CI: 9 scenarios / 92 assertions → 10 scenarios / 96 assertions.
Critical hotfix. The wrong $schema URL shipped in every release from v0.3.0 through v0.3.4 caused Claude Code to silently discard the entire settings.json. Anyone who installed claude-guardrails fresh on a machine during that window got zero active guardrails — no deny rules, no hooks, no scanner. Reported and fixed by @valtumi in #3.
$schemaURL in bothlite/settings.jsonandfull/settings.json— washttps://claude.ai/schemas/claude-code-settings.json(not accepted by Claude Code), nowhttps://json.schemastore.org/claude-code-settings.json. Credit: @valtumi (#3).
- CI assertion on
$schema—test_lite_fresh,test_full_fresh, andtest_merge_existingnow verify the post-install$schemafield matches the schemastore URL. This class of bug previously escaped CI because JSON-parseability and install/uninstall logic tests don't probe whether Claude Code actually honors the file. CI suite: 9 scenarios / 89 assertions → 9 scenarios / 92 assertions.
If you have a pre-0.3.5 install, re-running npx claude-guardrails install merges the corrected $schema value into your existing ~/.claude/settings.json (the jq merge prefers the new file's value). Or manually edit the $schema field to https://json.schemastore.org/claude-code-settings.json. Until you do one of these, your guardrails are not active.
Adds a commit-time secret scanner, closing the gap between the prompt-submit scanner (which catches pasted credentials) and the code Claude writes and then tries to commit. Also tightens the direct-push guardrail's regex to kill a real-world false positive discovered while shipping this change.
scan-commit.shPreToolUse hook (both variants) — fires onBashtool calls, inspects the command forgit commit, runsgit diff --cached --unified=0through the same regex set asscan-secrets.sh, and blocks (exit 2) on match. Reports matched pattern name(s) and the list of staged files. Word-boundary matching avoids false positives ongit commit-treeplumbing.- Shared pattern file
patterns/secrets.json— single source of truth for credential regexes, loaded by bothscan-secrets.sh(prompt) andscan-commit.sh(staged diff). Installs to~/.claude/hooks/patterns/secrets.json. Closes v0.4 WS2 (pattern extraction). - New CI scenario
scan-commit— functional test that stands up a throwaway git repo inside the sandboxed HOME and asserts seven response cases: clean diff allowed, AWS key blocked,git statuspasses through,git commit-treeplumbing not intercepted,git add && git commitchained form blocked, heredoc-wrapped commit message blocked, missing patterns file fails open. - New CI scenario
push-hook— functional test for the direct-push-to-protected-branch guardrail. 13 cases: 7 block (direct, force, master, production,&&-chained,;-chained, subshell-wrapped) and 6 allow (PR body with trigger phrase, echo commentary, grep searching for phrase, feature branch,main-prefixed branch name, non-push git command).
- PreToolUse hook counts: lite 3 → 4, full 5 → 6.
- Both
scan-secrets.shscripts refactored to load patterns from../patterns/secrets.json(supports$CLAUDE_GUARDRAILS_PATTERNSoverride for testing). Behavior unchanged; pattern list and output format preserved. - Direct-push hook regex tightened to only match
git pushat a command-boundary position (start of string, or after&,|,;,(). The old regex matched anywhere in the command string and false-positived ongh pr create --body "... git push origin main ..."— hit when creating this release's own PR. The trailing branch-name anchor also now requires whitespace / separator / EOS afterwards, so branches likemain-feature-branchno longer trigger. Known regression:sudo git push origin mainno longer matches — tradeoff documented. - CI suite: 7 scenarios / 58 assertions → 9 scenarios / 89 assertions, all green.
install.shcopiespatterns/secrets.json+scan-commit.sh;uninstall.shremoves~/.claude/hooks/scan-commit/and~/.claude/hooks/patterns/.
Alignment pass against Trail of Bits' claude-code-config threat model and deny-rule coverage.
- Privacy
envflags in both variants —DISABLE_TELEMETRY=1,DISABLE_ERROR_REPORTING=1,CLAUDE_CODE_DISABLE_FEEDBACK_SURVEY=1. Intentionally does not setCLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFICbecause that flag also disables auto-updates. - Broader credential deny coverage — both variants add
~/.kube/**(cluster bearer tokens) and~/.azure/**(Azure CLI creds).fulladds six crypto-wallet app-support paths (MetaMask, Exodus, Phantom, Solflare, Electrum on macOS +~/.electrum/**on Linux). - Redundant Bash deny rules as belt-and-suspenders alongside the PreToolUse hooks:
Bash(sudo *),Bash(mkfs *),Bash(dd *),Bash(rm -rf *).
- Deny rule counts: lite 15 → 21, full 28 → 40.
- README "How It Works" restructured so
/sandboxleads as the enforcement boundary and the other layers are framed explicitly as "can be bypassed." Adds a devcontainer pointer to Trail of Bits' companion repo for untrusted-code review sessions. - CI test
get_deny_count()expectations updated across all 7 scenarios; still 58/58 assertions green.
scan-secrets.shUserPromptSubmit hook in both variants. Blocks prompts containing live credentials (AWS, GitHub, Anthropic, OpenAI, Google, Slack, Stripe, 1Password tokens; PEM private-key headers; BIP39 mnemonics; 64-hex private keys;API_KEY=valueassignments). Prevents pasted secrets from reaching the model or being persisted to~/.claude/projects/.../*.jsonl. Pure bash + jq — no new runtime dependency. ~11 ms median per invocation.- New
docs/maintenance.mdquarterly review checklist covering deny rules, PreToolUse hooks, the secret scanner, the prompt injection defender, and upstream threat research. - v0.4 "Maintainability & Sustainability" plan in
docs/plan.md— pattern test corpus, data-file extraction, upstream tracking.
tests/ci-test.shnow forcesHOMEto a freshmktemp -dat script start and aborts if$HOMEmatches real-home patterns (/Users/*,/home/*,/root). Replaces the previousCI=trueenvironment-variable gate onclean_claude_dir(), which was bypassable and led to the deletion of a real user's~/.claude/during development.
- README,
full/SETUP.md,lite/SETUP.md, andCLAUDE.mdupdated to document the new UserPromptSubmit layer and the 6-layer defense-in-depth model. install.sh/uninstall.shnow handleUserPromptSubmithooks in both variants (merge via jq on install, surgical removal on uninstall).- CI test suite grew from 7 scenarios / ~40 assertions to 7 scenarios / 58 assertions; all green.
.claude/settings.local.json(Claude Code writes machine-specific permission grants here during local sessions).
Initial npm distribution. See git history (babf128 and earlier) for the install script, uninstall script, and lite/full variant baseline.