"""Allowlist of read-only scripts runnable from the /scripts page (#4). Single source of truth shared by BOTH: - the host bridge (``court_fetch_service.server`` — the ENFORCER that actually launches the process), and - the container API (``web/app.py`` — display-only: tells the UI which rows get a "הרץ" button). Each entry maps a script's basename to the EXACT, fixed argument list it runs with. **Read-only / audit scripts only**, with a safe fixed argv and **no user-supplied arguments** — never ``--apply``/``--force``. The system is single-user/internal, so this allowlist is a footgun-guard, not an auth boundary; enforcement lives on the trusted host side. Adding an entry is a security decision: verify the script is read-only (no DB writes, no destructive side effects) with the given argv before listing it. stdlib-only on purpose, so the lightweight host bridge can import it cheaply. """ from __future__ import annotations _REPO = "/home/chaim/legal-ai" _PYTHON = f"{_REPO}/mcp-server/.venv/bin/python" # basename → argv tail (script path relative to the repo root, then fixed flags). # Verified read-only 2026-06-17. `audit_corpus_integrity` runs with --no-notify # so it stays report-only (no notification side-effect) when run from the dashboard. SCRIPT_RUN_ALLOWLIST: dict[str, list[str]] = { "leak_guard.py": ["scripts/leak_guard.py"], "check_undefined_names.py": ["scripts/check_undefined_names.py"], "storage_leak_tripwire.py": ["scripts/storage_leak_tripwire.py"], "audit_training_corpus.py": ["scripts/audit_training_corpus.py"], "audit_corpus_integrity.py": ["scripts/audit_corpus_integrity.py", "--no-notify"], } def runnable_names() -> list[str]: """Sorted basenames the UI may show a "הרץ" button for (display-only).""" return sorted(SCRIPT_RUN_ALLOWLIST) def build_argv(name: str) -> list[str] | None: """Full argv (python + absolute script path + fixed flags) for an allowlisted script, or ``None`` when *name* is not allowlisted. Arguments are taken ONLY from the allowlist — anything the caller passes is ignored.""" tail = SCRIPT_RUN_ALLOWLIST.get(name) if tail is None: return None return [_PYTHON, f"{_REPO}/{tail[0]}", *tail[1:]]