Safety Hooks¶
Claude Code safety infrastructure that prevents accidental damage and provides audit logging.
Overview¶
Three hooks protect this infrastructure:
| Hook | Type | Purpose |
|---|---|---|
dangerous-command-blocker.py |
PreToolUse | Block catastrophic commands |
production-service-guard.py |
PreToolUse | Confirm critical service operations |
audit-logger.py |
PostToolUse | Log all tool executions |
Dangerous Command Blocker¶
Location: .claude/hooks/PreToolUse/dangerous-command-blocker.py
Blocked Patterns¶
| Pattern | Description |
|---|---|
rm -rf / |
Removing root filesystem |
rm -rf /* |
Removing all root contents |
docker system prune -a |
Docker system prune all |
DROP DATABASE |
SQL DROP DATABASE |
TRUNCATE TABLE |
SQL TRUNCATE TABLE |
mkfs |
Filesystem format commands |
dd if=.* of=/dev/ |
Direct disk writes |
> /dev/sd |
Overwriting disk devices |
chmod -R 777 / |
Dangerous permission changes |
chown -R .* / |
Dangerous ownership changes |
System Directory Protection¶
Writes to these directories are blocked:
- /etc/ - System configuration
- /usr/ - System binaries
- /boot/ - Boot files
- /sys/ - Kernel interface
- /proc/ - Process information
Bypass¶
If a blocked command is legitimately needed:
1. The hook returns a BLOCK decision with explanation
2. You must manually execute the command outside Claude Code
3. This ensures human verification of dangerous operations
Production Service Guard¶
Location: .claude/hooks/PreToolUse/production-service-guard.py
Protected Services¶
supa-db - PostgreSQL database
traefik - Reverse proxy
prometheus - Metrics collection
grafana - Dashboards
alertmanager - Alert routing
loki - Log aggregation
authentik - SSO provider
adguard - DNS filtering
gitea - Git server
cache-redis - Shared cache
session-redis - Session storage
Protected Operations¶
These operations on protected services require confirmation:
- stop - Stopping containers
- down - Bringing down compose stacks
- rm - Removing containers
- remove - Removing containers
- kill - Killing containers
- restart - Restarting containers
Confirmation Required¶
When a protected operation is detected, Claude will ask for confirmation before proceeding. This prevents accidental disruption of critical services.
Example blocked operation:
docker compose -f supabase/docker-compose.yml down
# Requires confirmation: "Stop critical service supa-db?"
Audit Logger¶
Location: .claude/hooks/PostToolUse/audit-logger.py
Log Format¶
Each entry includes: - timestamp: ISO 8601 format - session_id: Claude session identifier - tool: Tool name (Bash, Read, Write, etc.) - input_summary: First 200 characters of input - output_status: success/error
Log Location¶
Example Log Entry¶
{
"timestamp": "2025-12-04T10:30:45.123456",
"session_id": "abc123",
"tool": "Bash",
"input_summary": "docker ps --format \"table {{.Names}}\\t{{.Status}}\"",
"output_status": "success"
}
Viewing Logs¶
# Recent activity
tail -50 /srv/dockerdata/.claude/logs/audit.log
# Today's Bash commands
grep "$(date +%Y-%m-%d)" /srv/dockerdata/.claude/logs/audit.log | grep '"tool": "Bash"'
# Count operations by tool
grep -o '"tool": "[^"]*"' /srv/dockerdata/.claude/logs/audit.log | sort | uniq -c | sort -rn
# Find errors
grep '"output_status": "error"' /srv/dockerdata/.claude/logs/audit.log
Log Rotation¶
Audit logs grow over time. Consider rotating:
# Manual rotation
mv /srv/dockerdata/.claude/logs/audit.log /srv/dockerdata/.claude/logs/audit.log.$(date +%Y%m%d)
touch /srv/dockerdata/.claude/logs/audit.log
Hook Configuration¶
Hooks are registered in .claude/settings.local.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 /srv/dockerdata/.claude/hooks/PreToolUse/dangerous-command-blocker.py"
},
{
"type": "command",
"command": "python3 /srv/dockerdata/.claude/hooks/PreToolUse/production-service-guard.py"
}
]
}
],
"PostToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "python3 /srv/dockerdata/.claude/hooks/PostToolUse/audit-logger.py"
}
]
}
]
}
}
Creating Custom Hooks¶
Hook Input¶
Hooks receive JSON on stdin:
Hook Output¶
Hooks output JSON decision:
Or to block:
Hook Template¶
#!/usr/bin/env python3
import json
import sys
def main():
# Read input from stdin
input_data = json.loads(sys.stdin.read())
tool_name = input_data.get('tool_name', '')
tool_input = input_data.get('tool_input', {})
# Your logic here
should_block = False
reason = ""
if should_block:
result = {"decision": "BLOCK", "reason": reason}
else:
result = {"decision": "ALLOW"}
print(json.dumps(result))
if __name__ == "__main__":
main()
Related Documentation¶
- Claude Code Overview - Full Claude Code configuration
- Security - General security practices