#!/usr/bin/env python3 """ Command Validator Hook - PreToolUse[Bash] hook Validates bash commands using shadow learner insights """ import sys import json import re import os from pathlib import Path # Add lib directory to path sys.path.insert(0, str(Path(__file__).parent.parent / "lib")) from shadow_learner import ShadowLearner from models import ValidationResult class CommandValidator: """Validates bash commands for safety and success probability""" def __init__(self): self.shadow_learner = ShadowLearner() # Dangerous command patterns self.dangerous_patterns = [ r'rm\s+-rf\s+/', # Delete root r'mkfs\.', # Format filesystem r'dd\s+if=.*of=/dev/', # Overwrite devices r':(){ :|:& };:', # Fork bomb r'curl.*\|\s*bash', # Pipe to shell r'wget.*\|\s*sh', # Pipe to shell r';.*rm\s+-rf', # Command chaining with rm r'&&.*rm\s+-rf', # Command chaining with rm ] self.suspicious_patterns = [ r'sudo\s+rm', # Sudo with rm r'chmod\s+777', # Overly permissive r'/etc/passwd', # System files r'/etc/shadow', # System files r'nc.*-l.*-p', # Netcat listener ] def validate_command_safety(self, command: str) -> ValidationResult: """Comprehensive command safety validation""" # Normalize command for analysis normalized = command.lower().strip() # Check for dangerous patterns for pattern in self.dangerous_patterns: if re.search(pattern, normalized): return ValidationResult( allowed=False, reason=f"Dangerous command pattern detected", severity="critical" ) # Check for suspicious patterns for pattern in self.suspicious_patterns: if re.search(pattern, normalized): return ValidationResult( allowed=True, # Allow but warn reason=f"Suspicious command pattern detected", severity="warning" ) return ValidationResult(allowed=True, reason="Command appears safe") def validate_with_shadow_learner(self, command: str) -> ValidationResult: """Use shadow learner to predict command success""" try: prediction = self.shadow_learner.predict_command_outcome(command) if not prediction["likely_success"] and prediction["confidence"] > 0.8: suggestions = prediction.get("suggestions", []) suggestion_text = f" Try: {suggestions[0]}" if suggestions else "" return ValidationResult( allowed=False, reason=f"Command likely to fail (confidence: {prediction['confidence']:.0%}){suggestion_text}", severity="medium", suggestions=suggestions ) elif prediction["warnings"]: return ValidationResult( allowed=True, reason=prediction["warnings"][0], severity="warning", suggestions=prediction.get("suggestions", []) ) except Exception: # If shadow learner fails, don't block pass return ValidationResult(allowed=True, reason="No issues detected") def validate_command(self, command: str) -> ValidationResult: """Main validation entry point""" # Safety validation (blocking) safety_result = self.validate_command_safety(command) if not safety_result.allowed: return safety_result # Shadow learner validation (predictive) prediction_result = self.validate_with_shadow_learner(command) # Return most significant result if prediction_result.severity in ["high", "critical"]: return prediction_result elif safety_result.severity in ["warning", "medium"]: return safety_result else: return prediction_result def main(): """Main hook entry point""" try: # Read input from Claude Code input_data = json.loads(sys.stdin.read()) # Extract command from parameters tool = input_data.get("tool", "") parameters = input_data.get("parameters", {}) command = parameters.get("command", "") if tool != "Bash" or not command: # Not a bash command, allow it response = {"allow": True, "message": "Not a bash command"} print(json.dumps(response)) sys.exit(0) # Validate command validator = CommandValidator() result = validator.validate_command(command) if not result.allowed: # Block the command response = { "allow": False, "message": f"⛔ Command blocked: {result.reason}" } print(json.dumps(response)) sys.exit(1) # Exit code 1 = block operation elif result.severity in ["warning", "medium"]: # Allow with warning warning_emoji = "⚠️" if result.severity == "warning" else "🚨" message = f"{warning_emoji} {result.reason}" if result.suggestions: message += f"\n💡 Suggestion: {result.suggestions[0]}" response = { "allow": True, "message": message } print(json.dumps(response)) sys.exit(0) else: # Allow without warning response = {"allow": True, "message": "Command validated"} print(json.dumps(response)) sys.exit(0) except Exception as e: # Never block on validation errors - always allow operation response = { "allow": True, "message": f"Validation error: {str(e)}" } print(json.dumps(response)) sys.exit(0) if __name__ == "__main__": main()