Files
craftvia/src/lib/maturity.ts
T
msolarczekandClaude Opus 5 c8e6f30a27
CI / build-and-check (push) Canceled after 0s
CI / audit (push) Canceled after 0s
CI / sbom (push) Canceled after 0s
Basis: Certvia dev@a48c5fb als Fundament für Craftvia
Unveränderter Stand von certvia/dev (a48c5fb) plus Craftvia-Spezifikation
und Brandbook unter docs/craftvia/. ISMS-Module werden im Folgecommit entfernt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 11:05:39 +02:00

218 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<string, boolean>;
}
/**
* 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",
};