Richtlinien & Verfahren (Phase 1): Import, Rendering-Engine, Bibliothek, Coverage

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>
This commit is contained in:
2026-07-07 13:35:59 +02:00
co-authored by Claude Opus 4.8
parent 2706424c08
commit 89b0e3a5a5
52 changed files with 7627 additions and 1 deletions
+51
View File
@@ -0,0 +1,51 @@
# -*- 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")