99 lines
4.6 KiB
Python
Executable File
99 lines
4.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Dangerous Command Blocker Hook
|
|
Multi-level security system for blocking dangerous shell commands
|
|
"""
|
|
|
|
import json
|
|
import sys
|
|
import re
|
|
|
|
# Load command from stdin
|
|
data = json.load(sys.stdin)
|
|
cmd = data.get('tool_input', {}).get('command', '')
|
|
|
|
# === LEVEL 1: CATASTROPHIC COMMANDS (ALWAYS BLOCK) ===
|
|
catastrophic_patterns = [
|
|
(r'\brm\s+.*\s+/\s*$', 'rm on root directory'),
|
|
(r'\brm\s+.*\s+~\s*$', 'rm on home directory'),
|
|
(r'\brm\s+.*\s+\*\s*$', 'rm with star wildcard'),
|
|
(r'\brm\s+-[rfRF]*[rfRF]+.*\*', 'rm -rf with wildcards'),
|
|
(r'\b(dd\s+if=|dd\s+of=/dev)', 'dd disk operations'),
|
|
(r'\b(mkfs\.|mkswap\s|fdisk\s)', 'filesystem formatting'),
|
|
(r'\b:(\(\))?\s*\{\s*:\s*\|\s*:\s*&\s*\}', 'fork bomb'),
|
|
(r'>\s*/dev/sd[a-z]', 'direct disk write'),
|
|
(r'\bchmod\s+(-R\s+)?777\s+/', 'chmod 777 on root'),
|
|
(r'\bchown\s+(-R\s+)?.*\s+/$', 'chown on root directory'),
|
|
]
|
|
|
|
for pattern, desc in catastrophic_patterns:
|
|
if re.search(pattern, cmd, re.IGNORECASE):
|
|
print(f'❌ BLOCKED: Catastrophic command detected!', file=sys.stderr)
|
|
print(f'', file=sys.stderr)
|
|
print(f'Reason: {desc}', file=sys.stderr)
|
|
print(f'Command: {cmd[:100]}', file=sys.stderr)
|
|
print(f'', file=sys.stderr)
|
|
print(f'This command could cause IRREVERSIBLE system damage or data loss.', file=sys.stderr)
|
|
print(f'', file=sys.stderr)
|
|
print(f'Safety tips:', file=sys.stderr)
|
|
print(f' • Never use rm -rf with /, ~, or * wildcards', file=sys.stderr)
|
|
print(f' • Avoid recursive operations on system directories', file=sys.stderr)
|
|
print(f' • Use specific file paths instead of wildcards', file=sys.stderr)
|
|
print(f' • For cleanup, target specific directories: rm -rf /tmp/myproject', file=sys.stderr)
|
|
sys.exit(2)
|
|
|
|
# === LEVEL 2: CRITICAL PATH PROTECTION ===
|
|
critical_paths = [
|
|
(r'\b(rm|mv)\s+(-[rfRF]+\s+)?\.claude(/|$|\s)', 'Claude Code configuration'),
|
|
(r'\b(rm|mv)\s+(-[rfRF]+\s+)?\.git(/|$|\s)', 'Git repository'),
|
|
(r'\b(rm|mv)\s+(-[rfRF]+\s+)?node_modules(/|$|\s)', 'Node.js dependencies'),
|
|
(r'\b(rm|mv)\s+(-[rfRF]+\s+)?[^\s]*\.env\b', 'Environment variables'),
|
|
(r'\b(rm|mv)\s+(-[rfRF]+\s+)?[^\s]*package\.json\b', 'Package manifest'),
|
|
(r'\b(rm|mv)\s+(-[rfRF]+\s+)?[^\s]*package-lock\.json\b', 'Lock file'),
|
|
(r'\b(rm|mv)\s+(-[rfRF]+\s+)?[^\s]*yarn\.lock\b', 'Yarn lock file'),
|
|
(r'\b(rm|mv)\s+(-[rfRF]+\s+)?[^\s]*Cargo\.toml\b', 'Rust manifest'),
|
|
(r'\b(rm|mv)\s+(-[rfRF]+\s+)?[^\s]*go\.mod\b', 'Go module file'),
|
|
(r'\b(rm|mv)\s+(-[rfRF]+\s+)?[^\s]*requirements\.txt\b', 'Python dependencies'),
|
|
(r'\b(rm|mv)\s+(-[rfRF]+\s+)?[^\s]*Gemfile(\.lock)?\b', 'Ruby dependencies'),
|
|
(r'\b(rm|mv)\s+(-[rfRF]+\s+)?[^\s]*composer\.json\b', 'PHP dependencies'),
|
|
]
|
|
|
|
for pattern, desc in critical_paths:
|
|
if re.search(pattern, cmd, re.IGNORECASE):
|
|
print(f'🛑 BLOCKED: Critical path protection activated!', file=sys.stderr)
|
|
print(f'', file=sys.stderr)
|
|
print(f'Protected resource: {desc}', file=sys.stderr)
|
|
print(f'Command: {cmd[:100]}', file=sys.stderr)
|
|
print(f'', file=sys.stderr)
|
|
print(f'This path contains critical project files that should not be deleted accidentally.', file=sys.stderr)
|
|
print(f'', file=sys.stderr)
|
|
print(f'If you really need to modify this:', file=sys.stderr)
|
|
print(f' 1. Disable the hook temporarily in .claude/hooks.json', file=sys.stderr)
|
|
print(f' 2. Execute the command manually in your terminal', file=sys.stderr)
|
|
print(f' 3. Or modify specific files instead of using rm/mv on entire directories', file=sys.stderr)
|
|
sys.exit(2)
|
|
|
|
# === LEVEL 3: SUSPICIOUS PATTERNS (WARNING) ===
|
|
suspicious_patterns = [
|
|
(r'\brm\s+.*\s+&&', 'chained rm commands'),
|
|
(r'\brm\s+[^\s/]*\*', 'rm with wildcards'),
|
|
(r'\bfind\s+.*-delete', 'find -delete operation'),
|
|
(r'\bxargs\s+.*\brm', 'xargs with rm'),
|
|
]
|
|
|
|
for pattern, desc in suspicious_patterns:
|
|
if re.search(pattern, cmd, re.IGNORECASE):
|
|
print(f'⚠️ WARNING: Suspicious command pattern detected!', file=sys.stderr)
|
|
print(f'', file=sys.stderr)
|
|
print(f'Pattern: {desc}', file=sys.stderr)
|
|
print(f'Command: {cmd[:100]}', file=sys.stderr)
|
|
print(f'', file=sys.stderr)
|
|
print(f'This command uses patterns that could accidentally delete more than intended.', file=sys.stderr)
|
|
print(f'Consider reviewing the command carefully before execution.', file=sys.stderr)
|
|
print(f'', file=sys.stderr)
|
|
# Exit 0 to allow but with warning
|
|
sys.exit(0)
|
|
|
|
# Command is safe
|
|
sys.exit(0)
|