secret-commit-guard
Skill1 starsChanged 6 days agoMITView on GitHub
Git pre-commit hook that blocks commits containing secrets, for any repo. Scans only the lines being added for API keys and tokens (Anthropic, OpenAI, AWS, GitHub, GitLab, Stripe, Slack, Google, npm, PyPI, Hugging Face and more), private keys, JWTs, passwords in database URLs, auth headers and KEY=value assignments, and refuses .env, *.pem, *.p12 and id_rsa files outright. Installs next to an existing pre-commit hook without replacing it, and can also scan the working tree or the whole git history. Use when the user wants to stop secrets being committed, set up a pre-commit secret check, asks whether a repo has leaked keys in its history, or before open-sourcing a repo.
- Reads credentials
In this repository25 other files it gives its agents.
- bash-checkpointSkill
- brave-cdpSkill
- cf-backupSkill
- cf-pages-deploySkill
- claude-md-doctorSkill
- claude-safety-auditSkill
- claude-sandboxSkill
- claude-token-auditSkill
- cws-extension-checkSkill
- env-doctorSkill
- event-recap-videoSkill
- freelance-proposalSkill
- gif-makerSkill
- itch-html5-packageSkill
- link-preview-checkSkill
- mcp-auditSkill
- package-guardSkill
- pwa-checkSkill
- read-guardSkill
- readme-upgradeSkill
- release-notesSkill
- session-handoffSkill
- site-qaSkill
- thumbnail-makerSkill
- visual-diffSkill
The file
We split this skill into the rules it gives an agent
4 rules. About 0 are common, 1 is distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 31
Only added lines in the staged diff are scanned, so an old line elsewhere in an edited file doesn't block you.
- Very specific: line 34
Put secret-guard: allow in a comment on the line (// secret-guard: allow, # secret-guard: allow).
- Very specific: line 42
If core.hooksPath is set (husky, lefthook, a shared hooks folder), install changes nothing and prints the line to add to that hook: node …
- Very specific: line 47
If node is not on PATH when git runs the hook, it prints a warning and allows the commit.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
secret-commit-guard
scripts/secret-guard.mjs is zero-dependency Node 22+. It works in any git repo, not only ones Claude touches, and in Git Bash on Windows (the hook is a sh script that calls node).
node secret-guard.mjs install # in the repo: adds .git/hooks/pre-commit (keeps an existing hook and runs it after)
node secret-guard.mjs uninstall # removes it and puts the previous hook back
node secret-guard.mjs scan-staged # what the hook runs: added lines of the staged diff
node secret-guard.mjs scan [path] # files git tracks in the working tree (optionally one folder)
node secret-guard.mjs scan-history # every commit on every branch: commit, file:line, type
node secret-guard.mjs test 'API_KEY=…' # what the rules say about one line or filename
node tests/run.mjs # 93 tests: 57 rule cases + install/commit/chain/uninstall/history in throwaway reposinstall copies the script into .git/hooks/secret-guard.mjs, so the hook keeps working if this skill folder moves. Running install again updates it in place. Exit codes: scan and scan-history return 1 when they find something, so they also work in CI.
What it blocks
| Kind | Examples |
| Provider keys (by prefix) | sk-ant-, OpenAI sk-/sk-proj-, Stripe sk_live_/rk_live_/whsec_, AWS AKIA… + aws_secret_access_key, GitHub ghp_/gho_/github_pat_, GitLab glpat-, Slack xox?- + webhook URLs, Google AIza… + GOCSPX-, npm npm_, PyPI pypi-, Hugging Face hf_, Resend, SendGrid, Groq, DigitalOcean, Shopify, Telegram bot, Azure storage AccountKey= |
| Structural | -----BEGIN … PRIVATE KEY-----, JWTs, postgres://user:pass@… (and mysql, mongodb, redis, amqp, mssql), https://user:pass@…, Authorization: Bearer …, --password=… |
| Assignments | UPPER_NAME_WITH_TOKEN/SECRET/PASSWORD/API_KEY=value (env files, shell, YAML), and a quoted literal assigned to any name containing key/secret/token/password only if it looks random (20+ chars with 2+ digits and Shannon entropy ≥ 3.7, or 32+ hex chars) |
| Files, whatever they contain | .env, .env.local, .env.production … (not .env.example/.sample/.template), *.pem, *.key, *.p12, *.pfx, *.jks, id_rsa/id_ed25519/id_ecdsa/id_dsa |
Not flagged: values that are references (process.env.X, os.environ[…], ${VAR}, ${{ secrets.X }}, config.value, paths, URLs, numbers), placeholders (changeme, your-key-here, <…>, xxxx, {password}, anything containing "example", like AWS's AKIAIOSFODNN7EXAMPLE), one-line BEGIN…XXXX…END key samples in docs, lockfiles, and binary files (by content; binary sensitive filenames like .p12 are still blocked). On a copy of npm's own source (2,246 files) scan reported one finding, a base64 password in a README.
Only added lines in the staged diff are scanned, so an old line elsewhere in an edited file doesn't block you. The full secret is never printed, only the first 6 characters.
False positives
- Put
secret-guard: allowin a comment on the line (// secret-guard: allow,# secret-guard: allow). - Or list paths in
.secret-guard-ignoreat the repo root, one gitignore-style glob per line:tests/fixtures/,*.p12,docs/**/*.md. A pattern without/matches at any depth.
When it blocks
It prints file:line type (first 6 chars…) for each finding, then the fixes: move the value to an environment variable or secret manager; git reset HEAD <file> and add the file to .gitignore; mark a false positive; and git commit --no-verify only if you are certain. A secret that was already pushed is compromised: rotate it at the provider first, then clean history (git filter-repo / BFG) if needed.
Existing hooks and hook managers
- An existing
.git/hooks/pre-commitis renamed topre-commit.secret-guard-chainedand runs after the scan with the same arguments; its exit code still counts.uninstallrenames it back. - If
core.hooksPathis set (husky, lefthook, a shared hooks folder),installchanges nothing and prints the line to add to that hook:node "<path>/secret-guard.mjs" scan-staged || exit 1.
Limits (tell the user)
- Pattern and entropy based. It catches common, well-formed keys; it misses secrets with no recognizable shape (a plain password in a string assigned to an unrelated name, a key split across lines or strings, base64-encoded or encrypted blobs).
- A git hook is local and optional:
git commit --no-verify, a fresh clone, or a GUI that skips hooks bypasses it. For a team, also runscanin CI or turn on GitHub push protection. - If
nodeis not on PATH when git runs the hook, it prints a warning and allows the commit. Internal errors also allow the commit, with a message. scan-historyreadsgit log -p --all(excludingrefs/claude-checkpoints/*snapshots from bash-checkpoint). It reports each secret once, at the newest commit that added it; it does not see reflog-only commits or other clones.scanreads files up to 2 MB and skips lockfiles;.secret-guard-ignoreapplies to all three scan modes.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

