Alert on .env File Change Outside a Hook-Controlled Edit
The credential-file-read hook stops the agent from reading a `.env` file; this is the complementary check on the write side — it fires on any change to a watched env file, whether the agent caused it, a background process did, or something else entirely, which is exactly the kind of out-of-band change a tool-call log alone won't show.
1. Save this as the hook script
#!/usr/bin/env python3
"""FileChanged hook — flags a change to a watched .env file.
Wire with matcher ".envrc|.env" (exact-match characters only)."""
import json, sys, datetime
from pathlib import Path
data = json.load(sys.stdin)
log_path = Path(data.get("cwd", ".")) / ".claude" / "env_file_change_log.jsonl"
log_path.parent.mkdir(parents=True, exist_ok=True)
entry = {"ts": datetime.datetime.utcnow().isoformat() + "Z",
"file_path": data.get("file_path"), "change_type": data.get("change_type")}
with open(log_path, "a") as f:
f.write(json.dumps(entry) + "\n")
print(f"NOTICE: {data.get('file_path')} changed ({data.get('change_type')}).", file=sys.stderr)
sys.exit(0)
Save to a path inside your project, e.g. .claude/hooks/alert-env-file-change-outside-hook.sh, then chmod +x it.
2. Wire it into .claude/settings.json
{
"hooks": {
"FileChanged": [
{
"matcher": ".envrc|.env",
"hooks": [
{"type": "command", "command": "python3 .claude/hooks/alert-env-file-change-outside-hook.py"}
]
}
]
}
}
Before you use this
A hook runs with your local shell's permissions — review the script above before adding it, the same way you'd review any script you didn't write yourself. See our guide on hook security risks for the specific CVEs this category of feature has already produced.
Related hooks