Claude Code is Anthropic's AI coding assistant. It reads your files, edits your code, and runs commands on your machine. That's powerful, and it's also where things can go wrong:
- It might read files it shouldn't — the private keys you use to log into servers, or your cloud credentials.
- It might run commands you didn't mean — delete files, push broken code, wipe a disk.
- It might be tricked by something it read — a web page or document that quietly tells it to do things you didn't ask for.
This repo is a security and best-practices setup for Claude Code. It provides security hooks that block risky actions before they execute, and guidance on permissions, MCP servers, and safe workflows — so you're covered both technically and operationally when using Claude Code on a real machine.
⚠️ Platform note: Tested on macOS. Works on Linux with minor adjustments — see Platform differences below. Windows (WSL2) is untested.
| Component | What it guards |
|---|---|
hooks/protect.py |
Blocks dangerous deletions (rm -rf, find -delete, disk writes) and git push --force |
hooks/protect_secrets.py |
Blocks reads/exfil of credential files and raw secret literals in commands |
hooks/protect_output.py |
Blocks tool outputs containing secret material before they reach Claude's context |
hooks/protect_mcp_config.py |
Blocks @latest in MCP server args when Claude writes or edits a settings file |
hooks/hook_logger.py |
Shared JSONL audit logger — appended to by all hooks above |
hooks/validate-mcp.py |
Standalone validator — checks settings files for @latest; use in CI or as a git pre-commit hook |
hooks/git-hooks/pre-commit |
Git pre-commit hook script that calls validate-mcp.py --staged |
install.py |
Automated installer — copies hooks and wires settings.json |
settings/settings.json.example |
Global settings with all security hooks wired |
settings/settings.precompact.json.example |
Extends the above with the PreCompact audit hook |
settings/settings.project.json.example |
Project-level settings template (commit to repo) |
settings/settings.local.json.example |
Personal project override template (git-ignored) |
docs/ |
Detailed guides for hooks, permissions, MCP, CLAUDE.md, context, and CLI |
Option A — Automated installer (recommended)
python3 install.pyThe installer:
- Copies all hook scripts to
~/.claude/hooks/ - Resolves your home directory at install time and writes absolute paths into
~/.claude/settings.json - Is idempotent — safe to re-run; updates paths if the install location changed
- Backs up your existing
settings.jsontosettings.json.bakbefore writing
Preview what it would do without writing anything:
python3 install.py --dry-runRemove the hook entries it added (scripts stay on disk):
python3 install.py --uninstallOption B — Manual setup
Expand manual steps
Copy hook scripts:
mkdir -p ~/.claude/hooks
cp hooks/*.py ~/.claude/hooks/Wire hooks in ~/.claude/settings.json (create it if it doesn't exist):
{
"permissions": {
"defaultMode": "default"
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "python3 /YOUR/HOME/.claude/hooks/protect.py" },
{ "type": "command", "command": "python3 /YOUR/HOME/.claude/hooks/protect_secrets.py" }
]
}
],
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "python3 /YOUR/HOME/.claude/hooks/protect_output.py" }
]
}
]
}
}Replace /YOUR/HOME with echo $HOME. Claude Code's settings.json does not expand ~.
See settings/settings.json.example for the full recommended configuration.
Start a Claude Code session and run:
/hooks
# Expected: entries listed under PreToolUse[Bash] and PostToolUse[Bash]
echo '{"tool_input":{"command":"cat ~/.ssh/id_rsa"}}' | python3 ~/.claude/hooks/protect_secrets.py
# Expected: {"decision": "block", "reason": "BLOCKED: ..."}
echo '{"tool_output":"AKIAIOSFODNN7EXAMPLE"}}' | python3 ~/.claude/hooks/protect_output.py
# Expected: {"decision": "block", "reason": "BLOCKED: ..."}| Guide | Contents |
|---|---|
| docs/hooks.md | Hook event types, each script's detection logic, PreCompact example, installation |
| docs/permissions.md | default / acceptEdits / bypassPermissions — when to use each, scoping |
| docs/mcp.md | MCP server configuration, trust tiers, prompt injection mitigations |
| docs/security-checklist.md | Pre-flight checklist covering hooks, credentials, git, and MCP |
| docs/claude-md.md | Writing effective CLAUDE.md files — memory hierarchy, imports, # shorthand, starter template |
| docs/context-management.md | /compact vs /clear, PreCompact hook, when to start a new session |
| docs/cli-and-models.md | Slash commands, keyboard shortcuts, --print, --fast, model selection, effortLevel |
Claude Code merges settings in this order — later entries win:
flowchart TD
A["🌍 Global\n~/.claude/settings.json\nApplies to every project"]
B["📁 Project\n<project>/.claude/settings.json\nCommitted to the repo"]
C["👤 Personal override\n<project>/.claude/settings.local.json\nGit-ignored, never committed"]
M["⚙️ Final merged settings\nConflicts resolved — highest priority wins"]
A -->|"priority 1 — lowest"| M
B -->|"priority 2 — overrides global"| M
C -->|"priority 3 — highest"| M
style A fill:#1e3a5f,color:#ffffff,stroke:#4a90d9
style B fill:#1e4d2b,color:#ffffff,stroke:#4caf50
style C fill:#4d2b1e,color:#ffffff,stroke:#e07b39
style M fill:#2d2d2d,color:#ffffff,stroke:#888888
🔒 Security rule: Put security hooks in the global file (
~/.claude/settings.json) so they apply to every project and cannot be overridden or omitted by project-level settings.
⚠️ Warning:.claude/settings.local.jsoncontains personal overrides and must never be committed to version control. Add it to your global.gitignore:echo '.claude/settings.local.json' >> ~/.gitignore_global git config --global core.excludesfile ~/.gitignore_global
This setup was developed and tested on macOS. The hooks work as-is on most Linux distributions, but a few patterns are macOS-specific and should be reviewed:
| Pattern | macOS | Linux equivalent |
|---|---|---|
diskutil erase/zeroDisk/secureErase |
protect.py |
Add wipefs, wipe, shred /dev/... |
srm (secure-remove) |
protect.py |
shred is already covered |
/dev/disk*, /dev/rdisk* (raw disk) |
protect.py |
Add /dev/sd*, /dev/nvme*, /dev/vd* |
pbcopy (clipboard) |
protect_secrets.py READ_COPY_VERBS |
Add xclip, xsel, wl-copy |
On Windows / WSL2: untested. The python3 binary may be python. Credential paths (~/.ssh/, ~/.aws/) resolve correctly under WSL2; native Windows paths are not covered.
To add Linux-specific patterns, edit hooks/protect.py and hooks/protect_secrets.py and re-run python3 install.py. See docs/hooks.md for the pattern format.
All block events are written to:
~/.claude/hooks/logs/security-hooks.jsonl
Monitor in real time:
tail -f ~/.claude/hooks/logs/security-hooks.jsonl | python3 -m json.tool