"""Scope-Audit: Adressaten-Prüfung der Obligation-Registry (Review-Stage-Werkzeug). Prinzip (etabliert im CRA-Vuln-Cut, CSIRT/ENISA out_of_scope): die Registry modelliert **Hersteller-Pflichten**. Bestimmungen, die an BEHÖRDEN / notifizierte Stellen / Mitgliedstaaten adressiert sind (Marktüberwachung, Sanktionen, Anforderungen an Konformitätsbewertungsstellen), sind Enforcement-/Institutions-Recht → out_of_scope-KANDIDATEN. WICHTIG (False-Positive-Abgrenzung): eine Hersteller-Pflicht, Etwas AN eine Behörde zu MELDEN (z.B. `exploited_vuln_reporting_authorities`, applicability=products_with_digital_elements) ist IN-SCOPE — Adressat der Pflicht = Hersteller, Behörde = nur Empfänger. Der Audit key't daher auf `applicability` (Adressat), NICHT auf Behörden-Nennung im Namen. Deterministisch, kein LLM. Reklassifizierung = Owner-/User-Entscheidung (dieser Audit FLAGGT nur). Für jeden künftigen Regulierungs-Cut mitlaufen lassen. """ from __future__ import annotations import glob import json # applicability-Präfixe, die einen NICHT-Hersteller-Adressaten bezeichnen NON_MANUFACTURER_DOMAINS = { "domain:authority", "domain:notified_body", "domain:market_surveillance", "domain:member_state", "domain:commission", } def main() -> None: findings = [] total = 0 for f in sorted(glob.glob("obligations/cra*.json")): d = json.load(open(f, encoding="utf-8")) for o in d.get("obligations", []): total += 1 appl = (o.get("applicability") or "").strip() if appl in NON_MANUFACTURER_DOMAINS: findings.append({ "file": f.split("/")[-1], "id": o.get("id") or o.get("obligation_id"), "name": o.get("name"), "tier": o.get("tier"), "applicability": appl, "subdomain": o.get("subdomain"), "member_count": o.get("member_count"), "reason": "Adressat ist Behörde/notifizierte Stelle/Mitgliedstaat, nicht Hersteller", "precedent": "CRA-Vuln-Cut: CSIRT/ENISA out_of_scope (Adressat != Hersteller)", "recommendation": "out_of_scope ODER eigene Kategorie 'institutional/enforcement'", }) out = { "audit": "obligation scope audit (Adressat: Hersteller vs Behörde/notified_body)", "principle": "Registry modelliert Hersteller-Pflichten; Enforcement/Institutions-Recht = out_of_scope-Kandidat", "false_positive_guard": "Melde-AN-Behörde-Pflichten (applicability=domain:products…) bleiben IN-SCOPE", "obligations_scanned": total, "out_of_scope_candidates": findings, "decision_owner": "User/Registry-Owner — Audit FLAGGT nur, reklassifiziert nicht", } json.dump(out, open("obligations/scope_audit_findings.json", "w", encoding="utf-8"), ensure_ascii=False, indent=1) print(f"gescannt {total} Obligations | out_of_scope-Kandidaten: {len(findings)}") for fnd in findings: print(f" [{fnd['file']}] {fnd['id']:32} tier={fnd['tier']} appl={fnd['applicability']}") if __name__ == "__main__": main()