#!/usr/bin/env python3 """Fail closed when PostgreSQL migration authority escapes its canonical module.""" from __future__ import annotations import argparse import re import sys from pathlib import Path MAX_FILE_BYTES = 2 * 1024 * 1024 DDL = re.compile( r"(?is)\b(?:create|alter|drop|truncate|comment\s+on|grant|revoke)\s+" r"(?:(?:or\s+replace|unique|temporary|temp|unlogged)\s+)*" r"(?:table|index|schema|view|materialized\s+view|type|sequence|function|procedure|" r"trigger|extension|domain|policy|role)\b" ) RUNNER = re.compile( r"(?is)pg_advisory_(?:xact_)?lock|sqlx\s*::\s*migrate|_sqlx_migrations|" r"include_(?:str|bytes)!\s*\([^)]*\.sql|" r"(?:insert\s+into|update|delete\s+from)\s+__crank_(?:core_|mcp_|ext_)?migrations" ) def canonical(path: Path, root: Path) -> bool: relative = path.relative_to(root).as_posix() return relative == "crates/crank-registry/src/migrations.rs" or relative.startswith( "crates/crank-registry/src/migrations/" ) def candidates(root: Path) -> list[Path]: result: list[Path] = [] for base_name in ("apps", "crates"): base = root / base_name if not base.exists(): continue for path in base.rglob("*"): if path.suffix not in {".rs", ".sql"}: continue if "crank-test-support" in path.relative_to(root).parts: continue if path.is_symlink() or not path.is_file(): raise ValueError("production Rust/SQL path must be a regular non-symlink file") if "/tests/" in f"/{path.relative_to(root).as_posix()}/": continue result.append(path) return sorted(result) def main() -> int: parser = argparse.ArgumentParser() parser.add_argument("--root", type=Path, default=Path(".")) args = parser.parse_args() root = args.root.resolve() try: files = candidates(root) violations: list[str] = [] for path in files: if canonical(path, root): continue data = path.read_bytes() if len(data) > MAX_FILE_BYTES: raise ValueError("production Rust/SQL file exceeds scanner limit") text = data.decode("utf-8") if DDL.search(text) or RUNNER.search(text): violations.append(path.relative_to(root).as_posix()) except (OSError, UnicodeDecodeError, ValueError) as error: print(f"migration boundary check failed: {error}", file=sys.stderr) return 1 if violations: for path in violations: print(f"migration boundary violation: {path}", file=sys.stderr) return 1 print(f"migration boundary check passed: files={len(files)}") return 0 if __name__ == "__main__": raise SystemExit(main())