#!/usr/bin/env python3 import json import sys import re from pathlib import Path def is_dangerous_rm_command(command): """ Comprehensive detection of dangerous rm commands. Matches various forms of rm -rf and similar destructive patterns. """ # Normalize command by removing extra spaces and converting to lowercase normalized = ' '.join(command.lower().split()) # Pattern 1: Standard rm -rf variations patterns = [ r'\brm\s+.*-[a-z]*r[a-z]*f', # rm -rf, rm -fr, rm -Rf, etc. r'\brm\s+.*-[a-z]*f[a-z]*r', # rm -fr variations r'\brm\s+--recursive\s+--force', # rm --recursive --force r'\brm\s+--force\s+--recursive', # rm --force --recursive r'\brm\s+-r\s+.*-f', # rm -r ... -f r'\brm\s+-f\s+.*-r', # rm -f ... -r ] # Check for dangerous patterns for pattern in patterns: if re.search(pattern, normalized): return True # Pattern 2: Check for rm with recursive flag targeting dangerous paths dangerous_paths = [ r'/', # Root directory r'/\*', # Root with wildcard r'~', # Home directory r'~/', # Home directory path r'\$HOME', # Home environment variable r'\.\.', # Parent directory references r'\*', # Wildcards in general rm -rf context r'\.', # Current directory r'\.\s*$', # Current directory at end of command ] if re.search(r'\brm\s+.*-[a-z]*r', normalized): # If rm has recursive flag for path in dangerous_paths: if re.search(path, normalized): return True return False def is_env_file_access(tool_name, tool_input): """ Check if any tool is trying to access .env files containing sensitive data. """ if tool_name in ['Read', 'Edit', 'MultiEdit', 'Write', 'Bash']: # Check file paths for file-based tools if tool_name in ['Read', 'Edit', 'MultiEdit', 'Write']: file_path = tool_input.get('file_path', '') if '.env' in file_path and not file_path.endswith('.env.sample'): return True # Check bash commands for .env file access elif tool_name == 'Bash': command = tool_input.get('command', '') # Pattern to detect .env file access (but allow .env.sample) env_patterns = [ r'\b\.env\b(?!\.sample)', # .env but not .env.sample r'cat\s+.*\.env\b(?!\.sample)', # cat .env r'echo\s+.*>\s*\.env\b(?!\.sample)', # echo > .env r'touch\s+.*\.env\b(?!\.sample)', # touch .env r'cp\s+.*\.env\b(?!\.sample)', # cp .env r'mv\s+.*\.env\b(?!\.sample)', # mv .env ] for pattern in env_patterns: if re.search(pattern, command): return True return False def main(): try: # Read JSON input from stdin input_data = json.load(sys.stdin) tool_name = input_data.get('tool_name', '') tool_input = input_data.get('tool_input', {}) # Check for .env file access (blocks access to sensitive environment files) if is_env_file_access(tool_name, tool_input): print("BLOCKED: Access to .env files containing sensitive data is prohibited", file=sys.stderr) print("Use .env.sample for template files instead", file=sys.stderr) sys.exit(2) # Exit code 2 blocks tool call and shows error to Claude # Check for dangerous rm -rf commands if tool_name == 'Bash': command = tool_input.get('command', '') # Block rm -rf commands with comprehensive pattern matching if is_dangerous_rm_command(command): print("BLOCKED: Dangerous rm command detected and prevented", file=sys.stderr) sys.exit(2) # Exit code 2 blocks tool call and shows error to Claude # Ensure log directory exists log_dir = Path.cwd() / '.claude' / 'logs' log_dir.mkdir(parents=True, exist_ok=True) log_path = log_dir / 'pre_tool_use.json' # Read existing log data or initialize empty list if log_path.exists(): with open(log_path, 'r') as f: try: log_data = json.load(f) except (json.JSONDecodeError, ValueError): log_data = [] else: log_data = [] # Append new data log_data.append(input_data) # Write back to file with formatting with open(log_path, 'w') as f: json.dump(log_data, f, indent=2) sys.exit(0) except json.JSONDecodeError: # Gracefully handle JSON decode errors sys.exit(0) except Exception: # Handle any other errors gracefully sys.exit(0) if __name__ == '__main__': main()