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>
68 lines
3.4 KiB
TypeScript
68 lines
3.4 KiB
TypeScript
// AP3 — Anwendbarkeitserklärung (SoA). Prüft Vorbefüllung, Bedingungs-Logik,
|
|
// Idempotenz und Vollständigkeits-Markierung gegen die lokale DB (Wegwerf-Mandant).
|
|
//
|
|
// Lauf: npx tsx scripts/test-soa.ts
|
|
|
|
import "dotenv/config";
|
|
import { prisma, dbForTenant } from "../src/server/db";
|
|
import { ensureSoaEntries, loadIsoSoaControls } from "../src/server/soa-statement";
|
|
import { isSoaEntryComplete } from "../src/lib/soa";
|
|
|
|
let failures = 0;
|
|
const ok = (c: boolean, m: string) => { console.log(`${c ? "✓" : "✗ FEHLER"} ${m}`); if (!c) failures++; };
|
|
const SLUG = "ap3-test-soa";
|
|
|
|
async function cleanup() {
|
|
const t = await prisma.tenant.findUnique({ where: { slug: SLUG }, select: { id: true } });
|
|
if (t) {
|
|
await prisma.soaEntry.deleteMany({ where: { tenantId: t.id } });
|
|
await prisma.policyVariable.deleteMany({ where: { tenantId: t.id } });
|
|
await prisma.tenant.delete({ where: { id: t.id } });
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
await cleanup();
|
|
|
|
const controls = loadIsoSoaControls();
|
|
ok(controls.length === 93, `loadIsoSoaControls: ${controls.length} Annex-A-Controls (erwartet 93)`);
|
|
const devControl = controls.find((c) => c.condition === "FLAG_DEV_INHOUSE");
|
|
const plainControl = controls.find((c) => c.condition === null);
|
|
ok(!!devControl && !!plainControl, "je ein Control mit/ohne Bedingung vorhanden");
|
|
|
|
const tenant = await prisma.tenant.create({ data: { name: "AP3 SoA", slug: SLUG } });
|
|
const t = tenant.id;
|
|
// Flags: DEV-Inhouse AUS → DEV-Controls default nicht anwendbar; Personendaten AN.
|
|
await prisma.policyVariable.createMany({
|
|
data: [
|
|
{ tenantId: t, key: "FLAG_DEV_INHOUSE", title: "Dev", kind: "boolean", value: "false" },
|
|
{ tenantId: t, key: "FLAG_PERSONAL_DATA", title: "PD", kind: "boolean", value: "true" },
|
|
],
|
|
});
|
|
const db = dbForTenant(t);
|
|
|
|
const n1 = await ensureSoaEntries(db, t);
|
|
ok(n1 === 93, `ensureSoaEntries legt 93 Zeilen an (${n1})`);
|
|
const n2 = await ensureSoaEntries(db, t);
|
|
ok(n2 === 0, "zweiter Lauf ist idempotent (0 neue Zeilen)");
|
|
ok((await prisma.soaEntry.count({ where: { tenantId: t } })) === 93, "93 SoA-Zeilen persistiert");
|
|
|
|
const devEntry = await prisma.soaEntry.findFirst({ where: { tenantId: t, control: devControl!.control } });
|
|
ok(devEntry?.applicable === false, `Bedingung greift: ${devControl!.control} (FLAG_DEV_INHOUSE aus) → nicht anwendbar`);
|
|
const plainEntry = await prisma.soaEntry.findFirst({ where: { tenantId: t, control: plainControl!.control } });
|
|
ok(plainEntry?.applicable === true, `Control ohne Bedingung (${plainControl!.control}) → anwendbar`);
|
|
|
|
// Vollständigkeit: frisch ohne Begründung → unvollständig; nach Begründung → vollständig.
|
|
ok(!isSoaEntryComplete({ applicable: true, justification: "" }), "Control ohne Begründung → unvollständig");
|
|
ok(isSoaEntryComplete({ applicable: false, justification: "Ausschluss: keine Eigenentwicklung" }), "Ausschluss mit Begründung → vollständig");
|
|
const incomplete = await prisma.soaEntry.count({ where: { tenantId: t, justification: "" } });
|
|
ok(incomplete === 93, "alle 93 Zeilen initial ohne Begründung (unvollständig)");
|
|
|
|
await cleanup();
|
|
console.log("\n✓ aufgeräumt (Wegwerf-Mandant entfernt)");
|
|
}
|
|
|
|
main()
|
|
.then(() => { console.log(failures === 0 ? "\nAP3-SoA grün." : `\n${failures} Prüfung(en) fehlgeschlagen.`); process.exit(failures === 0 ? 0 : 1); })
|
|
.catch(async (e) => { console.error(e); await cleanup().catch(() => {}); process.exit(1); });
|