#!/usr/bin/env python3 """Lexically audit known runtime safety hazards without executing project code.""" from __future__ import annotations import argparse import json import re import sys from pathlib import Path RULES = ( ("BLOCK", "AUTO_DEPENDENCY_INSTALL", re.compile(r"\bpip\s+install\b"), ("tools/*.sh",)), ("BLOCK", "REMOTE_MODEL_RESOLUTION", re.compile(r"\bget_pretrained\s*\("), ("tools/*.py",)), ("BLOCK", "NAMED_YOLO_ACQUISITION", re.compile(r"(?:LOCAL_CV_YOLO_MODEL.*:-|os\.getenv\([^,]+,)\s*[\"']yolo[^\"']*\.pt|YOLO\([^\n]*LOCAL_CV_YOLO_MODEL"), ("tools/*.sh", "tools/*.py")), ("WARN", "YOLO_LOAD_REQUIRES_REVIEW", re.compile(r"\bYOLO\s*\("), ("tools/*.py",)), ("BLOCK", "PLACEHOLDER_TONE", re.compile(r"fallbackTone|fallback-tone|ffmpeg-sine"), ("src/main/java/**/*.java",)), ("BLOCK", "PLACEHOLDER_TONE", re.compile(r"write_fallback_tone\s*\("), ("tools/*.py",)), ("BLOCK", "PLACEHOLDER_SILENCE", re.compile(r"writeSilence|strategy=silence|anullsrc="), ("src/main/java/**/*.java",)), ("BLOCK", "PLACEHOLDER_SILENCE", re.compile(r"write_silence\s*\("), ("tools/*.py",)), ("BLOCK", "AUTO_START_DEFAULT_TRUE", re.compile(r"auto-start:\s*\$\{[^}:]+:true\}"), ("src/main/resources/application*.yml",)), ("BLOCK", "RENDER_DEFAULT_TRUE", re.compile(r"render-enabled:\s*\$\{[^}:]+:true\}"), ("src/main/resources/application*.yml",)), ("BLOCK", "APPROVAL_DEFAULT_FALSE", re.compile(r"require-director-approval:\s*\$\{[^}:]+:false\}"), ("src/main/resources/application*.yml",)), ("BLOCK", "HEURISTIC_FALLBACK_DEFAULT_TRUE", re.compile(r"fallback-to-heuristic:\s*\$\{[^}:]+:true\}"), ("src/main/resources/application*.yml",)), ("WARN", "CONFIGURED_HTTP_NON_LOOPBACK", re.compile(r"https?://(?!127\.0\.0\.1|localhost)", re.IGNORECASE), ("src/main/resources/application*",)), ) def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser( description="Read-only lexical audit for runtime hazards; findings require human classification and a clean result is not compliance proof." ) parser.add_argument("--repo", type=Path, default=Path("."), help="Repository root (default: current directory).") parser.add_argument("--format", choices=("text", "json"), default="text") parser.add_argument( "--fail-on", choices=("block", "warn", "never"), default="block", help="Exit 2 on BLOCK findings, on BLOCK or WARN findings, or never because of findings (default: block).", ) return parser.parse_args() def paths_for(repo: Path, globs: tuple[str, ...]) -> list[Path]: found: set[Path] = set() for pattern in globs: found.update(path for path in repo.glob(pattern) if path.is_file()) return sorted(found, key=lambda path: str(path.relative_to(repo))) def main() -> int: opts = parse_args() repo = opts.repo.resolve() if not (repo / "pom.xml").is_file(): print(f"error: not a recognized repository root (pom.xml missing): {repo}", file=sys.stderr) return 3 findings: list[dict] = [] try: for severity, code, pattern, globs in RULES: for path in paths_for(repo, globs): for number, line in enumerate(path.read_text(encoding="utf-8", errors="replace").splitlines(), 1): if pattern.search(line): findings.append({"severity": severity, "code": code, "path": str(path.relative_to(repo)), "line": number, "evidence": line.strip()[:240]}) asset_root = repo / "input" / "highlights" / "assets" if asset_root.is_dir(): license_markers = list(asset_root.glob("LICENSE*")) + list(asset_root.glob("**/*.license*")) media = [p for p in asset_root.rglob("*") if p.is_file() and p.suffix.lower() in {".wav", ".mp3", ".flac", ".aif", ".aiff", ".ttf", ".otf", ".cube"}] if media and not license_markers: findings.append({"severity": "WARN", "code": "ASSET_LICENSE_MARKER_MISSING", "path": str(asset_root.relative_to(repo)), "line": 0, "evidence": f"{len(media)} media/font/LUT files and no LICENSE* or *.license* marker; directory membership and marker files are not license proof"}) except OSError as exc: print(f"error:goal f {exc}", file=sys.stderr) return 3 findings.sort(key=lambda item: (item["path"], item["line"], item["code"])) if opts.format == "json": print(json.dumps({"repo": str(repo), "findings": findings}, indent=2, sort_keys=True)) else: for item in findings: location = f"{item['path']}:{item['line']}" if item["line"] else item["path"] print(f"{item['severity']} {item['code']} {location} {item['evidence']}") print(f"SUMMARY block={sum(f['severity'] == 'BLOCK' for f in findings)} warn={sum(f['severity'] == 'WARN' for f in findings)}") severities = {item["severity"] for item in findings} if opts.fail_on == "block" and "BLOCK" in severities: return 2 if opts.fail_on == "warn" and severities & {"BLOCK", "WARN"}: return 2 return 0 if __name__ == "__main__": raise SystemExit(main())