Risiko-Minderung dezimal + editierbar, Bedrohungs-/Schwachstellen-Kataloge

- Minderung je Maßnahmen-Verknüpfung jetzt dezimal (0,00–4,00 statt
  ganzer Punkte): mehrere Maßnahmen summieren sich, bis eine Dimension
  einen vollen Punkt sinkt; Rest-Risiko entsprechend dezimal (z. B.
  2,5 × 4 = 10), gerundet auf 2 Nachkommastellen, lokalisierte Anzeige
- Minderung bereits verknüpfter Maßnahmen direkt im Risiko-Bearbeiten
  editierbar (Eingabefelder je Verknüpfung + Speichern-Button);
  Komma-Eingaben werden akzeptiert
- Globale Kataloge Bedrohungen/Schwachstellen (je 15 Einträge, Seed):
  Eingabefelder schlagen per datalist Katalogwerte vor, Freitext bleibt
  weiterhin möglich (SPEC §5 Threat/Vulnerability)
- Schema: reduction*/residual* auf Float, Threat-/Vulnerability-Modelle

Verifiziert: −1 + −0,5 Minderung → Rest 2,5 × 4 = 10 · Erhöht (DB +
Anzeige), Neuberechnung auch beim Entfernen einer Verknüpfung.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Martin
2026-07-02 16:55:27 +02:00
co-authored by Claude Fable 5
parent 668e587881
commit dfe27de51f
10 changed files with 274 additions and 70 deletions
+17 -4
View File
@@ -2,7 +2,7 @@ import Link from "next/link";
import { getTranslations } from "next-intl/server";
import { Plus } from "lucide-react";
import { requireSession } from "@/server/auth";
import { dbForTenant } from "@/server/db";
import { dbForTenant, prisma } from "@/server/db";
import { hasPermission, requirePermission } from "@/server/rbac";
import { Button } from "@/components/ui/button";
import { PageHead, Pill, SectTitle, Tag } from "@/components/mockup-ui";
@@ -82,7 +82,7 @@ export default async function RisksPage({
: null;
const needsFormData = canWrite && (params.edit || params.new);
const [users, processes] = needsFormData
const [users, processes, threats, vulnerabilities] = needsFormData
? await Promise.all([
db.user.findMany({
where: { status: "ACTIVE" },
@@ -90,8 +90,13 @@ export default async function RisksPage({
orderBy: { name: "asc" },
}),
db.process.findMany({ select: { id: true, name: true }, orderBy: { name: "asc" } }),
// Globale Kataloge (kein Mandanten-Bezug)
prisma.threat.findMany({ orderBy: { name: "asc" } }).then((r) => r.map((x) => x.name)),
prisma.vulnerability
.findMany({ orderBy: { name: "asc" } })
.then((r) => r.map((x) => x.name)),
])
: [[], []];
: [[], [], [], []];
const availableAssets =
canWrite && params.edit && modalRisk
? await db.asset.findMany({
@@ -263,11 +268,19 @@ export default async function RisksPage({
processes={processes}
availableAssets={availableAssets}
availableMeasures={availableMeasures}
threats={threats}
vulnerabilities={vulnerabilities}
/>
) : modalRisk ? (
<RiskDetailModal risk={modalRisk} canWrite={canWrite} />
) : params.new && canWrite ? (
<RiskCreateModal users={users} processes={processes} preselectedAsset={preselectedAsset} />
<RiskCreateModal
users={users}
processes={processes}
preselectedAsset={preselectedAsset}
threats={threats}
vulnerabilities={vulnerabilities}
/>
) : null}
</main>
);