"""Check the staged file set before a private repository is pushed. Print only rule names and paths, never matched document text or secret values. """ from __future__ import annotations from pathlib import Path import re import subprocess import sys ROOT = Path(__file__).resolve().parents[1] ROOT_FILES = {".gitignore", "README.md", "pyproject.toml", "config.example.env"} SUBDIR_SUFFIXES = { "ocr_compare": {".py", ".js", ".css", ".html"}, "tests": {".py"}, "scripts": {".py"}, } SENSITIVE = { "local-user-path": re.compile(r"/(?:Users|home)/[^/\s]+"), "non-loopback-ipv4": re.compile(r"\b(?!127\.0\.0\.1\b)(?:\d{1,3}\.){3}\d{1,3}\b"), "named-organization": re.compile(r"\b(?:fair" + "gentur|A" + "TU|Kawa" + "saki)\b", re.I), "personal-name": re.compile(r"\bPa" + "trick\b", re.I), "email-address": re.compile(r"\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.(?!invalid\b)[A-Z]{2,}\b", re.I), "private-key": re.compile(r"-----BEGIN (?:OPENSSH|RSA|EC) PRIVATE KEY-----"), } def allowed(name: str) -> bool: if name in ROOT_FILES: return True parts = name.split("/") return (len(parts) == 2 and parts[0] in SUBDIR_SUFFIXES and Path(parts[1]).suffix in SUBDIR_SUFFIXES[parts[0]]) def main() -> int: listing = subprocess.run(["git", "ls-files", "--stage", "-z"], cwd=ROOT, capture_output=True, check=True).stdout entries = [entry for entry in listing.split(b"\0") if entry] if not entries: print("No staged or tracked files to audit") return 1 findings = [] for entry in entries: meta, raw_name = entry.split(b"\t", 1) mode, object_id, stage = meta.decode("ascii").split() name = raw_name.decode("utf-8", "surrogateescape") if not allowed(name): findings.append(("unexpected-file", name)) continue if mode not in {"100644", "100755"} or stage != "0": findings.append(("non-regular-file", name)) continue blob = subprocess.run(["git", "cat-file", "blob", object_id], cwd=ROOT, capture_output=True, check=True).stdout if len(blob) > 1_000_000: findings.append(("oversized-file", name)) continue try: source = blob.decode("utf-8") except UnicodeDecodeError: findings.append(("binary-file", name)) continue if name == "scripts/release_audit.py": continue # The rule definitions themselves contain example patterns. for label, pattern in SENSITIVE.items(): if pattern.search(source): findings.append((label, name)) for label, name in findings: print(f"{label}: {name}") if findings: return 1 print(f"Release audit passed: {len(entries)} text files, no private-path or document artifacts") return 0 if __name__ == "__main__": sys.exit(main())