# -*- coding: utf-8 -*- """ Prueft die ISO/IEC-27001-Sicht der gemeinsamen Dokumentenbibliothek. Gegenstueck zu _verify.py (das die TISAX-Sicht prueft). Geprueft wird: 1. Vollstaendigkeit — 27 Klauseln (Kap. 4-10) + 93 Anhang-A-Controls, keine Luecke, keine Dublette 2. Anker — jeder req_anchor/impl_anchor aus mapping-iso.json existiert in einer Datei 3. Umsetzungstext — jeder impl_anchor loest auf einen nicht leeren Umsetzungsblock auf 4. Rendering — ISO-Sicht (FLAG_FW_ISO27001=True, FLAG_FW_TISAX=False) bleibt ohne offene Platzhalter; ebenso die TISAX-Sicht (Regressionsschutz) 5. Verfahren — jede im Mapping genannte VA existiert und listet die ID im FULFILLS-Header Aufruf: python3 _verify_iso.py """ import re, glob, json, os, sys BASE = os.path.dirname(os.path.abspath(__file__)) LANG = sys.argv[sys.argv.index("--lang") + 1] if "--lang" in sys.argv else "de" # Dieselbe Prüfung gilt für beide Sprachfassungen; nur das Verzeichnis wechselt. PKG = BASE if LANG == "de" else os.path.normpath(os.path.join(BASE, "..", "isms-vorlagenpaket-v2-en")) schema = json.load(open(os.path.join(PKG, "variables.schema.json"), encoding="utf-8")) allvars = set(schema["properties"].keys()) mapping = json.load(open(os.path.join(PKG, "mapping-iso.json"), encoding="utf-8")) reqs = mapping["anforderungen"] problems = [] def ctx(framework): c = {k: (True if k.startswith("FLAG_") else k) for k in allvars} for k in ["FLAG_OT_USED", "FLAG_DEV_INHOUSE", "FLAG_CRYPTO_PKI", "FLAG_CUSTOMER_SYSTEMS"]: c[k] = False c["ORG_NAME"] = "Muster GmbH"; c["TOOL_NAME"] = "ISMS-Portal"; c["REVIEW_CYCLE"] = "jährlich" c["FLAG_FW_ISO27001"] = framework == "ISO" c["FLAG_FW_TISAX"] = framework == "TISAX" return c def render(t, c): pat_if = re.compile(r"\{\{#if (\w+)\}\}((?:(?!\{\{#if )(?!\{\{#unless )(?!\{\{/if\}\}).)*?)\{\{/if\}\}", re.S) pat_un = re.compile(r"\{\{#unless (\w+)\}\}((?:(?!\{\{#unless )(?!\{\{/unless\}\}).)*?)\{\{/unless\}\}", re.S) prev = None while prev != t: prev = t t = pat_if.sub(lambda m: (m.group(2) if c.get(m.group(1)) else ""), t) t = pat_un.sub(lambda m: ("" if c.get(m.group(1)) else m.group(2)), t) t = re.sub(r"\{\{LINK:[^}]+\}\}", "[LINK]", t) t = re.sub(r"\{\{(\w+)\}\}", lambda m: str(c[m.group(1)]) if m.group(1) in c else "«MISS:%s»" % m.group(1), t) return t # ── 1. Vollstaendigkeit ─────────────────────────────────────────────────────── clauses = [r["control"] for r in reqs if r["kind"] == "clause"] controls = [r["control"] for r in reqs if r["kind"] != "clause"] EXPECT = {"5": 37, "6": 8, "7": 14, "8": 34} if len(clauses) != 27: problems.append("Klausel-Anforderungen: %d statt 27" % len(clauses)) if len(controls) != 93: problems.append("Anhang-A-Controls: %d statt 93" % len(controls)) for grp, n in EXPECT.items(): have = sorted(int(c.split(".")[2]) for c in controls if c.split(".")[1] == grp) miss = [i for i in range(1, n + 1) if i not in have] if miss: problems.append("A.%s fehlt: %s" % (grp, miss)) if len(have) != len(set(have)): problems.append("A.%s enthaelt Dubletten" % grp) # ── 2./3. Anker und Umsetzungstext ──────────────────────────────────────────── impl_text, file_anchors = {}, set() for fp in glob.glob(os.path.join(PKG, "richtlinien", "*.md")): lines = open(fp, encoding="utf-8").read().split("\n") for i, ln in enumerate(lines): m = re.match(r"^$", ln.strip()) if not m: continue file_anchors.add("%s %s" % (m.group(1), m.group(2))) if m.group(1) == "IMPL": buf = [] for l in lines[i + 1:]: t = l.strip() if t == "" or t.startswith("", open(fp, encoding="utf-8").read()) va_fulfills[code] = [x.strip() for x in m.group(1).split(",")] if m else [] for r in reqs: for va in r["verfahren"]: if va not in va_fulfills: problems.append("%s: Verfahren %s existiert nicht" % (r["control"], va)) elif r["id"] not in va_fulfills[va]: problems.append("%s: %s fehlt im FULFILLS-Header von %s" % (r["control"], r["id"], va)) # ── Ergebnis ────────────────────────────────────────────────────────────────── pol = {} for r in reqs: pol.setdefault(r["policy"], []).append(r["control"]) print("Sprache: %s" % LANG.upper()) print("Anforderungen: %d (%d Klauseln + %d Controls)" % (len(reqs), len(clauses), len(controls))) print("Richtlinien: %s" % ", ".join("%s=%d" % (k, len(v)) for k, v in sorted(pol.items()))) print("Umsetzungsblöcke: %d referenziert, davon %d gemeinsam mit VDA ISA" % (len({r["impl_anchor"] for r in reqs}), len({r["impl_anchor"] for r in reqs if not r["impl_anchor"].startswith("IMPL ISO-")}))) print("Verfahren verknüpft:%d Anforderungen" % sum(1 for r in reqs if r["verfahren"])) print("Befunde: %d" % len(problems)) for p in problems[:25]: print(" !", p) print("OK" if not problems else "PRUEFEN") sys.exit(0 if not problems else 1)