claude-hooks/hooks/command_validator.py
Ryan Malloy 162ca67098 Initial commit: Claude Code Hooks with Diátaxis documentation
 Features:
- 🧠 Shadow learner that builds intelligence from command patterns
- 🛡️ Smart command validation with safety checks
- 💾 Automatic context monitoring and backup system
- 🔄 Session continuity across Claude restarts

📚 Documentation:
- Complete Diátaxis-organized documentation
- Learning-oriented tutorial for getting started
- Task-oriented how-to guides for specific problems
- Information-oriented reference for quick lookup
- Understanding-oriented explanations of architecture

🚀 Installation:
- One-command installation script
- Bootstrap prompt for installation via Claude
- Cross-platform compatibility
- Comprehensive testing suite

🎯 Ready for real-world use and community feedback!

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-19 18:25:34 -06:00

184 lines
6.4 KiB
Python
Executable File

#!/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()