Files
craftvia/scripts/build-c1-scope.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

64 lines
2.4 KiB
TypeScript

// Generator (Story A2-2): parst die C1-Scoping-Tabelle (Fachcontent) in die
// maschinenlesbare Scope-Datengrundlage `seed/scoping/c1-scope.json`.
// Lauf: npx tsx scripts/build-c1-scope.ts
//
// Spalten der C1-Tabelle: Control | Anforderungs-ID | Typ | AL2 | AL3 | Prüfziel | Scope-Bedingung(Flag)
// `condition` wird normalisiert: exaktes Feature-Flag (FLAG_*) → Flag-Name, sonst null
// ("immer im Scope" bzw. "Prüfziel … aktiv" werden allein über das Prüfziel gegatet).
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
import { dirname, resolve } from "node:path";
const SRC = resolve("docs/wizard-uebergabe/02_Fachcontent_C1-C9/C1_Scoping-AL-Pruefziel.md");
const OUT = resolve("seed/scoping/c1-scope.json");
const PRUEFZIEL: Record<string, string> = {
Informationssicherheit: "informationssicherheit",
Prototypenschutz: "prototypenschutz",
Datenschutz: "datenschutz",
};
const FLAG_RE = /^FLAG_[A-Z_]+$/;
interface C1Row {
id: string;
control: string;
type: "MUSS" | "SOLL" | "HOCH" | "SEHR HOCH";
al2: boolean;
al3: boolean;
pruefziel: string;
condition: string | null;
}
const rows: C1Row[] = [];
for (const line of readFileSync(SRC, "utf8").split("\n")) {
if (!line.startsWith("| ")) continue;
const cells = line.split("|").map((c) => c.trim());
// cells[0] = "" (vor dem ersten |); Nutzdaten ab cells[1]
const [, control, id, type, al2, al3, pruefzielText, condRaw] = cells;
if (!id || id === "Anforderungs-ID" || control.startsWith("---")) continue; // Header/Separator
const pruefziel = PRUEFZIEL[pruefzielText];
if (!pruefziel) continue; // keine gültige Datenzeile
const condClean = (condRaw ?? "").replace(/`/g, "").trim();
const condition = FLAG_RE.test(condClean) ? condClean : null;
rows.push({
id,
control,
type: type as C1Row["type"],
al2: al2 === "Ja",
al3: al3 === "Ja",
pruefziel,
condition,
});
}
mkdirSync(dirname(OUT), { recursive: true });
writeFileSync(OUT, JSON.stringify(rows, null, 2) + "\n");
// Kurzstatistik zur Kontrolle
const by = (f: (r: C1Row) => string) => rows.reduce<Record<string, number>>((a, r) => ((a[f(r)] = (a[f(r)] ?? 0) + 1), a), {});
console.log(`✓ ${rows.length} Anforderungen → seed/scoping/c1-scope.json`);
console.log(" Prüfziele:", JSON.stringify(by((r) => r.pruefziel)));
console.log(" Typen:", JSON.stringify(by((r) => r.type)));
console.log(" Bedingungen:", JSON.stringify(by((r) => r.condition ?? "(immer/Prüfziel)")));