Add validators module

This commit is contained in:
2026-01-29 21:26:30 +00:00
parent e4876502eb
commit 12f14232d9

View File

@@ -1,64 +1,35 @@
"""Pattern-based dangerous command detection.""" """Pattern-based dangerous command detection."""
from typing import List, Optional from typing import List, Optional
from ..models import Rule, Finding from ..models import Rule, Finding
class PatternValidator: class PatternValidator:
"""Validates shell scripts for dangerous command patterns."""
DEFAULT_PATTERNS = [ DEFAULT_PATTERNS = [
Rule( Rule(
id="DANGER001", id="DANGER001", name="Dangerous rm -rf with variable",
name="Dangerous rm -rf with variable", pattern=r"rm\s+-rf?\s+\$\w+", severity="critical",
pattern=r"rm\s+-rf?\s+\$\w+",
severity="critical",
message="Dangerous deletion command with variable: rm -rf with variables can delete unexpected content", message="Dangerous deletion command with variable: rm -rf with variables can delete unexpected content",
suggestion="Avoid using variables with rm -rf. Use absolute paths or verify the variable content first.", suggestion="Avoid using variables with rm -rf. Use absolute paths or verify the variable content first.",
), ),
Rule( Rule(
id="DANGER002", id="DANGER002", name="Eval with variable",
name="Eval with variable", pattern=r"eval\s+\$\w+", severity="critical",
pattern=r"eval\s+\$\w+",
severity="critical",
message="eval with variable can lead to command injection", message="eval with variable can lead to command injection",
suggestion="Avoid eval with variables. Use safer alternatives like explicit function calls.", suggestion="Avoid eval with variables. Use safer alternatives like explicit function calls.",
), ),
Rule( Rule(
id="DANGER003", id="DANGER003", name="Sudo without full path",
name="Sudo without full path", pattern=r"sudo\s+(rm|cp|mv|chmod|chown|useradd|userdel)\s", severity="critical",
pattern=r"sudo\s+(rm|cp|mv|chmod|chown|useradd|userdel)\s",
severity="critical",
message="sudo command without full path can be exploited", message="sudo command without full path can be exploited",
suggestion="Use full path with sudo, e.g., sudo /bin/rm instead of sudo rm", suggestion="Use full path with sudo, e.g., sudo /bin/rm instead of sudo rm",
), ),
Rule(
id="DANGER004",
name="Unrestricted pipe to shell",
pattern=r"\|\s*(bash|sh|zsh|fish)\s*(-[a-z]+)?\s*\$\w+",
severity="critical",
message="Pipe to shell with variable can execute arbitrary commands",
suggestion="Avoid piping variables directly to shell interpreters",
),
Rule(
id="DANGER005",
name="Backtick command injection",
pattern=r"`\$\w+`",
severity="critical",
message="Backtick substitution with variable can lead to command injection",
suggestion="Use $() instead of backticks and validate the variable content",
),
] ]
def __init__(self, custom_rules: Optional[List[Rule]] = None): def __init__(self, custom_rules=None):
"""Initialize the pattern validator with optional custom rules."""
self.rules = self.DEFAULT_PATTERNS.copy() self.rules = self.DEFAULT_PATTERNS.copy()
if custom_rules: if custom_rules:
self.rules.extend(custom_rules) self.rules.extend(custom_rules)
def validate(self, content: str, line_number: Optional[int] = None) -> List[Finding]: def validate(self, content, line_number=None):
"""Validate content for dangerous patterns."""
findings = [] findings = []
for rule in self.rules: for rule in self.rules:
if not rule.enabled: if not rule.enabled:
@@ -68,17 +39,3 @@ class PatternValidator:
finding = Finding.from_match(rule, match, line_number, content) finding = Finding.from_match(rule, match, line_number, content)
findings.append(finding) findings.append(finding)
return findings return findings
def validate_lines(self, lines: List[str]) -> List[Finding]:
"""Validate multiple lines, tracking line numbers."""
findings = []
for line_number, line in enumerate(lines, start=1):
line_findings = self.validate(line, line_number)
for finding in line_findings:
finding.context = line
findings.extend(line_findings)
return findings
def check(self, command: str) -> List[Finding]:
"""Check a single command for dangerous patterns."""
return self.validate(command)