Production-ready security hooks for Claude Code CLI with Pydantic validation
This repository provides two versions of comprehensive security hooks for Claude Code CLI:
- Version 1 (Basic): Template-based approach with manual JSON parsing
- Version 2 (Advanced): Pydantic-validated with type safety and enhanced error messages
Both versions address the same 15 security gaps in Claude Code CLI and are production-tested.
Simple, dependency-free security hook using manual validation:
# 1. Download hook
curl -o ~/.claude/hooks/security_hook_v1.py \\
https://raw.githubusercontent.com/hah23255/claude-hooks-public/main/v1-basic/security_hook.py
# 2. Make executable
chmod +x ~/.claude/hooks/security_hook_v1.py
# 3. Configure in settings.json
# (See Installation Guide below)Advanced hook with Pydantic schema validation and type safety:
# 1. Install Pydantic
pip install pydantic
# 2. Download schemas and hook
curl -o ~/.claude/hooks/schemas.py \\
https://raw.githubusercontent.com/hah23255/claude-hooks-public/main/v2-pydantic/schemas.py
curl -o ~/.claude/hooks/security_hook_v2.py \\
https://raw.githubusercontent.com/hah23255/claude-hooks-public/main/v2-pydantic/security_hook.py
# 3. Make executable
chmod +x ~/.claude/hooks/security_hook_v2.py
# 4. Configure in settings.json
# (See Installation Guide below)✅ 15 Security Gaps Addressed:
- Read tool monitoring
- Bash command scanning
- Glob/Grep enumeration detection
- Fail-closed storage policy
- Security scanner integration
- Network activity monitoring
- Rate limiting
- Task/agent monitoring
- Symbolic link resolution
- Enhanced Edit controls
- Configuration file protection
✅ Security Controls:
- File access restrictions (whitelist/blacklist)
- Dangerous bash pattern detection
- Network domain whitelisting
- Rate limiting per tool
- Comprehensive audit logging
- Fail-closed by default
✅ Monitoring:
- All tool operations logged
- Detailed audit trail (JSON Lines format)
- Decision tracking (ALLOWED/BLOCKED/RATE_LIMITED)
- Timestamp and session tracking
✅ Pydantic Schema Validation:
- Type-safe operations with IDE support
- Clear field-level error messages
- Automatic validation before security checks
- Tool whitelist enforcement
✅ Enhanced Error Messages:
# v1 (Basic):
KeyError: 'tool_name'
# v2 (Pydantic):
🚨 VALIDATION ERROR:
Input validation failed:
- tool_name: Field required [type=missing]
✅ Backward Compatibility:
- Graceful fallback if schemas not available
- No breaking changes
- Drop-in replacement for v1
| Feature | V1 (Basic) | V2 (Pydantic) |
|---|---|---|
| Dependencies | None | Pydantic v2 |
| Type Safety | ❌ No | ✅ Yes |
| Error Messages | Basic | Field-level |
| Validation | Manual | Automatic |
| IDE Support | Limited | Full autocomplete |
| Learning Curve | Easy | Moderate |
| Production Ready | ✅ Yes | ✅ Yes |
| File Size | 14 KB | 18 KB |
| Performance | Fast | Fast (~1ms overhead) |
- For learning or minimal dependencies: Use V1 (Basic)
- For production or type safety: Use V2 (Pydantic)
V1 (Basic):
curl -o ~/.claude/hooks/security_hook_v1.py \\
https://raw.githubusercontent.com/hah23255/claude-hooks-public/main/v1-basic/security_hook.py
chmod +x ~/.claude/hooks/security_hook_v1.pyV2 (Pydantic):
# Install Pydantic first
pip install pydantic
# Download files
curl -o ~/.claude/hooks/schemas.py \\
https://raw.githubusercontent.com/hah23255/claude-hooks-public/main/v2-pydantic/schemas.py
curl -o ~/.claude/hooks/security_hook_v2.py \\
https://raw.githubusercontent.com/hah23255/claude-hooks-public/main/v2-pydantic/security_hook.py
chmod +x ~/.claude/hooks/security_hook_v2.pycurl -o ~/.claude/security-config.json \\
https://raw.githubusercontent.com/hah23255/claude-hooks-public/main/config/security-config.jsonEdit ~/.claude/security-config.json:
{
"version": "1.0",
"fail_closed": true,
"restricted_paths": [
"~/.ssh",
"~/.gnupg",
"~/.claude/hooks"
],
"allowed_paths": [
"/YOUR/PROJECT/PATH",
"/YOUR/WORK/DIRECTORY"
],
"dangerous_bash_patterns": [
"rm\\\\s+-rf",
"dd\\\\s+if=",
"curl.*\\\\|.*bash"
],
"rate_limits": {
"Read": 100,
"Write": 50,
"Bash": 20,
"Glob": 50,
"Grep": 50,
"WebFetch": 30,
"Task": 10
}
}Add to ~/.claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "python3 ~/.claude/hooks/security_hook_v2.py"
}
]
}
]
}
}Note: Use security_hook_v1.py if using V1 (Basic)
# Test with valid input
echo '{"session_id":"test","transcript_path":"/tmp/t.json","cwd":"/tmp","permission_mode":"default","hook_event_name":"PreToolUse","tool_name":"Read","tool_input":{"file_path":"/tmp/test.txt"}}' | \\
python3 ~/.claude/hooks/security_hook_v2.py
# Should exit with code 0 (allowed)
echo $?-
GAP 1: Read Tool Monitoring
- All Read operations logged
- Path access validation
- Restricted path blocking
-
GAP 2: Bash Command Scanning
- Pattern-based detection
- 18+ dangerous command patterns
- Configurable blocklist
-
GAP 3: Enumeration Detection
- Glob/Grep monitoring
- Sensitive pattern detection
- Audit logging
-
GAP 4: Fail-Closed Policy
- Missing config → block all
- Invalid config → block all
- Error during validation → block all
-
GAP 7: Network Monitoring
- Domain whitelist enforcement
- URL logging
- Configurable allowed domains
-
GAP 9: Rate Limiting
- Per-tool rate limits
- Time-window based (60s)
- Persistent state tracking
-
GAP 10: Agent Monitoring
- Task tool monitoring
- Suspicious prompt detection
- Agent spawn logging
-
GAP 12: Symbolic Link Resolution
- os.path.realpath() resolution
- Symlink detection logging
- Prevents path traversal via symlinks
-
GAP 13: Edit Controls
- Critical file protection
- Enhanced Edit validation
- SSH/GPG file blocking
-
GAP 15: Config Protection
- Settings.json protected
- Security-config.json protected
- Hook files protected
# Allowed path
{"tool_name": "Read", "tool_input": {"file_path": "/allowed/project/file.py"}}
# → ALLOWED
# Restricted path
{"tool_name": "Write", "tool_input": {"file_path": "~/.ssh/id_rsa"}}
# → BLOCKED: Access to restricted path: ~/.ssh# Safe command
{"tool_name": "Bash", "tool_input": {"command": "ls -la"}}
# → ALLOWED
# Dangerous command
{"tool_name": "Bash", "tool_input": {"command": "rm -rf /"}}
# → BLOCKED: Dangerous command pattern: rm\\s+-rf# Within limit
100 Read operations in 60s
# → All ALLOWED
# Exceeded limit
101st Read operation in 60s
# → BLOCKED: Rate limit exceeded: 101/100 calls in 60sAll operations are logged to ~/.claude/audit.jsonl:
{
"timestamp": "2025-10-30T17:38:21.123456",
"tool": "Read",
"input_summary": "{'file_path': '/project/file.py'}",
"decision": "ALLOWED",
"reason": "",
"details": {
"file_path": "/project/file.py",
"resolved_path": "/storage/emulated/0/project/file.py"
},
"validation": {
"pydantic": true,
"version": "2.0.0"
}
}# View recent operations
tail -20 ~/.claude/audit.jsonl | jq .
# Count blocked operations
jq 'select(.decision=="BLOCKED")' ~/.claude/audit.jsonl | wc -l
# View validation failures (V2 only)
jq 'select(.decision=="VALIDATION_FAILED")' ~/.claude/audit.jsonl| Field | Type | Required | Description |
|---|---|---|---|
version |
string | Yes | Config version |
fail_closed |
boolean | Yes | Fail-closed on errors |
restricted_paths |
array | Yes | Paths to block |
allowed_paths |
array | Yes | Paths to allow (whitelist) |
dangerous_bash_patterns |
array | Yes | Regex patterns to block |
allowed_domains |
array | No | Network domain whitelist |
rate_limits |
object | Yes | Per-tool rate limits |
sensitive_patterns |
array | No | Enumeration detection |
Symptom: Operations not being monitored
Fix:
# 1. Verify hook exists and is executable
ls -la ~/.claude/hooks/security_hook_v2.py
chmod +x ~/.claude/hooks/security_hook_v2.py
# 2. Verify settings.json configuration
jq '.hooks.PreToolUse' ~/.claude/settings.json
# 3. Test hook manually
echo '{"session_id":"test","transcript_path":"/tmp/t.json","cwd":"/tmp","permission_mode":"default","hook_event_name":"PreToolUse","tool_name":"Read","tool_input":{}}' | \\
python3 ~/.claude/hooks/security_hook_v2.pySymptom: "CRITICAL: Security configuration not found"
Fix:
# Download template
curl -o ~/.claude/security-config.json \\
https://raw.githubusercontent.com/hah23255/claude-hooks-public/main/config/security-config.json
# Or create manually
cat > ~/.claude/security-config.json <<EOF
{
"version": "1.0",
"fail_closed": true,
"restricted_paths": ["~/.ssh"],
"allowed_paths": ["/your/project"],
"dangerous_bash_patterns": ["rm\\\\s+-rf"],
"rate_limits": {"Read": 100}
}
EOFSymptom: "ModuleNotFoundError: No module named 'pydantic'"
Fix:
# Install Pydantic
pip install pydantic
# Or use V1 (Basic) instead
cp ~/.claude/hooks/security_hook_v1.py ~/.claude/hooks/security_hook.py# Clone repository
git clone https://github.com/hah23255/claude-hooks-public.git
cd claude-hooks-public
# Run V1 tests
bash tests/test_v1.sh
# Run V2 tests (requires Pydantic)
pip install pydantic
bash tests/test_v2.shSee DEVELOPMENT.md for guide on creating custom hooks.
# 1. Install Pydantic
pip install pydantic
# 2. Download schemas
curl -o ~/.claude/hooks/schemas.py \\
https://raw.githubusercontent.com/hah23255/claude-hooks-public/main/v2-pydantic/schemas.py
# 3. Backup current hook
cp ~/.claude/hooks/security_hook_v1.py ~/.claude/hooks/security_hook_v1.backup
# 4. Download V2 hook
curl -o ~/.claude/hooks/security_hook_v2.py \\
https://raw.githubusercontent.com/hah23255/claude-hooks-public/main/v2-pydantic/security_hook.py
# 5. Update settings.json to use v2
# Change: security_hook_v1.py → security_hook_v2.py
# 6. Test
echo '{"session_id":"test","transcript_path":"/tmp/t.json","cwd":"/tmp","permission_mode":"default","hook_event_name":"PreToolUse","tool_name":"Read","tool_input":{}}' | \\
python3 ~/.claude/hooks/security_hook_v2.pyContributions welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new features
- Submit a pull request
MIT License - See LICENSE file
- Issues: GitHub Issues
- Documentation: See docs/ directory
- Examples: See examples/ directory
- Built for Claude Code CLI
- Uses Pydantic for validation (V2)
- Inspired by security best practices from OWASP and NIST
See CHANGELOG.md for version history.
Version: 2.0.0 Last Updated: 2025-10-30 Maintained: Active