#!/usr/bin/env python3 """ Heuristik-Check: Router mit FastAPI-Routen und Auth sollten bei mandantenrelevanten APIs get_tenant_context nutzen — siehe ACCESS_LAYER_AND_GOVERNANCE_PLAN.md. Lauf aus Repo-Root: python backend/scripts/check_access_layer_hints.py Mit Fehler-Exit bei Verstößen (z. B. CI): set ACCESS_LAYER_STRICT=1 # Windows: set ACCESS_LAYER_STRICT=1 """ from __future__ import annotations import os import sys from pathlib import Path # Router, die bewusst keinen TenantContext verwenden (global / Auth-only / Admin-Tools). # Neuen Eintrag nur mit kurzer Begründung im Commit/Audit ergänzen. EXEMPT_ROUTERS: frozenset[str] = frozenset( { "auth.py", "admin_users.py", "platform_media_storage.py", "catalogs.py", "skills.py", "maturity_models.py", "matrix_stack_bundle.py", "import_wiki.py", "import_wiki_admin.py", } ) def main() -> int: strict = os.environ.get("ACCESS_LAYER_STRICT", "").strip().lower() in ("1", "true", "yes") root = Path(__file__).resolve().parents[1] routers = root / "routers" if not routers.is_dir(): print("check_access_layer_hints: routers/ nicht gefunden", file=sys.stderr) return 1 issues: list[str] = [] for path in sorted(routers.glob("*.py")): name = path.name text = path.read_text(encoding="utf-8") if "@router." not in text: continue if name in EXEMPT_ROUTERS: continue if "get_tenant_context" in text: continue if "require_auth" not in text: continue issues.append( f" {path.relative_to(root.parent)}: Routen + require_auth, " f"aber kein get_tenant_context — TenantContext ergänzen oder in EXEMPT_ROUTERS eintragen " f"(mit Begründung / Audit)." ) if not issues: print("check_access_layer_hints: OK (keine auffälligen Router außerhalb EXEMPT).") return 0 print("check_access_layer_hints: mögliche ACCESS_LAYER-Abweichungen:\n", file=sys.stderr) for line in issues: print(line, file=sys.stderr) print( "\nHinweis: Heuristik — false positives möglich. " "Bei echter Ausnahme Datei zu EXEMPT_ROUTERS hinzufügen.", file=sys.stderr, ) return 1 if strict else 0 if __name__ == "__main__": raise SystemExit(main())