Static safety analysis of a shell script. Hard violations → safe=False (script is blocked). Warnings → safe=True (script is allowed but user should review).
(self, script: str)
| 293 | |
| 294 | def validate_script(self, script: str) -> ScriptValidationResult: |
| 295 | """ |
| 296 | Static safety analysis of a shell script. |
| 297 | |
| 298 | Hard violations → safe=False (script is blocked). |
| 299 | Warnings → safe=True (script is allowed but user should review). |
| 300 | """ |
| 301 | violations: list[str] = [] |
| 302 | warnings: list[str] = [] |
| 303 | |
| 304 | for pattern, description in BANNED_SCRIPT_PATTERNS: |
| 305 | if re.search(pattern, script, re.IGNORECASE | re.MULTILINE): |
| 306 | violations.append(f"Banned pattern detected: {description}") |
| 307 | |
| 308 | # Warnings (soft checks) |
| 309 | if "sudo" in script and "NOPASSWD" in script: |
| 310 | warnings.append("Script modifies sudoers configuration") |
| 311 | if re.search(r">\s*/etc/", script): |
| 312 | warnings.append("Script overwrites a file in /etc/ — review carefully") |
| 313 | if "iptables" in script or "nftables" in script: |
| 314 | warnings.append("Script modifies firewall rules — verify connectivity is maintained") |
| 315 | if re.search(r"systemctl\s+restart", script): |
| 316 | warnings.append("Script restarts a service — ensure this is non-critical or off-hours") |
| 317 | if not re.search(r"(?:\.bak|\.orig|cp\s+.*\s+\S+)", script): |
| 318 | warnings.append("No backup step detected — consider adding 'cp file file.bak' before modifications") |
| 319 | |
| 320 | return ScriptValidationResult( |
| 321 | safe=len(violations) == 0, |
| 322 | violations=violations, |
| 323 | warnings=warnings, |
| 324 | ) |
| 325 | |
| 326 | # ── Audit log ────────────────────────────────────────────────────────────── |
| 327 | |
| 328 | def get_audit_log(self) -> list[dict]: |
no test coverage detected