import type { PrismaClient } from "@prisma/client"; /** * Seed der verwalteten Register-Tabellen (§7b) und des Anwender-Handbuchs (§9.7). * Defaults aus den Auftraggeber-Vorgaben FB-80-04 (Risikomatrix) und * AA-80-20 (Klassifizierung/Handhabung). Idempotent pro Mandant. */ const NOW = "2026-07-07T00:00:00.000Z"; const daysFrom = (base: string, d: number) => new Date(new Date(base).getTime() + d * 86400000); // Klassifizierungs-Handhabungsmatrix (AA-80-20) — Reihenfolge: Offen · Intern · Vertraulich · Streng vertraulich const CLASSES = [ { name: "Offen", description: "Öffentlich; keine Schutzanforderungen." }, { name: "Intern", description: "Nur für Mitarbeitende; geringer Schutzbedarf." }, { name: "Vertraulich", description: "Begrenzter Personenkreis; hoher Schutzbedarf." }, { name: "Streng vertraulich", description: "Namentlich Berechtigte; sehr hoher Schutzbedarf." }, ]; // [Offen, Intern, Vertraulich, Streng vertraulich] const ASPECTS: { name: string; category: string; rules: [string, string, string, string] }[] = [ { name: "Kennzeichnung (Papier/elektronisch)", category: "Kennzeichnung", rules: ["nicht nötig", "„Intern“ empfohlen", "„Vertraulich“ verpflichtend", "„Streng vertraulich“ + Bearbeiter"] }, { name: "Vervielfältigung", category: "Handhabung", rules: ["frei", "im Bedarfsfall", "nur mit Freigabe", "nur mit Freigabe, protokolliert"] }, { name: "Weitergabe intern", category: "Weitergabe", rules: ["frei", "an Mitarbeitende", "an Berechtigte, need-to-know", "namentlich Berechtigte, dokumentiert"] }, { name: "Postweg / Versand", category: "Weitergabe", rules: ["Standard", "Standard", "verschlossen, persönlich/Einschreiben", "Einschreiben eigenhändig, Empfangsbestätigung"] }, { name: "E-Mail", category: "Weitergabe", rules: ["ohne Auflagen", "intern ohne Auflagen", "extern verschlüsselt (BL-CRY-04)", "immer verschlüsselt, nur an Berechtigte"] }, { name: "Fax intern/extern", category: "Weitergabe", rules: ["zulässig", "zulässig", "nur mit Ankündigung/Abholung", "unzulässig"] }, { name: "Mobile Datenträger", category: "Speicherung", rules: ["zulässig", "nur freigegebene", "nur verschlüsselt (BL-CRY-03)", "nur verschlüsselt, freigegeben, protokolliert"] }, { name: "Internet / Blogs / Social Media", category: "Weitergabe", rules: ["zulässig", "kein interner Bezug", "unzulässig", "unzulässig"] }, { name: "Entsorgung Papier", category: "Entsorgung", rules: ["Altpapier", "Datentonne", "Aktenvernichter ≥ P-4", "Aktenvernichter ≥ P-5, protokolliert"] }, { name: "Löschung elektronisch / Hardware", category: "Entsorgung", rules: ["normal löschen", "normal löschen", "sicheres Löschen (BL-DEL-01)", "sicheres Löschen/Vernichten, Nachweis"] }, { name: "Speicherung (IT-Systeme)", category: "Speicherung", rules: ["beliebig", "interne Systeme", "zugriffsbeschränkte Ablagen", "verschlüsselt, streng zugriffsbeschränkt"] }, { name: "Homeoffice / mobiles Arbeiten", category: "Nutzung", rules: ["zulässig", "zulässig", "nur mit Sichtschutz, gesperrt bei Abwesenheit", "nur nach Freigabe, keine Papierform"] }, { name: "Öffentlicher Bereich / Dienstreise", category: "Nutzung", rules: ["zulässig", "diskret", "Sichtschutz, nicht unbeaufsichtigt", "unzulässig (kritische Länder)"] }, { name: "Verbale Weitergabe", category: "Weitergabe", rules: ["frei", "unter Kollegen", "nur in geschützter Umgebung", "nur namentlich Berechtigte, abhörsicher"] }, ]; // Risiko-Bewertungsmatrix (FB-80-04): Schadensausmaß(1–4) × EW(1–4) = 1–16 const RISK_CLASSES = [ { name: "Niedrig", maxScore: 3, acceptance: "wird akzeptiert", tone: "ok" }, { name: "Mittel", maxScore: 6, acceptance: "Maßnahmen; Akzeptanz durch Risk Owner", tone: "warn" }, { name: "Hoch", maxScore: 9, acceptance: "Akzeptanz durch Risikomanagement-Gremium", tone: "orange" }, { name: "Kritisch", maxScore: 16, acceptance: "Akzeptanz durch Geschäftsführung", tone: "risk" }, ]; const EW_LEVELS = [ { level: 1, label: "Unwahrscheinlich", definition: "Ereignis in absehbarer Zeit kaum zu erwarten (seltener als alle 5 Jahre)." }, { level: 2, label: "Gelegentlich", definition: "Ereignis denkbar, aber nicht häufig (ca. alle 1–5 Jahre)." }, { level: 3, label: "Wahrscheinlich", definition: "Ereignis tritt voraussichtlich ein (mehrmals pro Jahr)." }, { level: 4, label: "Sehr wahrscheinlich", definition: "Ereignis tritt regelmäßig/häufig ein (monatlich oder öfter)." }, ]; const DAMAGE_DIMS: { name: string; levels: Record }[] = [ { name: "Gesetzes-/Vertragsverstöße", levels: { "1": "unerheblich", "2": "geringfügige Verstöße", "3": "erhebliche Verstöße/Bußgelder", "4": "gravierende Rechtsfolgen/Straftatbestand" } }, { name: "Datenschutz", levels: { "1": "kein Personenbezug", "2": "geringe Betroffenheit", "3": "sensible Daten betroffen", "4": "massenhaft sensible Daten / hohe Betroffenheit" } }, { name: "Persönliche Unversehrtheit", levels: { "1": "keine", "2": "leichte Beeinträchtigung", "3": "Gesundheitsgefahr", "4": "Gefahr für Leib und Leben" } }, { name: "Aufgabenerfüllung", levels: { "1": "keine Einschränkung", "2": "geringe Einschränkung", "3": "erhebliche Einschränkung", "4": "Kernaufgaben nicht erfüllbar" } }, { name: "Innen-/Außenwirkung (Reputation)", levels: { "1": "keine", "2": "intern begrenzt", "3": "öffentlich wahrnehmbar", "4": "nachhaltiger Reputationsschaden" } }, { name: "Störungs-/Ausfallzeit", levels: { "1": "< 1 Std.", "2": "bis 1 Tag", "3": "bis 1 Woche", "4": "> 1 Woche" } }, { name: "Finanzielle Auswirkungen", levels: { "1": "bis 10 T€", "2": "bis 50 T€", "3": "bis 200 T€", "4": "über 500 T€" } }, ]; // Krypto-Register (VA-07) — Demo-Datensätze inkl. Ablaufüberwachung const CRYPTO = [ { dienst: "TLS Web-Portal (portal.gefim.example)", schluessel: "RSA 3072 · Let's Encrypt", algorithmus: "TLS 1.3 / AES-256", ablaufOffset: 45, verantwortlich: "IT-Leitung", speicherort: "Reverse Proxy", baselineRef: "BL-CRY-01" }, { dienst: "VPN-Gateway", schluessel: "ECDSA P-256 · interne CA", algorithmus: "IKEv2 / AES-256-GCM", ablaufOffset: 12, verantwortlich: "IT-Leitung", speicherort: "Firewall/HSM", baselineRef: "BL-CRY-02" }, { dienst: "Festplattenverschlüsselung Notebooks", schluessel: "BitLocker · TPM 2.0", algorithmus: "AES-256-XTS", ablaufOffset: null, verantwortlich: "IT-Betrieb", speicherort: "MDM-Recovery", baselineRef: "BL-CRY-03" }, { dienst: "S/MIME Zertifikate Geschäftsführung", schluessel: "RSA 2048 · öffentliche CA", algorithmus: "S/MIME", ablaufOffset: -8, verantwortlich: "IT-Leitung", speicherort: "Smartcard", baselineRef: "BL-CRY-04" }, ]; // Anwender-Handbuch (§9.7) — kuratierte Themen, Werte via {{VARIABLE}} synchron zur Baseline const HANDBOOK: { category: string; title: string; bodyMd: string; sourceRefs: string[] }[] = [ { category: "Zugang & Passwörter", title: "Passwörter & Mehr-Faktor-Anmeldung", bodyMd: "**Das Wichtigste kurz:**\n\n- Nutze Passwörter mit **mindestens {{PW_MIN_LENGTH}} Zeichen** ({{PW_COMPLEXITY}}).\n- Aktiviere **Mehr-Faktor-Authentifizierung (MFA)** über {{TECH_MFA}} für {{MFA_SCOPE}}.\n- Gib Passwörter **nie** weiter und nutze für jeden Dienst ein eigenes.\n- Bei Verdacht auf Kompromittierung: Passwort sofort ändern und melden.", sourceRefs: ["R08#4.1.2"], }, { category: "Zugang & Passwörter", title: "Zugriffsrechte – nur was du brauchst", bodyMd: "- Zugriff erhältst du **nach dem Minimalprinzip** (need-to-know) über {{TOOL_TICKET}}.\n- Nicht mehr benötigte Rechte werden entzogen; deine Rechte werden regelmäßig **überprüft (Rezertifizierung, {{RECERT_FREQ}})**.\n- Sperre deinen Bildschirm bei jedem Verlassen des Arbeitsplatzes.", sourceRefs: ["R08#4.2.1"], }, { category: "Umgang mit Informationen", title: "Daten richtig klassifizieren & handhaben", bodyMd: "- Behandle Informationen gemäß ihrer **Schutzklasse** (Offen · Intern · Vertraulich · Streng vertraulich).\n- **Vertrauliche** Inhalte extern nur **verschlüsselt** versenden.\n- Dokumente nicht offen liegen lassen (Clean Desk); vertrauliche Ausdrucke sicher vernichten.", sourceRefs: ["R02"], }, { category: "Sicheres Arbeiten", title: "Mobiles Arbeiten & Homeoffice", bodyMd: "- Nutze nur **freigegebene Geräte** (verwaltet über {{TECH_MDM}}).\n- Achte auf **Sichtschutz** in der Öffentlichkeit; keine vertraulichen Gespräche in Bahn/Café.\n- Verbinde dich extern nur über {{TECH_VPN}} mit MFA.", sourceRefs: ["R06"], }, { category: "Sicheres Arbeiten", title: "E-Mail, Internet & Schadsoftware", bodyMd: "- Öffne **keine unerwarteten Anhänge/Links**; im Zweifel nachfragen.\n- Melde verdächtige E-Mails (Phishing) über den Meldeweg.\n- {{TECH_MALWARE}} schützt deine Geräte – deaktiviere den Schutz nie.", sourceRefs: ["R10", "R09"], }, { category: "Vorfälle", title: "Sicherheitsvorfall? So meldest du ihn", bodyMd: "**Sofort melden** bei Verdacht auf Vorfall, Datenverlust oder verlorenem Gerät:\n\n- an die {{ROLE_ISB}} bzw. den definierten Meldeweg.\n- Lieber einmal zu viel melden – schnelle Meldung begrenzt den Schaden.\n- Nichts vertuschen, nichts eigenmächtig „reparieren“.", sourceRefs: ["R04"], }, ]; export async function importManaged(prisma: PrismaClient, tenantId: string) { // Idempotent: alte Datensätze pro Mandant entfernen await prisma.handlingRule.deleteMany({ where: { tenantId } }); await prisma.handlingAspect.deleteMany({ where: { tenantId } }); await prisma.classificationClass.deleteMany({ where: { tenantId } }); await prisma.cryptoEntry.deleteMany({ where: { tenantId } }); await prisma.riskMatrixClass.deleteMany({ where: { tenantId } }); await prisma.riskEwLevel.deleteMany({ where: { tenantId } }); await prisma.riskDamageDimension.deleteMany({ where: { tenantId } }); await prisma.handbookTopic.deleteMany({ where: { tenantId } }); // Register-/Handbuch-Dokumente in der Bibliothek (interaktiv gerendert) const registerDocs = [ { code: "CRYPTO", type: "REGISTER" as const, title: "Verschlüsselungsmechanismen-Register", orderIdx: 902, intro: "Verwaltetes Register der eingesetzten Verschlüsselung (VA-07). Ablaufüberwachung aktiv." }, { code: "RISKMATRIX", type: "REGISTER" as const, title: "Risiko-Bewertungsmatrix", orderIdx: 903, intro: "Zentrale Bewertungsmatrix Schadensausmaß × Eintrittswahrscheinlichkeit (R03 / VA-09), Defaults nach FB-80-04." }, { code: "CLASSIFICATION", type: "REGISTER" as const, title: "Klassifizierung & Handhabungsmatrix", orderIdx: 904, intro: "Schutzklassen und Handhabungsregeln (R02 / VA-08), Defaults nach AA-80-20." }, { code: "HANDBUCH", type: "HANDBUCH" as const, title: "Anwender-Handbuch Informationssicherheit", orderIdx: 950, intro: "Kompakte Zusammenfassung der wichtigsten Regeln für Mitarbeitende – mit Deep-Links in die Quelldokumente." }, // Verwaltete Register (GAP-Report §5.2). Vorerst als benannte Register-Dokumente mit // dokumentierten Pflichtspalten; die editierbare Datenpflege (Attribute/Cross-Links) folgt (WP3.0). { code: "REG-PROJECTS", type: "REGISTER" as const, title: "Register Informationssicherheit in Projekten", orderIdx: 905, intro: "Projekte mit IS-Klassifizierung, Risikobewertung, abgeleiteten Maßnahmen/Status und ISB-Einbindung (R01 / VA-19). Pflichtspalten: Projekt, IS-Klassifizierung, Risikobewertung, Maßnahmen/Status, ISB-Einbindung." }, { code: "REG-SENS-ROLES", type: "REGISTER" as const, title: "Register sensibler Tätigkeiten", orderIdx: 906, intro: "Sensible Tätigkeitsbereiche/Rollen mit geforderten Eignungsnachweisen und Prüftiefe (R05 / VA-14). Pflichtspalten: Rolle/Bereich, geforderte Eignungsnachweise, Prüftiefe, Verantwortlich, Review." }, { code: "REG-AUDIT-PLAN", type: "REGISTER" as const, title: "Audit-Programm-Register", orderIdx: 907, intro: "Auditprogramm mit Zeitplan, Umfang, geprüften Controls, Prüfern und Ergebnissen (R03 / VA-15). Pflichtspalten: Zeitplan, Umfang, geprüfte Controls, Prüfer, Ergebnisse." }, { code: "REG-EXT-SERVICES", type: "REGISTER" as const, title: "Register externe IT-/Cloud-/KI-Dienste", orderIdx: 908, intro: "Externe/Cloud-/KI-Dienste mit Schutzbedarf, Datenlokation (EU), Verschlüsselung, Exit-Strategie, Quelle/Lieferant und Freigabestatus (R13/VA-10 + R02/VA-08). Cross-Link an Lieferantensteuerung und Asset-Inventar." }, ]; for (const d of registerDocs) { await prisma.policyDocument.upsert({ where: { tenantId_code: { tenantId, code: d.code } }, update: { title: d.title, type: d.type, orderIdx: d.orderIdx, rawMarkdown: `# ${d.title}\n\n${d.intro}` }, create: { tenantId, code: d.code, type: d.type, title: d.title, version: "1.0", status: "FREIGEGEBEN", orderIdx: d.orderIdx, rawMarkdown: `# ${d.title}\n\n${d.intro}` }, }); } // Krypto-Register for (let i = 0; i < CRYPTO.length; i++) { const c = CRYPTO[i]; await prisma.cryptoEntry.create({ data: { tenantId, dienst: c.dienst, schluessel: c.schluessel, algorithmus: c.algorithmus, ablaufdatum: c.ablaufOffset === null ? null : daysFrom(NOW, c.ablaufOffset), verantwortlich: c.verantwortlich, speicherort: c.speicherort, baselineRef: c.baselineRef, orderIdx: i, }, }); } // Klassifizierung + Handhabungsmatrix const classIds: string[] = []; for (let i = 0; i < CLASSES.length; i++) { const cl = await prisma.classificationClass.create({ data: { tenantId, name: CLASSES[i].name, description: CLASSES[i].description, orderIdx: i } }); classIds.push(cl.id); } for (let a = 0; a < ASPECTS.length; a++) { const asp = await prisma.handlingAspect.create({ data: { tenantId, name: ASPECTS[a].name, category: ASPECTS[a].category, orderIdx: a } }); for (let c = 0; c < classIds.length; c++) { await prisma.handlingRule.create({ data: { tenantId, classId: classIds[c], aspectId: asp.id, text: ASPECTS[a].rules[c] } }); } } // Risiko-Bewertungsmatrix for (let i = 0; i < RISK_CLASSES.length; i++) { const r = RISK_CLASSES[i]; await prisma.riskMatrixClass.create({ data: { tenantId, name: r.name, maxScore: r.maxScore, acceptance: r.acceptance, tone: r.tone, orderIdx: i } }); } for (const ew of EW_LEVELS) await prisma.riskEwLevel.create({ data: { tenantId, level: ew.level, label: ew.label, definition: ew.definition } }); for (let i = 0; i < DAMAGE_DIMS.length; i++) { await prisma.riskDamageDimension.create({ data: { tenantId, name: DAMAGE_DIMS[i].name, levels: DAMAGE_DIMS[i].levels, orderIdx: i } }); } // Anwender-Handbuch for (let i = 0; i < HANDBOOK.length; i++) { const h = HANDBOOK[i]; await prisma.handbookTopic.create({ data: { tenantId, category: h.category, title: h.title, bodyMd: h.bodyMd, sourceRefs: h.sourceRefs, orderIdx: i } }); } return { crypto: CRYPTO.length, classes: CLASSES.length, aspects: ASPECTS.length, damage: DAMAGE_DIMS.length, handbook: HANDBOOK.length }; }