Add shellgen UI and safety modules
This commit is contained in:
146
app/shellgen/safety/checker.py
Normal file
146
app/shellgen/safety/checker.py
Normal file
@@ -0,0 +1,146 @@
|
||||
"""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"
|
||||
Reference in New Issue
Block a user