Iteration 3: Risikoanalyse + Menü-Trennung + Prozesskategorie
- Risiko-Modul (SPEC §4.2): Risk/RiskAsset-Schema mit RLS, laufende Nummer je Mandant (R-001), Brutto-/Rest-Bewertung (5×5), Behandlung, Status, Owner- und Prozess-Bezug, n:m betroffene Assets - /risks im Mockup-Layout: 5×5-Heatmap (Farb-Bänder + R-Chips, Legende, Achsen) und Risikoregister mit Score-Pillen, Behandlungs-Tags, Status - Risiko-Popups nach App-Muster: Read-only-Detail (Bewertungs-Band Brutto vs. Rest, betroffene Assets, Maßnahmen-Platzhalter für Iteration 4), Bearbeiten im Popup (Bewertung, Assets verknüpfen, Löschen), Anlegen; Server-Actions mit Zod, RBAC und Audit-Log - Menü aufgetrennt: eigene Punkte "Asset-Inventar", "Business Impact Analyse" und "Risikoanalyse" (Modul-Tabs entfernt) - Prozesskategorie (Kernprozess/Managementprozess/Unterstützender Prozess): Schema-Feld, Formulare, Listen-Spalte, Popup-Tag - Asset-Detail-Popup optisch ans Prozess-Popup angeglichen (Stammdaten- Karte mit violettem Rand, Abhängigkeiten-Tabelle, Risiken-Band) - "Zugeordnete Risiken" jetzt echt: im Asset-Popup (verknüpfte Risiken) und im Prozess-Popup (direkt + über Assets aggregiert); Dashboard-KPI "Offene Risiken" mit echten Zahlen; Seed mit 4 Beispiel-Risiken Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+108
@@ -138,6 +138,7 @@ async function main() {
|
||||
tenantId: tenant.id,
|
||||
name: "Auftragsabwicklung",
|
||||
description: "Vom Kundenauftrag bis zur Auslieferung.",
|
||||
category: "CORE",
|
||||
ownerId: owner.id,
|
||||
},
|
||||
});
|
||||
@@ -169,6 +170,7 @@ async function main() {
|
||||
tenantId: tenant.id,
|
||||
name: "Kundenbetreuung",
|
||||
description: "Anfragen, Angebote, Support.",
|
||||
category: "SUPPORT",
|
||||
ownerId: isb.id,
|
||||
},
|
||||
});
|
||||
@@ -195,6 +197,112 @@ async function main() {
|
||||
|
||||
console.log("✔ Beispiel-Assets, -Prozesse und BIA angelegt");
|
||||
}
|
||||
|
||||
// 6. Beispiel-Risiken (nur wenn noch keine existieren)
|
||||
const riskCount = await prisma.risk.count({ where: { tenantId: tenant.id } });
|
||||
if (riskCount === 0) {
|
||||
const isb = await prisma.user.findUniqueOrThrow({
|
||||
where: { tenantId_email: { tenantId: tenant.id, email: "admin@demo.example" } },
|
||||
});
|
||||
const byName = async (name: string) =>
|
||||
prisma.asset.findFirstOrThrow({ where: { tenantId: tenant.id, name } });
|
||||
const erp = await byName("ERP-System");
|
||||
const crm = await byName("Kundendatenbank");
|
||||
const hoster = await byName("Cloud-Hoster (IaaS)");
|
||||
const auftrag = await prisma.process.findFirstOrThrow({
|
||||
where: { tenantId: tenant.id, name: "Auftragsabwicklung" },
|
||||
});
|
||||
|
||||
const risks: {
|
||||
title: string;
|
||||
threat: string;
|
||||
vulnerability: string;
|
||||
likelihood: number;
|
||||
impact: number;
|
||||
residual?: [number, number];
|
||||
treatment: "AVOID" | "MITIGATE" | "TRANSFER" | "ACCEPT";
|
||||
status: "OPEN" | "IN_TREATMENT" | "ACCEPTED" | "CLOSED";
|
||||
assets: string[];
|
||||
processId?: string;
|
||||
description?: string;
|
||||
}[] = [
|
||||
{
|
||||
title: "Ransomware auf ERP",
|
||||
threat: "Schadsoftware / Verschlüsselungstrojaner",
|
||||
vulnerability: "Fehlende Offline-Backups, Makro-Ausführung erlaubt",
|
||||
likelihood: 4,
|
||||
impact: 5,
|
||||
residual: [2, 4],
|
||||
treatment: "MITIGATE",
|
||||
status: "IN_TREATMENT",
|
||||
assets: [erp.id, crm.id],
|
||||
processId: auftrag.id,
|
||||
description: "Verschlüsselung der ERP-Datenbank würde die Auftragsabwicklung stoppen.",
|
||||
},
|
||||
{
|
||||
title: "Ausfall Cloud-Hoster",
|
||||
threat: "Ausfall des Rechenzentrums / Insolvenz Dienstleister",
|
||||
vulnerability: "Kein Ausweich-Standort, Single Provider",
|
||||
likelihood: 4,
|
||||
impact: 4,
|
||||
treatment: "TRANSFER",
|
||||
status: "OPEN",
|
||||
assets: [hoster.id, erp.id],
|
||||
processId: auftrag.id,
|
||||
},
|
||||
{
|
||||
title: "Phishing / Social Engineering",
|
||||
threat: "Gezielte Phishing-Kampagnen",
|
||||
vulnerability: "Fehlende Awareness-Schulungen",
|
||||
likelihood: 3,
|
||||
impact: 3,
|
||||
residual: [2, 3],
|
||||
treatment: "MITIGATE",
|
||||
status: "IN_TREATMENT",
|
||||
assets: [crm.id],
|
||||
},
|
||||
{
|
||||
title: "Verlust mobiler Geräte",
|
||||
threat: "Diebstahl/Verlust von Notebooks",
|
||||
vulnerability: "Unvollständige Festplattenverschlüsselung",
|
||||
likelihood: 2,
|
||||
impact: 3,
|
||||
treatment: "ACCEPT",
|
||||
status: "ACCEPTED",
|
||||
assets: [],
|
||||
},
|
||||
];
|
||||
|
||||
let refNo = 1;
|
||||
for (const r of risks) {
|
||||
const risk = await prisma.risk.create({
|
||||
data: {
|
||||
tenantId: tenant.id,
|
||||
refNo: refNo++,
|
||||
title: r.title,
|
||||
description: r.description,
|
||||
threat: r.threat,
|
||||
vulnerability: r.vulnerability,
|
||||
likelihood: r.likelihood,
|
||||
impact: r.impact,
|
||||
score: r.likelihood * r.impact,
|
||||
residualLikelihood: r.residual?.[0],
|
||||
residualImpact: r.residual?.[1],
|
||||
residualScore: r.residual ? r.residual[0] * r.residual[1] : undefined,
|
||||
treatment: r.treatment,
|
||||
status: r.status,
|
||||
ownerId: isb.id,
|
||||
processId: r.processId,
|
||||
},
|
||||
});
|
||||
for (const assetId of r.assets) {
|
||||
await prisma.riskAsset.create({
|
||||
data: { tenantId: tenant.id, riskId: risk.id, assetId },
|
||||
});
|
||||
}
|
||||
}
|
||||
console.log(`✔ ${risks.length} Beispiel-Risiken angelegt`);
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user