Fundament des VDA-ISA-2027-Richtlinienmoduls (Spec §1–9):
- Datenmodell: PolicyDocument, PolicyRequirement, PolicyVariable (Variablen +
Feature-Flags), PolicyBaselineParam, PolicyEvidence — inkl. RLS + Tenant-Guard
- Seed-Importer (import-policies.ts): liest die echten .md-Dateien (15 Richtlinien
L00/R01–R14 + 11 Verfahren), mapping.json (120 Anforderungen/46 Controls),
variables.schema.json (53 Variablen/Flags), Technische-Sicherheits-Baseline
(31 BL-Parameter) und Nachweisregister; idempotent pro Mandant
- 6 im Vorlagenpaket beschädigte Variablen-Tokens (VA-08/09/10/12/13) repariert
(dokumentiert im README des Übergabepakets)
- Rendering-Engine (policy-render.ts, Handlebars + marked): verschachtelte
{{#if FLAG}}, {{VARIABLE}}, {{LINK:…}}-Deeplinks, Hidden-Anker + BL-Referenzen
im Lesemodus entfernt (Wert bleibt), zentral verwaltete Abschnitte unterdrückt,
wiederholtes „Umsetzung bei <Org>" reduziert (§7a); lenienter Fallback +
Residue-Check über alle Flag-Kombinationen (analog _verify.py)
- UI: Bibliothek mit Typ-Chips/KPIs, Lesemodus-Popup (einklappbare Info-Tabelle,
Control-Chips, Richtlinie↔Verfahren-Verlinkung), Coverage-Matrix
(Control → Richtlinie → MUSS/SOLL → Verfahren → Anforderungs-IDs)
- Nav-Punkt „Richtlinien" aktiviert; de/en-Übersetzungen
Verifiziert: Import 28 Dokumente/120 Anforderungen; Rendering rückstandsfrei
über alle Flag-Kombinationen; Bibliothek, Lesemodus (R08 nested flags), Coverage
im Browser.
Später (Phase 2+): Bearbeiten/Freigabe-Workflow mit Versionierung, verwaltete
Tabellen (Krypto-/Risiko-/Klassifizierungsregister), Anwender-Handbuch,
DOCX/PDF-Export, Word-Upload, KI-Wizard, zentrale Baseline-/Variablen-Einstellseite.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
52 lines
2.3 KiB
Python
52 lines
2.3 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Korrekter rekursiver {{#if}}-Renderer (wie echtes Handlebars) zur Verifikation.
|
|
import re, glob, json, os
|
|
BASE=os.path.dirname(__file__)
|
|
schema=json.load(open(os.path.join(BASE,"variables.schema.json")))
|
|
allvars=set(schema["properties"].keys())
|
|
|
|
def make_ctx(inc, extra=None):
|
|
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["FLAG_INCLUDE_SHOULD"]=inc
|
|
c["ORG_NAME"]="Muster GmbH"; c["TOOL_NAME"]="ISMS-Portal"; c["REVIEW_CYCLE"]="jährlich"
|
|
if extra: c.update(extra)
|
|
return c
|
|
|
|
def render_if(t,c):
|
|
# rekursiv innerste Bloecke zuerst
|
|
pat=re.compile(r"\{\{#if (\w+)\}\}((?:(?!\{\{#if )(?!\{\{/if\}\}).)*?)\{\{/if\}\}",re.S)
|
|
prev=None
|
|
while prev!=t:
|
|
prev=t
|
|
t=pat.sub(lambda m:(m.group(2) if c.get(m.group(1)) else ""),t)
|
|
return t
|
|
|
|
def render(t,c):
|
|
t=render_if(t,c)
|
|
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
|
|
|
|
files=sorted(glob.glob(os.path.join(BASE,"richtlinien/*.md")))+[os.path.join(BASE,"Nachweisregister_zentral.md"),os.path.join(BASE,"ISA-Mapping-Matrix.md")]
|
|
problems=0
|
|
for inc in [True,False]:
|
|
c=make_ctx(inc)
|
|
for fp in files:
|
|
r=render(open(fp,encoding="utf-8").read(),c)
|
|
bad=re.findall(r"\{\{.*?\}\}",r); miss=sorted(set(re.findall(r"«MISS:\w+»",r)))
|
|
if bad or miss:
|
|
problems+=1; print("!",os.path.basename(fp),"inc=%s"%inc,"open:",bad[:4],"miss:",miss[:4])
|
|
d=json.load(open(os.path.join(BASE,"mapping.json")))
|
|
fa=set()
|
|
for fp in glob.glob(os.path.join(BASE,"richtlinien/*.md")):
|
|
for m in re.findall(r"<!-- (REQ|IMPL) ([0-9.\-A-Za-z]+) -->",open(fp,encoding="utf-8").read()): fa.add(m[0]+" "+m[1])
|
|
ma=set()
|
|
for a in d["anforderungen"]: ma.add(a["req_anchor"]); ma.add(a["impl_anchor"])
|
|
leftover_hb=any("{{" in a["requirement"] or "{{" in a["implementation"] for a in d["anforderungen"])
|
|
print("Render-Probleme:",problems)
|
|
print("mapping-Anker ohne Datei:",sorted(ma-fa))
|
|
print("Datei-Anker ohne mapping:",sorted(fa-ma))
|
|
print("Handlebars-Reste in mapping-Text:",leftover_hb)
|
|
print("OK" if problems==0 and not (ma-fa) and not (fa-ma) and not leftover_hb else "PRUEFEN")
|