Add validators module

This commit is contained in:
2026-01-29 21:22:05 +00:00
parent 5b955769ae
commit e7f9f68dd4

View File

@@ -0,0 +1,84 @@
"""Pattern-based dangerous command detection."""
from typing import List, Optional
from ..models import Rule, Finding
class PatternValidator:
"""Validates shell scripts for dangerous command patterns."""
DEFAULT_PATTERNS = [
Rule(
id="DANGER001",
name="Dangerous rm -rf with variable",
pattern=r"rm\s+-rf?\s+\$\w+",
severity="critical",
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.",
),
Rule(
id="DANGER002",
name="Eval with variable",
pattern=r"eval\s+\$\w+",
severity="critical",
message="eval with variable can lead to command injection",
suggestion="Avoid eval with variables. Use safer alternatives like explicit function calls.",
),
Rule(
id="DANGER003",
name="Sudo without full path",
pattern=r"sudo\s+(rm|cp|mv|chmod|chown|useradd|userdel)\s",
severity="critical",
message="sudo command without full path can be exploited",
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):
"""Initialize the pattern validator with optional custom rules."""
self.rules = self.DEFAULT_PATTERNS.copy()
if custom_rules:
self.rules.extend(custom_rules)
def validate(self, content: str, line_number: Optional[int] = None) -> List[Finding]:
"""Validate content for dangerous patterns."""
findings = []
for rule in self.rules:
if not rule.enabled:
continue
matches = rule._compiled_pattern.finditer(content) if rule._compiled_pattern else []
for match in matches:
finding = Finding.from_match(rule, match, line_number, content)
findings.append(finding)
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)