import c5 from "../../seed/scoping/c5-controls.json"; /** * Reifegrad-Engine (Story A7-2) — regelbasierte, nachvollziehbare Herleitung eines * Reifegrad-Vorschlags 0–3 je Control aus dem Belegstatus (C5 §2), der Zielreifegrad * (C5 §3) und die „offenen Punkte" (C5 §4). Reine, seiteneffektfreie Logik; die * Belegstände werden vom Aufrufer aus Richtlinien-/Verfahrensdokumenten, Inventar und * Risikoregister abgeleitet. Der Vorschlag ist nicht bindend — die Bestätigung/ * Überschreibung durch den Bearbeiter ist Pflicht (persistiert als ControlAssessment). * * Grundlage: VDA-ISA-Reifegradmodell 0–3, Fachcontent C5. Belegtabelle je Control: * `seed/scoping/c5-controls.json` (Generierung: scripts/build-c5-controls.ts). */ /** * Neutrale, framework-UNABHÄNGIGE Control-Spezifikation (Belegbasis, Feindesign §1/§2): * welche Richtlinie/Verfahren zuständig sind und ob Asset-/Risiko-Verknüpfung gefordert * ist. Eine Tatsache über die Organisation, keine Frage der Norm — daher von TISAX (C5) * UND ISO (mapping-iso) befüllt und von der GETEILTEN Belegauflösung (loadEvidenceResolver) * konsumiert. `loadEvidenceResolver` darf `ControlSpec` NICHT um ein Framework erweitern. */ export interface ControlSpec { control: string; title: string; /** Zuständige Richtlinie(n): L00 | R01..R14 (TISAX) bzw. R.. (ISO). Leer = keine dedizierte. */ policy: string[]; /** Geforderte Verfahren: VA-xx. Leer = operative Steuerung in der Richtlinie verankert. */ verfahren: string[]; /** Control setzt auf Inventar auf (Asset-Verknüpfung gefordert). */ needsAsset: boolean; /** Control setzt auf Risikoregister auf (Risiko-Verknüpfung gefordert). */ needsRisk: boolean; } /** TISAX-Belegtabelle (C5 §1) = neutrale ControlSpec + Standard-Zielreifegrad (VDA-ISA-Zusatz). */ export interface C5ControlSpec extends ControlSpec { /** Standard-Zielreifegrad laut Tabelle (informativ; effektiv aus Scope, s. targetMaturity). */ target: number; } /** Vollständige C5-Belegtabelle (45 IS-Controls). */ export const C5_CONTROLS = c5 as C5ControlSpec[]; const C5_BY_ID = new Map(C5_CONTROLS.map((c) => [c.control, c])); /** Generischer Fallback-Spec für Controls ohne §5-Zeile (Proto 8.x / Datenschutz 9.x, C5 §6). */ export function specForControl(control: string): C5ControlSpec { return ( C5_BY_ID.get(control) ?? { control, title: control, policy: [], verfahren: [], needsAsset: false, needsRisk: false, target: 2, // §6-Gruppen: Standard-Zielreifegrad 2 (Ausnahmen HOCH → 3, s. targetMaturity). } ); } /** Validierungsstatus eines Belegs (C5 §1): fehlt → verknüpft (unvalidiert) → validiert. */ export type EvidenceStatus = "fehlt" | "verknuepft" | "validiert"; /** Aus den Fachmodulen abgeleiteter Ist-Belegstand eines Controls. */ export interface ControlEvidence { /** Status der (aggregierten) zuständigen Richtlinie(n). */ policy: EvidenceStatus; /** Status der geforderten Verfahren (aggregiert, „schwächster" gewinnt). */ verfahren: EvidenceStatus; /** Asset-Verknüpfung vorhanden (nur relevant, wenn spec.needsAsset). */ assetLinked: boolean; /** Risiko-Verknüpfung vorhanden (nur relevant, wenn spec.needsRisk). */ riskLinked: boolean; /** * Nur für Controls OHNE dediziertes Verfahren (C5 §2, Sonderfall): validierte, * dokumentierte Umsetzungsregelung/Konfiguration (N-basisch), die für Grad 2 an die * Stelle von (V) tritt. Für Controls mit Verfahren irrelevant. */ implementationRule?: boolean; /** * Operativer Wirksamkeitsnachweis (N) validiert UND aktuell (Aktualitätsregel ≤12 M., * C5 §1) — wiederkehrender Review/Audit/Test. Grad-3-Trigger. Frühe Onboarding-Phase * i. d. R. false → Deckelung auf Grad 2. */ operationalProof: boolean; /** Nachweis widerspricht Richtlinie / offene Findings → Deckelung auf max. 1 (C5 §2). */ contradiction?: boolean; /** Control enthält nur SOLL (MUSS definitionsgemäß erfüllt) → Bewertung startet bei 1 (C5 §2). */ onlySoll?: boolean; } export type MaturityRule = "R0" | "R1a" | "R1b" | "R1c" | "R2" | "R3" | "CAP"; export interface MaturitySuggestion { value: 0 | 1 | 2 | 3; rule: MaturityRule; /** Kurzbegründung (nachvollziehbar, an Belege gekoppelt). */ reason: string; } /** * Reifegrad-Vorschlag nach C5 §2 (generische Belegkonstellations-Tabelle) inkl. * Sonderfälle: Controls ohne Verfahren (V-Slot durch validierte Umsetzungsregelung * ersetzt), nur-SOLL-Controls (Start bei 1) und Deckelung bei Widerspruch/Findings. */ export function suggestMaturity(spec: C5ControlSpec, ev: ControlEvidence): MaturitySuggestion { // Deckelung: Nachweis widerspricht Richtlinie / offene Findings → max. Grad 1 (C5 §2). if (ev.contradiction) { return { value: 1, rule: "CAP", reason: "Nachweis widerspricht Richtlinie bzw. offene Findings — Vorschlag gedeckelt (max. 1)." }; } const requiresProcedure = spec.verfahren.length > 0; // Bei Controls ohne dediziertes Verfahren tritt für Grad 2 eine validierte, dokumentierte // Umsetzungsregelung (implementationRule) an die Stelle von (V); der wiederkehrende // Wirksamkeitsnachweis (operationalProof) bleibt eigenständiger Grad-3-Trigger. const procedureOk = requiresProcedure ? ev.verfahren === "validiert" : ev.implementationRule === true; const assetOk = !spec.needsAsset || ev.assetLinked; const riskOk = !spec.needsRisk || ev.riskLinked; const policyValidated = ev.policy === "validiert"; const policyLinked = ev.policy !== "fehlt"; // R3: R2 erfüllt UND mindestens ein validierter, aktueller operativer Wirksamkeitsnachweis. if (policyValidated && procedureOk && assetOk && riskOk && ev.operationalProof) { return { value: 3, rule: "R3", reason: "Richtlinie und Verfahren validiert, Verknüpfungen vorhanden und aktueller Wirksamkeitsnachweis." }; } // R2: Richtlinie validiert UND alle Verfahren validiert UND (falls einschlägig) Asset-/Risiko-Verknüpfung. if (policyValidated && procedureOk && assetOk && riskOk) { return { value: 2, rule: "R2", reason: "Vorgehen dokumentiert und gesteuert; Richtlinie und Verfahren validiert." }; } // R1b: Richtlinie validiert, aber ein gefordertes Verfahren fehlt/unvalidiert (oder Verknüpfung fehlt). if (policyValidated) { return { value: 1, rule: "R1b", reason: "Richtlinie validiert, aber gefordertes Verfahren bzw. Verknüpfung fehlt oder ist unvalidiert." }; } // R1a: zuständige Richtlinie verknüpft, aber nicht validiert. if (policyLinked) { return { value: 1, rule: "R1a", reason: "Regelungsabsicht erkennbar (Richtlinie verknüpft), aber nicht validiert/gelebt." }; } // R1c: nur operativer Nachweis/Umsetzung ohne validierte Richtlinie. if (ev.operationalProof || ev.implementationRule || ev.verfahren !== "fehlt") { return { value: 1, rule: "R1c", reason: "Tätigkeit wird durchgeführt, aber nicht dokumentiert gesteuert (keine validierte Richtlinie)." }; } // nur-SOLL: MUSS definitionsgemäß erfüllt → Bewertung startet bei 1. if (ev.onlySoll) { return { value: 1, rule: "R1c", reason: "Nur-SOLL-Control: MUSS-Ebene definitionsgemäß erfüllt (Start bei Grad 1)." }; } // R0: keine Belege bzw. nur unvalidierte Fragmente ohne zuständige Richtlinie. return { value: 0, rule: "R0", reason: "Kein belastbarer Nachweis einer geregelten Vorgehensweise." }; } export interface ScopeForTarget { /** Assessment-Level aus TenantSettings (A2-1): AL2 oder AL3. */ level: "AL2" | "AL3"; flags: Record; } /** * Zielreifegrad je Control (C5 §3): Standard 3; Absenkung auf 2 nur bei reinem MUSS-/AL2-Scope * (kein SOLL). HOCH/SEHR-HOCH-Schutzbedarf erzwingt Grad 3 (keine Absenkung). */ export function targetMaturity(scope: ScopeForTarget): 2 | 3 { const high = scope.flags.FLAG_HIGH_PROTECTION || scope.flags.FLAG_VERY_HIGH_PROTECTION; if (scope.level === "AL3" || high || scope.flags.FLAG_INCLUDE_SHOULD) return 3; // reiner AL2-/MUSS-Scope (kein SOLL, kein erhöhter Schutzbedarf): Absenkung auf Grad 2. return 2; } /** Ein offener Punkt (Gap) nach C5 §4 mit fehlender Belegklasse und empfohlener Maßnahme. */ export interface OpenPoint { control: string; kind: "reifegradluecke" | "richtlinie" | "verfahren" | "nachweis" | "verknuepfung" | "widerspruch"; actual: number; target: number; /** Fehlende Belegklasse / Kurzbeschreibung. */ missing: string; /** Empfohlene Maßnahme (Aufgabentitel). */ action: string; } /** * Leitet die offenen Punkte (Gap → Aufgabe) nach C5 §4 ab: Reifegradlücke, fehlender * Pflichtbeleg (Richtlinie/Verfahren), fehlender/veralteter Nachweis bei Zielgrad 3, * fehlende Asset-/Risiko-Verknüpfung und Widerspruch/Findings. */ export function openPoints(spec: C5ControlSpec, ev: ControlEvidence, suggestion: MaturitySuggestion, target: number): OpenPoint[] { const out: OpenPoint[] = []; const base = { control: spec.control, actual: suggestion.value, target }; if (ev.contradiction) { out.push({ ...base, kind: "widerspruch", missing: "Nachweis mit Findings/Widerspruch", action: `Abweichung in Control ${spec.control} beheben` }); } if (ev.policy !== "validiert" && spec.policy.length > 0) { out.push({ ...base, kind: "richtlinie", missing: `Richtlinie ${spec.policy.join("/")}`, action: `Richtlinie ${spec.policy.join("/")} erstellen/validieren (Control ${spec.control})` }); } if (spec.verfahren.length > 0 && ev.verfahren !== "validiert") { out.push({ ...base, kind: "verfahren", missing: `Verfahren ${spec.verfahren.join("/")}`, action: `Verfahren ${spec.verfahren.join("/")} erstellen/validieren (Control ${spec.control})` }); } if (spec.needsAsset && !ev.assetLinked) { out.push({ ...base, kind: "verknuepfung", missing: "Asset-Verknüpfung", action: `Asset-/Inventarverknüpfung für Control ${spec.control} herstellen` }); } if (spec.needsRisk && !ev.riskLinked) { out.push({ ...base, kind: "verknuepfung", missing: "Risiko-Verknüpfung", action: `Risikoverknüpfung für Control ${spec.control} herstellen` }); } if (target >= 3 && !ev.operationalProof && !ev.contradiction) { out.push({ ...base, kind: "nachweis", missing: "operativer Wirksamkeitsnachweis (aktuell)", action: `Wirksamkeitsnachweis (Review/Audit/Test) für Control ${spec.control} führen` }); } // Reifegradlücke zuletzt, nur wenn nicht schon durch einen konkreten Pflichtbeleg abgedeckt. if (suggestion.value < target && out.length === 0) { out.push({ ...base, kind: "reifegradluecke", missing: `Reifegrad ${suggestion.value} < Ziel ${target}`, action: `Belege/Umsetzung für Control ${spec.control} bis Zielgrad ${target} anheben` }); } return out; } export const MATURITY_LABEL: Record<0 | 1 | 2 | 3, string> = { 0: "Grad 0 — nicht geregelt", 1: "Grad 1 — informell", 2: "Grad 2 — dokumentiert/gesteuert", 3: "Grad 3 — wirksam/integriert", };