Add validators module
This commit is contained in:
84
src/validators/patterns.py
Normal file
84
src/validators/patterns.py
Normal 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)
|
||||||
Reference in New Issue
Block a user