"""Safety checking for generated commands.""" import re from typing import Tuple, List class SafetyChecker: """Check if commands are safe to execute.""" DANGEROUS_PATTERNS: List[str] = [ r"rm\s+-rf\s+[/]", r"rm\s+-rf\s+[/a-zA-Z0-9_/.-]+\s*$", r"dd\s+if=", r"mkfs", r"format", r"shred", r":\(\)\s*:\|\s*:\s*&\s*;", r"chmod\s+777", r"chown\s+[a-zA-Z0-9]+:[a-zA-Z0-9]+\s+[/]", r"echo\s+.*>\s*/dev/sd[a-z]", r">\s*/dev/[sh]d[a-z]", r"mv\s+.*\s+[/dev]/null", r"cp\s+.*\s+[/dev]/null", r"cat\s+[/dev/][ur]andom", ] SAFE_PATTERNS: List[str] = [ r"^ls", r"^cat\s+[a-zA-Z0-9_./-]", r"^grep", r"^find", r"^echo", r"^pwd", r"^cd\s", r"^git", r"^npm\s+install", r"^pip\s+install", r"^docker\s+(ps|images|logs)", r"^ps\s", r"^top", r"^htop", r"^free", r"^df\s", r"^du\s", r"^head\s", r"^tail\s", r"^less\s", r"^more\s", r"^wc\s", r"^sort\s", r"^uniq\s", r"^cut\s", r"^sed\s", r"^awk\s", r"^jq\s", r"^tree", r"^which\s", r"^type\s", r"^alias\s", r"^unalias", r"^history", r"^export\s", r"^unset\s", r"^env\s", r"^printenv", ] def __init__(self): """Initialize safety checker with compiled patterns.""" self._dangerous_regexes = [ re.compile(pattern, re.IGNORECASE) for pattern in self.DANGEROUS_PATTERNS ] self._safe_regexes = [ re.compile(pattern, re.IGNORECASE) for pattern in self.SAFE_PATTERNS ] def check(self, command: str) -> Tuple[bool, str]: """Check if a command is safe to execute. Args: command: The shell command to check. Returns: Tuple of (is_safe, warning_message). """ if not command or not command.strip(): return False, "Empty command" normalized = command.strip() for regex in self._dangerous_regexes: if regex.search(normalized): return False, f"Dangerous pattern detected: {regex.pattern}" for regex in self._safe_regexes: if regex.search(normalized): return True, "Command appears safe" if normalized.startswith("#") or normalized.startswith("//"): return False, "Comment-only command" return True, "Command safety unclear - confirmation recommended" def is_safe(self, command: str) -> bool: """Quick check if command is definitely safe. Args: command: The shell command to check. Returns: True if command is in safe patterns. """ is_safe, _ = self.check(command) return is_safe def is_dangerous(self, command: str) -> bool: """Quick check if command is definitely dangerous. Args: command: The shell command to check. Returns: True if command matches dangerous patterns. """ is_safe, _ = self.check(command) return not is_safe def get_risk_level(self, command: str) -> str: """Get risk level for a command. Args: command: The shell command to check. Returns: Risk level: "safe", "caution", or "dangerous". """ is_safe, warning = self.check(command) if "dangerous" in warning.lower(): return "dangerous" elif "unclear" in warning.lower(): return "caution" else: return "safe"