Protect .env and secret files
Blocks Claude from writing to .env files, keys, and credential files - unless you say otherwise.
PreToolUseEdit|Writeblocking riskneeds manual checkWhat it does
Fires before Edit/Write tool calls. The script checks the target file's name against a protected list (.env, .env.*, *.pem, *.key, anything with 'credentials' or 'secrets' in the name) and returns a permissionDecision of deny with a clear reason. Everything else passes through to the normal permission flow. Official-guide pattern, updated to the current JSON decision output.
When it runs
| Event | PreToolUse |
| Matcher | Edit|Write |
| Scope | project or user |
| Platforms | macos, linux |
| Timeout | 10s |
| Requires | jq |
| Failure mode | block |
| Verified against | Claude Code hooks reference, event/matcher schema checked 2026-09-10 |
| Test status | needs manual check - run the test steps below before relying on it |
Config
Merge this into .claude/settings.json (or ~/.claude/settings.json for user scope) - or use the generator to combine hooks.
{
"hooks": {
"PreToolUse": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/protect-env-secrets.sh",
"timeout": 10
}
],
"matcher": "Edit|Write"
}
]
}
}Script
Save as .claude/hooks/protect-env-secrets.sh and chmod +x it.
#!/bin/bash
# protect-env-secrets: deny writes to secret-bearing files.
set -u
file=$(jq -r '.tool_input.file_path // empty')
[ -z "$file" ] && exit 0
base=$(basename "$file")
protected=0
case "$base" in
.env|.env.*|*.pem|*.key|*.p12|*.pfx) protected=1 ;;
*credential*|*secret*) protected=1 ;;
esac
if [ "$protected" = "1" ]; then
jq -n --arg f "$file" '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: ("Writes to secret-bearing files are blocked by the protect-env-secrets hook (" + $f + "). Edit it yourself, or temporarily disable the hook.")
}
}'
exit 0
fi
exit 0
Install
- Save the script to .claude/hooks/protect-env-secrets.sh and chmod +x it.
- Add the config fragment to .claude/settings.json (project scope) or ~/.claude/settings.json (everywhere).
Test it
- Positive: ask Claude to 'add a line to .env' - the write must be denied with the hook's reason visible.
- Negative: ask Claude to edit .env.example... note: this one IS caught too (matches .env.*). Rename it or narrow the case list if that bothers you.
- Negative: ask Claude to edit src/index.ts - normal flow, no denial.
If it blocks something you wanted
When Claude is blocked it sees the deny reason and can tell you. To let a specific edit through, make the edit yourself, or remove/disable the hook entry in settings.json for that session.
Sources
- Claude Code hooks reference (checked 2026-09-10)
- Claude Code hooks guide (checked 2026-09-10)
Related hooks
Block destructive shell commands
Blocks rm -rf, disk tools, force pushes, and destructive git commands before they run.
PreToolUseBashblocking riskneeds manual checkSecret scan on changed files
Runs gitleaks on every file Claude edits and warns when something looks like a secret.
PostToolUseEdit|Writereview riskneeds manual checkPro Pack (later)
Framework bundles, cross-platform scripts, tested blocking policies, and a test harness. Free recipes and the generator stay free.
Waitlist opens soon.