Fundament: ISMS-Module entfernt; Craftvia-Rollen, Module, Navigation, i18n-Split

- ISMS-Routen, Actions, Server-/Lib-Code, Komponenten, Prisma-Modelle, Seeds,
  Importer, Skripte und ISMS-Tests entfernt (Fundament bleibt: Auth, Identity,
  MFA/WebAuthn, RBAC, Audit, Mail, Storage, Backup/DSGVO, Plattform-Admin)
- Schema auf Fundament-Modelle reduziert; TenantSettings generisch (+phone/email)
- TENANT_MODELS (db.ts, backup/topology.ts) und PII-Felder ausgedünnt
- RBAC: Rollen tenant-admin/backoffice/team-lead/technician + Craftvia-Permissions
- Modul-Katalog (customers, sites, teams, work_orders, imports, field, reports,
  emergency, documents, notifications, lotse) + Navigation aus src/lib/nav.ts
- Modul-Routen mit requireModule-Layout und Platzhalterseite
- Message-Katalog je Namespace (messages/<locale>/<namespace>.json), fs-Loader
- check-module-guards: Modul-Key aus src/server/actions/<moduleKey>/
- Provisionierung, Admin-Konsole, Einstellungen, Files-Route, Mail entkoppelt
- Seed minimal (demo/demo2, Nutzer je Rolle); Fundament-Tests auf Role/
  NotificationPreference-Fixtures umgestellt

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-14 11:35:44 +02:00
co-authored by Claude Opus 5
parent c8e6f30a27
commit 8491c7f173
443 changed files with 1325 additions and 87773 deletions
@@ -1,168 +0,0 @@
# Contracts-Checkliste — Kickoff Dev A × Dev B (15 Min.)
> Zweck: Die Naht zwischen **Paket Dev A** (Wizard-Shell/Scoping/AL) und **Paket Dev B**
> (Engines/Fragebogen/Richtlinien) **einmal gemeinsam bestätigen**, bevor beide parallel starten.
> Grundlage: der identische *Contracts-Anhang* §1–§6 in beiden Entwicklerpaketen.
> Vorgehen: Punkt für Punkt durchgehen, abhaken, offene Entscheidungen unten eintragen, beide signieren.
> Danach laufen A und B konfliktarm parallel — einzige echte Nahtstellen sind
> `prisma/schema.prisma`, `scripts/check-module-guards.ts` und die Step-Registry.
**Basis-Branch:** `dev` · **Feature-Branches:** `dev/a*-…` (A) bzw. `dev/b*-…` (B) · **PR-Ziel:** `dev`
Kein Push durch die Agenten; häufig auf `dev` rebasen; kleine PRs je Story.
---
## §1 Task-Objekt — **Owner: Dev B** (A konsumiert)
Dev B besitzt das Modell + `createTask`-Action; Dev A / Wizard-Gates rufen sie auf.
- [ ] `type`: `document_create | evidence_provide | technical | organizational | validation | policy_approval`
- [ ] Felder: `owner`, `dueDate`, `priority: 'hoch'|'mittel'|'niedrig'`, `status`
- [ ] `resources` (JSON): `{ tool, budget, personnel, time }`
- [ ] `origin` (Herkunft/Trigger) vorhanden
- [ ] `links` (polymorph): `{ control?, risk?, document?, asset? }`
- [ ] Bestehender `policy_approval`-Flow bleibt unverändert lauffähig (Regression grün)
- [ ] **Signatur der `createTask`-Action steht fix**, bevor A ihre Gates verdrahtet
→ Signatur hier notieren: `_____________________________________________`
## §2 Validierungsstatus — **Owner: Dev A** (B konsumiert)
- [ ] `enum ObjectReviewStatus { offen, in_bearbeitung, zur_validierung, validiert, zurueckgewiesen }`
- [ ] Zusatzfelder: `reviewComment`, `reviewerId`
- [ ] Regel bestätigt: **„Nur `validiert` zählt als bestätigt."**
- [ ] RBAC-Recht `validate_objects` + Rolle `external_validator` (klonbar) angelegt
- [ ] Feld-Konvention (an welchen Objekten der Status hängt) für Wizard-Gates fix
## §3 Flags in `variables.schema.json` — **Owner: Dev B** (Namen fix, A entwickelt dagegen)
- [ ] Namen unverändert übernommen (keine Umbenennung ohne beidseitige Abstimmung):
`FLAG_INCLUDE_SHOULD, FLAG_HIGH_PROTECTION, FLAG_VERY_HIGH_PROTECTION,
FLAG_ELEVATED_PROTECTION, FLAG_PERSONAL_DATA, FLAG_PROTOTYPE_PROTECTION,
FLAG_CLOUD_USED, FLAG_AI_USED, FLAG_OT_USED, FLAG_DEV_INHOUSE, FLAG_MOBILE_WORK,
FLAG_MOBILE_DEVICES, FLAG_CRYPTO_PKI, FLAG_EXTERNAL_IT, FLAG_CUSTOMER_SYSTEMS,
FLAG_ISB_EXTERNAL, FLAG_ISB_INTERNAL`
- [ ] `FLAG_ELEVATED_PROTECTION` bleibt **abgeleitet** (`applyProtection`) — nie manuell setzen
- [ ] Nach Schema-Änderung: `python3 seed/isms-vorlagenpaket-v2/_verify.py` → **OK**
## §4 Prüfziele — beidseitig fix
- [ ] Werte: `informationssicherheit | prototypenschutz | datenschutz`
- [ ] Wirkung bestätigt: Kapitel `8.x` nur bei Prototyp, `9.x` nur bei Datenschutz
## §5 Step-Registry — **Owner: Dev A** (B konsumiert)
- [ ] Signatur fix: `registerStep({ key, title, order, guard, component })`
- [ ] Schritt-Keys/Reihenfolge:
`1 scoping · 2 context · 3 roles · 4 policies · 5 assets · 6 risks · 7 controls · 8 gap · 9 readiness`
- [ ] Klar: **B liefert die Komponenten für `context` (Schritt 2) und `policies` (Schritt 4)**;
A stellt die Registry-Schnittstelle stabil bereit, bevor B einklinkt
## §6 Migrations-Protokoll — beidseitig verbindlich
- [ ] Vor jedem Migrations-Erzeugen zuerst auf `dev` rebasen
- [ ] **Migrationen nie gleichzeitig ohne Absprache** erstellen
- [ ] Je Branch **genau eine Migration pro Story**
- [ ] Prisma-7-Flow: `migrate diff --from-config-datasource … --to-schema … --script`
→ RLS-DO-Block manuell anhängen → `migrate deploy` → `generate`
- [ ] Neue tenant-gebundene Modelle → `TENANT_MODELS` (`src/server/db.ts`) **und** RLS-Policy
- [ ] Neue Server-Action-Datei → in `scripts/check-module-guards.ts` eintragen (sonst Build-Fail)
---
## Erwartete Migrations-Reihenfolge (gemeinsam festlegen)
Beide erzeugen Migrationen an `schema.prisma` — Reihenfolge vorab abstimmen, um Konflikte zu vermeiden.
Vorschlag (an tatsächlicher Story-Reihenfolge ausrichten):
| # | Story | Owner | Migration (Arbeitsname) |
|---|-------|-------|-------------------------|
| 1 | A1-1 | A | `onboarding_progress` |
| 2 | F1 | B | `tasks_wizard_fields` |
| 3 | F2 | A | (Enum `ObjectReviewStatus` + `external_validator`) |
| 4 | A2-2 | A | `wizard_scope` |
| 5 | B3 | B | `wizard_facts` |
- [x] Reihenfolge oben bestätigt / angepasst — A1-1(A) → F1(B) → F2(A) → A2-2(A) → B3(B)
- [x] Wer erzeugt die **erste** Migration? → **Dev A** (`onboarding_progress`, A1-1) — von Dev B bestätigt; B rebast danach zuerst
## Offene Fachpunkte (aus C0 — vor bzw. begleitend zu klären)
- [ ] Neue `FLAG_*` in `variables.schema.json` ergänzt (F4, Dev B)
- [ ] Vorlagen `P01`/`D01` + `VA-20` angelegt (B4-3, Dev B)
- [ ] Baseline `BL-*` normalisiert
- [ ] ISB-Freigabe der neuen/geänderten Texte (fachlich, kein Code)
---
## Sign-off
- Dev A bestätigt §1–§6 + Migrations-Reihenfolge: **Claude (Dev A)** (Datum: 2026-07-28)
- Dev B bestätigt §1–§6 + Migrations-Reihenfolge: **Claude (Dev B)** (Datum: 2026-07-28)
> Änderungen an §1–§6 nach dem Kickoff nur **gemeinsam** und mit Update dieser Datei
> **und** von `docs/STAND-dev-branch.md`.
---
## Dev A — Kickoff-Vorbereitung (Positionen & Vorschläge, warten auf B-Bestätigung)
> Nicht-bindend bis zum gemeinsamen Sign-off. „A-Vorschlag" = braucht B's Ja; „A bestätigt" =
> liegt in A's Hoheit und ist von A's Seite geklärt.
**§1 Task-Objekt (Owner B):** A bestätigt die Feldliteralwerte (`type`-Werte, `priority`, `resources`,
`origin`, `links`) wie im Anhang. **A braucht von B eine fixe `createTask`-Signatur, bevor A Gates/Trigger
verdrahtet.** A-Vorschlag als Startpunkt:
`createTask(input: { type, title, origin, owner?, dueDate?, priority?, resources?, links? }): Promise<Task>`
(Server-Action in B's Hoheit; A ruft nur auf). → **B bestätigt/ändert Signatur:** ✅ **bestätigt (unverändert)** —
`createTask(input: { type, title, origin, owner?, dueDate?, priority?, resources?, links? }): Promise<Task>`.
Mapping/Defaults (B-Hoheit, F1): `owner` → DB-Spalte `assigneeId` (eine verantwortliche Person; optional, da B1-Vorschläge unassigned starten);
`priority` default `'mittel'`; `resources = { tool?, budget?, personnel?, time? }` (alle optional); `links = { control?, risk?, document?, asset? }` (IDs/Refs).
`status` setzt die Action **serverseitig** (Default `OPEN`; auto-generierte B1-Vorschläge starten im Vorschlags-Status zur Bestätigung) — **kein** Input-Feld.
Bestehende `entityType/entityId/entityRef` bleiben für den `policy_approval`-Flow erhalten (F1 fügt nur Felder hinzu); `links.document` bildet den Dokumentbezug ab.
**§2 Validierungsstatus (Owner A) — A bestätigt:**
- `enum ObjectReviewStatus { offen, in_bearbeitung, zur_validierung, validiert, zurueckgewiesen }` + `reviewComment`, `reviewerId`.
- Regel „Nur `validiert` zählt als bestätigt" gilt für alle Wizard-Gates.
- RBAC-Recht `validate_objects` + klonbare Rolle `external_validator`.
- **Feld-Konvention (Gate-Quelle):** In A1-1 trägt der Schritt-Datensatz `OnboardingProgress` den Review-Status;
das „Weiter"-Gate liest den Status des Vorgänger-Schritts. Sobald Domänenobjekte je Schritt existieren
(spätere Stories), wandert die Statusquelle auf das jeweilige Primärobjekt (Generalisierung = A3).
→ **B nimmt das für seine Schritte (2 context, 4 policies) so an:** ✅ **ja** — B liest für `context` (2) und `policies` (4)
den Review-Status des Vorgänger-Schritts aus `OnboardingProgress` (Quelle A1-1); „nur `validiert` zählt". B schreibt in diesen Schritten
nur Fakten/Flags bzw. Richtlinien-Objekte, **nicht** den Gate-Status selbst. Hinweis: Schritt 4 nutzt weiterhin den bestehenden
`policy_approval`-Workflow für die inhaltliche Freigabe; das Wizard-„Weiter"-Gate hängt aber am `OnboardingProgress`-Status, nicht am Task.
**§3 Flags (Owner B):** A übernimmt die Namen aus §3 **unverändert**, benennt nichts um, behandelt
`FLAG_ELEVATED_PROTECTION` als abgeleitet (nie manuell). A entwickelt `scope-filter.ts` gegen diese Namen,
**bevor** F4 landet. → **B bestätigt Namensliste final:** ✅ **final** — die 17 Namen bleiben unverändert. Ist-Stand geprüft:
**14 bereits** in `variables.schema.json` vorhanden; F4 ergänzt **genau die 3 fehlenden** `FLAG_PROTOTYPE_PROTECTION`, `FLAG_ISB_EXTERNAL`,
`FLAG_ISB_INTERNAL` (+ fehlende `ROLE_*/TECH_*` aus C2, **ohne** FLAG-Namen anzufassen). `FLAG_ELEVATED_PROTECTION` bleibt abgeleitet. Nach F4: `_verify.py` → OK.
**§4 Prüfziele:** A bestätigt `informationssicherheit | prototypenschutz | datenschutz`; Kap. `8.x` nur bei
Prototyp, `9.x` nur bei Datenschutz (C1-Methodik).
**§5 Step-Registry (Owner A) — A bestätigt & stellt bereit:**
- Signatur `registerStep({ key, title, order, guard, component })`, Keys/Reihenfolge
`1 scoping · 2 context · 3 roles · 4 policies · 5 assets · 6 risks · 7 controls · 8 gap · 9 readiness`.
- A liefert die stabile Registry-Schnittstelle in A1-1, **bevor** B `context` (2) und `policies` (4) einklinkt.
→ **B bestätigt Schnittstelle als ausreichend:** ✅ **ausreichend** — `registerStep({ key, title, order, guard, component })` + Keys 1–9
genügen, um `context` (2) und `policies` (4) einzuklinken. Einzige Bitte an A: `guard` muss das **Ausblenden** eines Schrittes erlauben
(z. B. `policies` nur bei aktivem Richtlinien-Modul) und die `component`-Client/Server-Boundary sauber halten. Keine weiteren Felder von B benötigt.
**§6 Migrations-Reihenfolge — A-Vorschlag:**
| # | Story | Owner | Migration | Anmerkung |
|---|-------|-------|-----------|-----------|
| 1 | A1-1 | A | `onboarding_progress` | **A erzeugt die erste Migration** |
| 2 | F1 | B | `tasks_wizard_fields` | B rebast danach zuerst auf `dev` |
| 3 | F2 | A | `object_review_status` (Enum + `external_validator`) | |
| 4 | A2-2 | A | `wizard_scope` | |
| 5 | B3 | B | `wizard_facts` | |
→ **Wer erzeugt die erste Migration? A-Vorschlag: Dev A (A1-1).** B bestätigt: ✅ **ja** — Dev A erzeugt `onboarding_progress` zuerst;
B rebast danach zuerst auf `dev` und erzeugt dann `tasks_wizard_fields` (F1).
→ Reihenfolge oben bestätigt/angepasst: ✅ **bestätigt (unverändert)** — A1-1(A) → F1(B) → F2(A) → A2-2(A) → B3(B); je Branch genau eine Migration, nie gleichzeitig.
**Nicht-blockierend für A1-1** (betrifft spätere A-Stories bzw. B/Berater): neue `FLAG_*` in
`variables.schema.json` (F4), `seed/scoping/c1-scope.json` erzeugt A aus der C1-Tabelle (445 Zeilen),
P01/D01/VA-20 + Baseline-Normalisierung (B4/Berater), ISB-Freigabe (fachlich).
@@ -1,82 +0,0 @@
# Umsetzungspaket **Dev A** — Wizard-Fundament, Scoping & AL2/AL3 zentral
> Claude-Code-Prompt für Entwickler A. Parallel zu **Paket Dev B**. Basis-Branch **`dev`**. Fachcontent: `Fachcontent_Wizard_C1-C9/C1_Scoping-AL-Pruefziel.md`. Repo-Doku: `docs/HANDOVER-DEV.md`, `STAND-dev-branch.md`.
## Auftrag (Kurz)
Baue das **Wizard-Grundgerüst**, den **Scoping-Schritt** und verlege den **AL2/AL3-Schalter zentral ins Admin-/Superadmin-Portal**. Du lieferst die Klammer, in die Dev B seine Schritt-Inhalte einklinkt.
## Branches (unter `dev` abzweigen, PR-Ziel `dev`)
- `dev/a1-wizard-shell`
- `dev/a2-scoping-admin-al`
Häufig auf `dev` rebasen. Kleine PRs je Story.
## Datei-Hoheit (nur DU fasst diese an)
`src/app/(app)/onboarding/**` · `src/server/actions/onboarding.ts` · `src/lib/scope-filter.ts` · `src/app/(platform)/admin/**` (AL-Einstellung) · `src/lib/modules.ts` (nur Eintrag `onboarding`) · Scope-/Progress-Modelle in `prisma/schema.prisma`.
**Koordiniert (mit Dev B abstimmen, siehe Contracts-Anhang):** `prisma/schema.prisma` (Migrations-Reihenfolge), `scripts/check-module-guards.ts` (deine neuen Actions eintragen), Validierungsstatus-Enum.
---
## Story A1 — Wizard-Shell & Navigation (`dev/a1-wizard-shell`)
**A1-1 Step-Registry & State-Machine (L)**
- Neues Modul **`onboarding`** in `src/lib/modules.ts` (Default aktiv für Bestands-Tenants).
- Bereich `src/app/(app)/onboarding/**` mit einer **Step-Registry**: Schritte registrieren sich mit `{ key, title, order, guard, component }`. Dev B klinkt „context" (Schritt 2) und „richtlinien" (Schritt 4) ein — definiere die Registry-Schnittstelle stabil (Contracts-Anhang §5).
- **State-Machine**: `offen → in_bearbeitung → zur_validierung → validiert` je Schritt; **Gate**: „Weiter" ist gesperrt, solange das Vorgänger-Objekt nicht `validiert` ist (nutzt Validierungsstatus, Contracts §2).
- **Persistenter Fortschritt** je Mandant: Modell `OnboardingProgress` (tenant-gebunden, `TENANT_MODELS`+RLS), Migration `onboarding_progress`.
- Server-Action-Datei `src/server/actions/onboarding.ts` über `moduleGuard("onboarding")`; in `scripts/check-module-guards.ts` eintragen.
- **AK:** Fortschritt resumierbar; Gate blockiert korrekt; Schritte via Registry einklinkbar; `build`/Guard-Check grün.
**A1-2 Dashboard-Kachel (S)**
- Kachel „Onboarding-Fortschritt" in `dashboard/page.tsx` analog der bestehenden Freigabe-Kachel (Anteil erledigter Schritte, nächster offener Schritt).
---
## Story F2 (dein Anteil der Foundation) — Validierungsstatus-Enum (`dev/a1-wizard-shell`)
- Lege das **wiederverwendbare Enum** `ObjectReviewStatus` + Feld-Konvention an (Contracts §2), das Wizard-Gates und später A3 nutzen. RBAC-Recht `validate_objects`; Rolle **`external_validator`** ergänzen.
- **AK:** Enum + Recht vorhanden; Wizard-Gate liest den Status; Rolle im RBAC klonbar.
- **Hinweis:** Die vollständige Generalisierung über alle Objekttypen ist Story A3 (späterer Branch) — hier nur Enum + Wizard-Nutzung.
---
## Story A2 — Scoping + AL2/AL3 zentral im Adminportal (`dev/a2-scoping-admin-al`)
**A2-1 AL2/AL3 zentral (M)** — *explizite Anforderung*
- Der Assessment-Level (AL2/AL3) wird **ausschließlich** im **Admin-/Superadmin-Portal je Mandant** gesetzt → **einzige Quelle der Wahrheit**. UI in `src/app/(platform)/admin/**`, Action in `src/server/actions/admin.ts`/`platform.ts`.
- Mandanten-`/settings` und der Scoping-Schritt zeigen AL nur **read-only** an (kein Änderungsrecht mehr im Tenant).
- AL treibt die bestehenden Flags `FLAG_HIGH_PROTECTION`/`FLAG_VERY_HIGH_PROTECTION` und den vorhandenen Coverage-Filter (Coverage zeigt bei AL2 keine „sehr hoch"-Controls — Verhalten beibehalten, Quelle umziehen).
- **AK:** AL ist nur im Adminportal änderbar; Änderung propagiert in Coverage + Zusatzanforderungen; Tenant kann AL nicht mehr selbst setzen.
**A2-2 Scope-Objekt + Filter-Engine (M)**
- Scoping (Schritt 1) erfasst: **Prüfziele** (Informationssicherheit / Prototypenschutz / Datenschutz), Standorte, Geltungsbereich, Ausschlüsse → Modell `WizardScope` (tenant-gebunden, RLS).
- **`src/lib/scope-filter.ts`**: gegeben (AL, gesetzte `FLAG_*`, aktive Prüfziele) → liefert die **aktiven Anforderungen**. Datengrundlage: **C1-Tabelle** (412 Zeilen: `Control · Anforderungs-ID · Typ · AL2 · AL3 · Prüfziel · Scope-Bedingung(Flag)`), als Seed/JSON `seed/scoping/c1-scope.json` einlesen (ID = `mapping.json`-`id`).
- Filterlogik exakt nach C1-Methodik: MUSS/SOLL = AL2+AL3 (SOLL über `FLAG_INCLUDE_SHOULD`); HOCH nur bei `FLAG_HIGH_PROTECTION`; SEHR HOCH nur bei `FLAG_VERY_HIGH_PROTECTION`; Kapitel 8.x nur bei Prüfziel Prototyp, 9.x nur bei Datenschutz.
- **AK / Tests:** Unit-Tests gegen repräsentative C1-Zeilen (z. B. `1.2.2-H1` erscheint nur bei `FLAG_HIGH_PROTECTION`; `1.1.1-S1` nur bei `FLAG_INCLUDE_SHOULD`; `8.*` nur bei Prototyp). Geänderter Scope blendet nachgelagerte Schritte korrekt.
**Fachcontent:** `C1_Scoping-AL-Pruefziel.md`. **Abhängigkeit:** A1 (Shell), Contracts §2 (Status), §3 (Flags-Namen von Dev B/F4 — bis dahin gegen die im Contracts-Anhang fixierten Namen entwickeln).
---
## Definition of Done (jede Story)
`npx tsc --noEmit` → `npm run lint` → `npm run build` (Guard-Check) grün · neue Action in `check-module-guards.ts` · neue tenant-Modelle in `TENANT_MODELS`+RLS+Migration (RLS-DO-Block) · Browser-Verifikation · Demo-Seed lauffähig.
## Reihenfolge
A1-1 → F2 → A1-2 → A2-1 → A2-2. Danach (Folge-Paket): A3 (Validierung generalisieren), A5/A6/A7/A8.
---
## Contracts-Anhang (identisch in Paket A und B — an der Naht abstimmen)
**§1 Task-Objekt (Dev B besitzt das Modell, du konsumierst es):**
`Task { type: 'document_create'|'evidence_provide'|'technical'|'organizational'|'validation'|'policy_approval', owner, dueDate, priority: 'hoch'|'mittel'|'niedrig', status, resources: {tool,budget,personnel,time}, origin, links: {control?,risk?,document?,asset?} }`. Gates/Trigger erzeugen Aufgaben über die Action von Dev B.
**§2 Validierungsstatus (du besitzt das Enum):**
`enum ObjectReviewStatus { offen, in_bearbeitung, zur_validierung, validiert, zurueckgewiesen }` + `reviewComment`, `reviewerId`. Rolle `external_validator`. „Nur `validiert` zählt als bestätigt."
**§3 Flags (Dev B pflegt `variables.schema.json`, Namen sind fix):**
`FLAG_INCLUDE_SHOULD, FLAG_HIGH_PROTECTION, FLAG_VERY_HIGH_PROTECTION, FLAG_ELEVATED_PROTECTION, FLAG_PERSONAL_DATA, FLAG_PROTOTYPE_PROTECTION, FLAG_CLOUD_USED, FLAG_AI_USED, FLAG_OT_USED, FLAG_DEV_INHOUSE, FLAG_MOBILE_WORK, FLAG_MOBILE_DEVICES, FLAG_CRYPTO_PKI, FLAG_EXTERNAL_IT, FLAG_CUSTOMER_SYSTEMS, FLAG_ISB_EXTERNAL, FLAG_ISB_INTERNAL`.
**§4 Prüfziele:** `informationssicherheit | prototypenschutz | datenschutz`.
**§5 Step-Registry (du definierst, B konsumiert):** `registerStep({ key, title, order, guard, component })`; Schritt-Keys: `1 scoping · 2 context · 3 roles · 4 policies · 5 assets · 6 risks · 7 controls · 8 gap · 9 readiness`.
**§6 Migrations-Protokoll:** Vor dem Erzeugen einer Migration auf `dev` rebasen; Migrationen nie gleichzeitig ohne Absprache erstellen; je Branch genau eine Migration pro Story.
@@ -1,100 +0,0 @@
# Umsetzungspaket **Dev B** — Aufgaben, Regel-Engine, Fragebogen & Richtlinien-Import/Upload
> Claude-Code-Prompt für Entwickler B. Parallel zu **Paket Dev A**. Basis-Branch **`dev`**. Fachcontent: `C2_Fragenkatalog-Wirkung.md`, `C3_Vorlagen-Annotation.md`, `C0_README` (offene Punkte). Repo-Doku: `docs/HANDOVER-DEV.md`, `STAND-dev-branch.md`.
## Auftrag (Kurz)
Baue die **Regel-Engine**, den **Fragebogen**, die **Aufgaben-Erzeugung** und die **Richtlinien: Import bei Modul-Aktivierung + manueller Upload**. Du lieferst die Inhalte/Engines, die sich in die Wizard-Shell von Dev A einklinken.
## Branches (unter `dev` abzweigen, PR-Ziel `dev`)
- `dev/b1-tasks-erweiterung`
- `dev/b2-regel-engine`
- `dev/b3-fragebogen`
- `dev/b4-richtlinien-import-upload`
Häufig auf `dev` rebasen. Kleine PRs je Story.
## Datei-Hoheit (nur DU fasst diese an)
`src/server/actions/tasks.ts` (Task-Modell/Logik) · `src/lib/rules/**` · `seed/isms-vorlagenpaket-v2/variables.schema.json` · Fragebogen unter `src/app/(app)/onboarding/steps/context/**` und `.../policies/**` (in Dev-A-Registry eingeklinkt) · `prisma/import-policies.ts` · `src/app/(app)/policies/**` (Upload/Import-Einstieg) · `seed/isms-vorlagenpaket-v2/` (P01/D01/VA-20).
**Koordiniert (mit Dev A abstimmen):** `prisma/schema.prisma` (Migrations-Reihenfolge), `scripts/check-module-guards.ts`, Step-Registry-Schnittstelle (§5).
---
## Story F1 — Aufgaben-Objekt-Schema erweitern (`dev/b1-tasks-erweiterung`)
- Erweitere das bestehende `Task`/`TaskComment` (heute Typ `policy_approval`) um die Felder aus Contracts §1 (`type`, `owner`, `dueDate`, `priority`, `status`, `resources` JSON, `origin`, polymorphe `links`). Bestehender Freigabe-Flow bleibt lauffähig.
- Migration `tasks_wizard_fields`; `TENANT_MODELS`+RLS bleiben.
- **AK:** neue Felder vorhanden/typisiert; `policy_approval`-Flow unverändert grün.
## Story B1 — Auto-Generierung mit Bestätigung (`dev/b1-tasks-erweiterung`)
- Aus Triggern (Schritt 3/4/6/7/8 + Zurückweisung) **Aufgabenvorschlag** erzeugen (Vorschlag → Bearbeiter bestätigt/verwirft). Trigger-Set exakt aus **C2 §8** (z. B. „ISB nicht benannt" → `organizational`/Control 1.2.2; „externe IT-Dienstleister = ja" → `evidence_provide`/Control 6.1.1; „kein Restore-Test" → `technical`/BL-OPS-06/Control 5.2.9).
- Anzeige/Filter im Modul „Aufgaben" (`src/app/(app)/tasks/`), Ressourcenfelder editierbar.
- **AK:** Trigger erzeugt korrekt verknüpften Vorschlag; Bestätigung übernimmt; Verwerfen dokumentiert.
---
## Story F4 — Variablen/Flags erweitern (`dev/b2-regel-engine`)
- In `seed/isms-vorlagenpaket-v2/variables.schema.json` ergänzen: `FLAG_PROTOTYPE_PROTECTION`, `FLAG_ISB_EXTERNAL`, `FLAG_ISB_INTERNAL` + fehlende `ROLE_*`/`TECH_*` aus C2. Namen = Contracts §3 (fix, da Dev A darauf entwickelt).
- **AK:** `python3 seed/isms-vorlagenpaket-v2/_verify.py` → **OK**.
## Story F3 + B2 — Regel-/Mapping-Engine (`dev/b2-regel-engine`)
- **DSL** (Contracts §7) als Typen in `src/lib/rules/dsl.ts`; **Engine** in `src/lib/rules/engine.ts`: `Bedingung(Antwort|Scope|Flag) → Wirkung(Klausel ein/aus · Control relevant · Risiko/Asset-Bezug · Aufgabe)`.
- Koppelt an vorhandene `{{#if FLAG}}`-Render-Flags und die Lieferanten-Anforderungs-Engine (Muster).
- **Regeldaten** aus **C2 §5** (Q-FEAT-01…10) als Regelobjekte hinterlegen (z. B. `FLAG_CLOUD_USED` → R12-Klauseln + Controls 5.3.2/5.3.4/6.1.3 + Risiken R-CLOUD-*).
- **AK / Tests:** Unit-Tests, die für jede Q-FEAT-Antwort die erwarteten Wirkungen prüfen; Antwortänderung propagiert deterministisch.
## Story B3 — Fragebogen/Fakten (Schritt 2) (`dev/b3-fragebogen`)
- Dynamischer, **bedingter** Fragebogen mit Abschnitten **A–F aus C2** (Antworttypen + Anzeige-Bedingungen); Antworten als **wiederverwendbare Faktenobjekte** (Migration `wizard_facts`, RLS).
- Abschnitt **E** (Baseline) als **vorbelegte Defaults** aus `Technische-Sicherheits-Baseline.md` — nur bestätigen/anpassen, keine Ersteingabe.
- Zentrale Variablen bleiben serverseitig gesperrt (nur `/settings`, siehe `src/lib/policy-variables.ts`) — Fragebogen schreibt Fakten/Flags, nicht die gesperrten Variablen.
- Einklinken über Dev-A-Step-Registry (§5), Schritt-Key `context`.
- **AK:** Fragen A–F vorhanden; bedingte Sichtbarkeit über Flags; Antwort propagiert in abhängige Objekte (via B2); Baseline-Defaults vorbelegt.
---
## Story B4 — Richtlinien: Import bei Aktivierung + manueller Upload (`dev/b4-richtlinien-import-upload`)
**B4-1 Vorlagen-Import bei Modul-Aktivierung (M)** — *explizite Anforderung*
- Aktiviert der Superadmin das **Richtlinien-Modul** für einen Mandanten, wird das Vorlagenpaket **mandantenweit nicht-destruktiv importiert**: kapsle die bestehende Logik `prisma/import-policies.ts` (Diff/Upsert/`archivedAt`, `{dryRun}`) als **serverseitig aufrufbare Action/Job**. Zusätzlich Button „Vorlagen importieren/aktualisieren" in Admin **und** unter `/policies`.
- **AK:** Modul-Aktivierung triggert Import; idempotent; Status/Freigabe/Overrides/Variablenwerte bleiben erhalten; Änderungsreport sichtbar.
**B4-2 Manueller Upload eigener Richtlinien im Menü (M)** — *explizite Anforderung; Storage später*
- Unter `/policies` Einstieg „Eigene Richtlinie hochladen" mit **Pflicht-Control-Zuordnung** (Multi-Select aus Control-Katalog, optional Anforderungs-IDs) gemäß **C3 §4**.
- Datei-Persistenz über ein **gestubbtes Storage-Adapter-Interface** `src/server/storage/adapter.ts` (echtes Backend = Folge-Epic S1). Upload legt Metadaten + Block-Modell + Control-Mapping an; die Zuordnung fließt wie ein `<!-- REQ -->`-Anker in die Nachweislage (Schritt 7).
- **AK:** „Eigene Richtlinie hochladen" vorhanden mit Control-Zuordnung; Storage-Adapter gekapselt (später ohne UI-Änderung verdrahtbar); hochgeladenes Dokument erscheint in der Coverage/Nachweislage.
**B4-3 Proto/DS-Vorlagen P01/D01 + VA-20 + mapping.json (M)**
- Neue Vorlagen `P01_Prototypenschutz.md` (+ Verfahren `VA-20_Prototypen-Zutritt-und-Transport`) und `D01_Datenschutz.md` nach **C3 §2-Konvention** anlegen; `mapping.json`-Einträge im gleichen Schema für 8.x/9.x (IDs aus C1/C6-Dekomposition); Prototyp-Klauseln in `{{#if FLAG_PROTOTYPE_PROTECTION}}`.
- `_verify.py` um die neuen Verzeichnisse/Anker erweitern.
- **AK:** `python3 _verify.py` → **OK**; neue Anforderungen erscheinen im Scope, wenn Prüfziel Prototyp/Datenschutz aktiv (Dev-A-Filter).
**Fachcontent:** `C3` (Annotationskonvention + P01/D01), `C2` (Flag-Wirkung), `C0` (offene Punkte 1–3).
---
## Definition of Done (jede Story)
`npx tsc --noEmit` → `npm run lint` → `npm run build` (Guard-Check) grün · neue Action in `check-module-guards.ts` · neue tenant-Modelle in `TENANT_MODELS`+RLS+Migration (RLS-DO-Block) · bei Seed-Änderung `_verify.py` → **OK** · Browser-Verifikation.
## Reihenfolge
F1 → B1 → F4 → (F3+B2) → B3 → B4-1 → B4-2 → B4-3. Danach (Folge-Paket): A6-Risikokatalog-Anbindung (C4), B5 Umsetzungshinweise (C6), B6 Versionierung, B7 Export (C9).
---
## Contracts-Anhang (identisch in Paket A und B — an der Naht abstimmen)
**§1 Task-Objekt (du besitzt das Modell):**
`Task { type: 'document_create'|'evidence_provide'|'technical'|'organizational'|'validation'|'policy_approval', owner, dueDate, priority: 'hoch'|'mittel'|'niedrig', status, resources: {tool,budget,personnel,time}, origin, links: {control?,risk?,document?,asset?} }`. Dev A/Wizard-Gates rufen deine `createTask`-Action zum Erzeugen von Aufgaben.
**§2 Validierungsstatus (Dev A besitzt das Enum, du konsumierst):**
`enum ObjectReviewStatus { offen, in_bearbeitung, zur_validierung, validiert, zurueckgewiesen }` + `reviewComment`, `reviewerId`. Rolle `external_validator`.
**§3 Flags (du pflegst `variables.schema.json`, Namen sind fix):**
`FLAG_INCLUDE_SHOULD, FLAG_HIGH_PROTECTION, FLAG_VERY_HIGH_PROTECTION, FLAG_ELEVATED_PROTECTION, FLAG_PERSONAL_DATA, FLAG_PROTOTYPE_PROTECTION, FLAG_CLOUD_USED, FLAG_AI_USED, FLAG_OT_USED, FLAG_DEV_INHOUSE, FLAG_MOBILE_WORK, FLAG_MOBILE_DEVICES, FLAG_CRYPTO_PKI, FLAG_EXTERNAL_IT, FLAG_CUSTOMER_SYSTEMS, FLAG_ISB_EXTERNAL, FLAG_ISB_INTERNAL`.
**§4 Prüfziele:** `informationssicherheit | prototypenschutz | datenschutz`.
**§5 Step-Registry (Dev A definiert, du konsumierst):** `registerStep({ key, title, order, guard, component })`; du lieferst Komponenten für `context` (Schritt 2) und `policies` (Schritt 4).
**§6 Migrations-Protokoll:** Vor dem Erzeugen einer Migration auf `dev` rebasen; Migrationen nie gleichzeitig ohne Absprache erstellen; je Branch genau eine Migration pro Story.
---
## Gemeinsamer Kickoff (15 Min., beide Entwickler, vor Story-Start)
Contracts §1–§6 gemeinsam bestätigen (Feldnamen, Enum-Werte, Flag-Namen, Registry-Signatur, Migrations-Reihenfolge). Danach arbeiten beide Pakete konfliktarm parallel; einzige echte Nahtstellen sind `schema.prisma`, `check-module-guards.ts` und die Step-Registry — alle drei sind im Contracts-Anhang fixiert.
@@ -1,68 +0,0 @@
# Anweisung an den Berater — Fachcontent-Zulieferung (parallel zur Entwicklung)
Der Onboarding-Wizard hat seinen größten Engpass **nicht im Code, sondern im Fachcontent**. Die folgenden Arbeitspakete **C1–C9** laufen **parallel** zur Entwicklung und schalten jeweils eine oder mehrere Entwickler-Epics scharf. Bitte im angegebenen **Format** liefern und die **„muss vorliegen bis"**-Reihenfolge einhalten — sonst werden C2/C3/C4/C5/C6 zum kritischen Pfad.
> Bezug: Entwickler-Epics siehe `Wizard-Entwickler-Backlog.md`. Bestehendes Vorlagenpaket: `seed/isms-vorlagenpaket-v2/` (34 Dokumente, `mapping.json`, `_verify.py`).
---
## Übersicht (was schaltet was frei)
| WP | Inhalt | Schaltet frei | Format | Muss vorliegen bis |
|---|---|---|---|---|
| **C1** | AL2/AL3-Kennzeichnung + Prüfziel-Zuordnung je Control | A2 (Scoping/Filter) | Tabelle/CSV je Control | vor M2 |
| **C2** | Fragenkatalog + Antwort→Wirkung-Mapping | B2 Regel-Engine, B3 Fragebogen | strukturierte Tabelle | vor M1/M2 |
| **C3** | Regelfähige Vorlagen-Auszeichnung | B4 Richtlinien | Annotation im Vorlagenpaket | vor M2 |
| **C4** | VDA-/Standard-Risiko-Katalog + Standardmaßnahmen | A6 Risiko | Tabelle/CSV | vor M4 |
| **C5** | Reifegrad-Logik je Control | A7 Control-Assessment | Regeltabelle | vor M5 |
| **C6** | Umsetzungshinweise je Teilanforderung | B5 + A7 | strukturierter Text je Anforderung | fortlaufend, Kern vor M3 |
| **C7** | ISMS-Soll-Rollenmodell + Funktionstrennung | A4 Rollen | Regelliste + Vorlagen | vor M3 |
| **C8** | Priorisierungslogik Gap + Quick-Wins | A8 Gap | Regeltext | vor M6 |
| **C9** | Auswertungs-/Interpretationstexte + Export-Layout | B7 Readiness | Text + Layout-Skizze | vor M6 |
---
## Arbeitspakete im Detail
### C1 — Scoping-Grundlage (→ A2)
Je VDA-ISA-Control angeben: Zutreffen bei **AL2 / AL3** (SEHR HOCH), Zuordnung zu **Prüfzielen** (Info-Sicherheit / Prototypenschutz / Datenschutz), typische **Ausschluss-/Scope-Regeln**.
**Format:** eine Zeile je Control/Teilanforderung mit Spalten `Control-ID · Typ (MUSS/SOLL/HOCH/SEHR HOCH) · AL2? · AL3? · Prüfziel · Scope-Bedingung`. (Baut auf den vorhandenen Flags/Typen im `mapping.json` auf.)
### C2 — Fragenkatalog + Antwort→Wirkung-Mapping (→ B2, B3)
Der geführte Fragebogen (Schritt 2) **und** die Regel-Engine hängen hieran. Je Frage: Text, Antworttyp, **Bedingung** (wann anzeigen), und die **Wirkung** jeder Antwort: welche **Variable/Platzhalter** gefüllt wird, welche **Klausel** ein-/ausgeblendet wird, welche **Controls/Assets/Risiken** betroffen sind, ob eine **Aufgabe** entsteht.
**Format:** Tabelle `Frage-ID · Frage · Antwortoptionen · Anzeige-Bedingung · Wirkung(Variable / Klausel / Control / Risiko / Aufgabe)`. **Kritischer Pfad — bitte zuerst.**
### C3 — Regelfähige Vorlagen-Auszeichnung (→ B4)
Die 34 Vorlagen so **annotieren**, dass klar ist: welche **Klausel/Baustein** bei welcher **Antwort/Flag** erscheint bzw. entfällt, und welche **Controls** ein Dokument belegt.
**Format:** Auszeichnung direkt im Vorlagenpaket-Stil (bestehende `{{#if FLAG}}`-Mechanik + `mapping.json`-Control-Verknüpfung); danach `python3 _verify.py` → **OK**.
### C4 — Risiko-Katalog (→ A6)
Kuratierter **Standard- und VDA-geforderter Risiko-Katalog**: je Risiko Beschreibung, betroffene **Assets/Controls**, empfohlene **Standardmaßnahmen**, Default-Bewertungshinweis.
**Format:** Tabelle `Risiko-ID · Titel · Beschreibung · Controls · Asset-Typen · Standardmaßnahme(n) · Default-Einschätzung`.
### C5 — Reifegrad-Logik (→ A7)
Regeln, wie aus vorhandenen **Belegen** (Dokument/Risiko/Asset + Validierungsstatus) ein **Reifegrad-Vorschlag** je Control entsteht, und was der **Zielreifegrad** ist. Vorschlag ist regelbasiert, **Bestätigung durch Bearbeiter Pflicht**.
**Format:** Regeltabelle `Control · Bedingung(Belege) · Reifegrad-Vorschlag · Zielreifegrad · offene-Punkt-Kriterium`.
### C6 — Umsetzungshinweise (→ B5, A7)
Je **Teilanforderung** ein Hinweis mit: **organisatorischer** Umsetzungsoption, **technischer** Option, typischen **Nachweisen**, passender **Vorlage** (Verweis), **Ressourcenindikation** (Tool/Personal/Budget/Zeit). Filterbar nach AL2/AL3.
**Format:** strukturierter Block je Anforderungs-ID (Felder org/tech/Nachweise/Vorlage/Ressourcen). **Fortlaufend, Kern-Set vor M3.**
### C7 — ISMS-Soll-Rollenmodell + Funktionstrennung (→ A4)
Soll-Rollen (GF, ISB, DSB, IT-Verantwortung), **Funktionstrennungs-Regeln** (welche Kombination unzulässig), und die **Bestellungs-/Ernennungs-Vorlagen** (ISB-Bestellung etc.) mit Rollen-Platzhaltern.
**Format:** Regelliste + Vorlagen im Paket-Stil (Platzhalter).
### C8 — Priorisierungslogik Gap (→ A8)
Wie offene Punkte priorisiert werden (Muss-/AL3-kritisch = hoch), Dedup-Kriterien, Definition **Quick-Wins**.
**Format:** kurzer Regeltext + Beispiel-Priorisierung.
### C9 — Auswertung & Export (→ B7)
Interpretationstexte fürs Reifegrad-Dashboard, empfohlene nächste Schritte vor dem Assessment, und das **Layout des VDA-ISA-Katalog-Exports** (Reihenfolge, Felder, „bestätigt/unbestätigt").
**Format:** Textbausteine + Layout-Skizze.
---
## Prozess-Hinweise
- Zulieferung **iterativ** je Kapitel/Control-Gruppe möglich (nicht „alles auf einmal") — die Entwicklung kann teilbefüllt starten.
- **ISB-Freigabe** der neuen/angepassten Texte (VA/Richtlinien) ist ein eigener fachlicher Schritt (steht bereits offen auf `dev`).
- Alle Vorlagen-/Katalog-Änderungen nach dem Einspielen mit **`python3 seed/isms-vorlagenpaket-v2/_verify.py` → OK** gegenprüfen.
@@ -1,120 +0,0 @@
# Onboarding-Wizard — Machbarkeitsanalyse (Product Owner)
Bewertung des Berater-Fahrplans (`Onboarding_Wizard_Fahrplan_Detail.md`) gegen den dokumentierten Funktionsstand (`HANDOVER-PM.md`, Stand 2026-07-20). Ziel: Umsetzbarkeit, Wiederverwendung vorhandener Funktionen, neue Funktionen, Komplexitäten — als Grundlage für die anschließende Aufgaben-/Epic-Definition.
**Status-Legende:** ✅ Vorhanden (direkt nutzbar) · 🟡 Teilweise (erweitern) · 🟥 Neu (bauen)
**Aufwand:** S ≤ 1 Tag · M 2–4 Tage · L > 1 Woche (Entwicklung; Fachcontent separat)
---
## 1. Gesamturteil
**Der Wizard ist umsetzbar — und sitzt auf einem für dieses Vorhaben ungewöhnlich starken Fundament.** Rund zwei Drittel der fachlichen Bausteine existieren bereits produktiv (Assets/BIA, Risikoanalyse, Richtlinien-/Template-Engine mit Control-Mapping, importierter VDA-ISA-Katalog mit 316 Anforderungen/45 Controls, AL2/AL3-Schalter, Vier-Augen-Freigabe, Lieferanten-Reifegrad-/Gate-Logik, Coverage-Matrix, Audit-Log, Mandantenfähigkeit).
Der Wizard ist damit **weniger „neues Modul" als vielmehr eine geführte Orchestrierung vorhandener Module** plus einige neue Quer­schnitts-Engines. Der eigentliche Aufwand liegt — wie der Fahrplan selbst richtig betont — **nicht in der Software, sondern im Fachcontent** (regelfähige Vorlagen, VDA-Risiko-Katalog, Umsetzungshinweise, Reifegrad-Logik). Das ist der kritische Pfad und zugleich die Stelle, an der die GEFIM-Projekt-DNA (~100 Projekte) zum Tragen kommt.
**Drei Dinge müssen früh und sauber gebaut werden, sonst werden sie später teuer nachgezogen:** (1) ein generisches Aufgaben-Modul, (2) der generische Validierungs-Workflow, (3) der Regel-/Mapping-Layer. Sie sind die Klammer um alle Schritte.
---
## 2. Querschnittsmechaniken (Kap. 2 des Fahrplans)
| Mechanik | Status | Vorhanden (wiederverwendbar) | Neu zu bauen | Aufwand | Risiko |
|---|:--:|---|---|:--:|---|
| **2.1 Validierungs-Workflow** | 🟡 | Vier-Augen-Freigabe Richtlinien (Entwurf→In Freigabe→Freigegeben), Lieferanten-ISB-Reifegrad-Freigabe, RBAC, Audit-Log | **Generisches** Status-/Review-Modell über alle Objekttypen (Modul/Richtlinie/Risiko/Control-Bewertung), Rolle „externer Berater" als Validierer, Kommentare, „unbestätigt zählt nicht" in der Auswertung | M | Querschnitt — früh bauen, nicht anflanschen |
| **2.2 Umsetzungshinweise** | 🟡→🟥 | `implementation`-Feld je Anforderung im Katalog, Anwender-Handbuch (kuratiert, Deep-Links) | Strukturiertes Hinweis-Modell (org/tech/Nachweise/Vorlagen-Verweis/**Ressourcenindikation**), Scope-/Antwort-Filter, Inline-Panel | Backend S · FE S · **Content L** | Content-Flaschenhals (SME) |
| **2.3 Maßnahmen → Aufgaben-Modul** | 🟡 | Maßnahmen-Kanban (risikobezogen), berechnetes Restrisiko | **Generisches** Aufgaben-Objekt (Typ/Verantwortlich/Fälligkeit/Priorität/**Ressourcen**/Herkunft/Verknüpfung Control·Risiko·Dok·Asset), Auto-Vorschlag aus Triggern + Bestätigung | M–L | **Keystone** — alle Schritte hängen daran |
| **2.4 Audit-Trail & Versionierung** | 🟡 | Audit-Log vorhanden | Echte Versionierung (Richtlinien-Diff ist offen), Versionierung von Katalog/Templates/Risiko-Katalog + Instanz-Referenz + Update-Propagation | M–L | Verzahnt mit offener „Versionierung+Diff" & destruktivem Re-Import |
---
## 3. Fachliche Bausteine / Datenobjekte (Kap. 3)
| Baustein | Status | Vorhanden | Neu / Lücke | Aufwand |
|---|:--:|---|---|:--:|
| Control-Katalog VDA ISA | ✅ | Import 316 Anforderungen / 45 Controls, je Anforderung adressierbar, Typen MUSS/SOLL/HOHER/SEHR HOHER | Ggf. explizite AL2/AL3-Kennzeichnung je Anforderung sichtbar machen (Flags vorhanden) | S |
| Template-Bibliothek | ✅ | 28 Dokumente, Platzhalter/Variablen, optionale Klauseln via `{{#if}}`, Control-Verknüpfung, Freigabe | Regel-Tagging über Feature-Flags hinaus (Scope/Antwort-getrieben) | S–M |
| Upload eigener Dokumente | 🟡 | (geplant) Word-Upload als Block-Modell + Anhang, versioniert | Control-Zuordnung des Uploads in die Nachweislage; Upload selbst ist noch offen | M |
| Fakten-/Fragemodell | 🟡 | Variablen-Pflegestelle, Stammdaten (Settings) → ISMS-Variablen | **Dynamischer, bedingter Fragebogen** als wiederverwendbare Faktenobjekte | M |
| Risiko-Katalog | 🟡 | Bedrohungs-/Schwachstellen-Kataloge, Risikoregister, Control-Verknüpfung | Kuratierter **Standard-/VDA-Risiko-Katalog** (vordefiniert, erweiterbar) | Backend M · **Content L** |
| Regel-/Mapping-Layer | 🟡 | Handlebars-Flags, Variablen-Mapping, Lieferanten-Anforderungs-Engine (Schutzbedarf→Stufen) | **Generalisierung:** Antwort → betroffene Controls/Assets/Risiken (nicht nur Dokument-Klauseln) | L |
| Reifegrad-/Gap-Engine | 🟡 | Coverage-Matrix (Control↔Dokument), Lieferanten-Reifegrad-Freigabe, Gate→Risiko | Control-**Scoring aus Belegen** + Markierung offener Teilanforderungen | Backend M–L · SME M |
---
## 4. Die neun Schritte (Kap. 4)
| Schritt | Status | Trägt auf vorhandenem auf | Neu zu bauen | Aufwand |
|---|:--:|---|---|:--:|
| **1 Scoping** | 🟡 | AL2/AL3-Schalter (global + Override), TISAX-Level in Settings | Scope-Objekt (Prüfziele/Standorte/Geltungsbereich/Ausschlüsse) + **Filterung** der Control-/Template-/Risiko-Sets | FE S · Backend M |
| **2 Kontextaufnahme** | 🟡 | Stammdaten→Variablen | Geführter, **bedingter Fragebogen** + wiederverwendbare Faktenobjekte + Propagation | M |
| **3 Rollen & Verantwortlichkeiten** | 🟡 | RBAC/Rollen je Mandant, RACI-Matrix (Lieferanten) | ISMS-Rollenmodell (GF/ISB/DSB/IT), **Funktionstrennungs-Prüfung**, Rollen-Platzhalter in Dokumenten (ISB-Bestellung) | Backend M · FE S |
| **4 Richtlinien & VA** | ✅🟡 | Template-Tailoring (a) voll: variablenbasiert, Klausel-Ein/Ausblendung, Freigabe | (b) Upload+Control-Mapping (Upload offen); (c) „später erstellen" → Aufgabe (hängt an 2.3) | (a) ✅ · (b) M · (c) S |
| **5 Asset-Inventar** | ✅ | Assets & BIA: C/I/A, Schutzbedarf, Eigentümer, Vererbung, Abhängigkeiten, Risiko-Verknüpfung | „unvollständig/kein Eigentümer" → Aufgabe (Trigger) | S |
| **6 Risikomanagement** | ✅🟡 | 5×5-Heatmap, Register, Behandlung, Maßnahmen, Control-Verknüpfung | VDA-Risiko-Katalog-Auswahl; Maßnahme→Aufgabe (2.3); Methodik konfigurierbar (siehe Offen #2) | M |
| **7 Control-Zuordnung & Reifegrad** | 🟡→🟥 | Coverage-Matrix, Beleg-Verknüpfung, Lieferanten-Reifegrad-Muster | **Control-Assessment-Oberfläche** (SoA ist bisher Platzhalter) + Reifegrad-Selbsteinschätzung + Gap-Markierung je Teilanforderung | Backend L · FE L · SME L |
| **8 Gap- & Maßnahmenableitung** | 🟥 | Ergebnisse aus 6/7 | Aggregation/Dedup/Priorisierung → konsolidierte Gap-Liste (hängt an 2.3) | M |
| **9 Assessment-Readiness** | 🟥 | validierte Objekte, Coverage-Daten | Reifegrad-Dashboard + vorausgefüllte VDA-ISA-Katalogsicht + **Export** (koppelt an offenen DOCX/PDF-Export) | Backend M · FE L |
| **Wizard-Shell (implizit)** | 🟥 | — | Geführter Multi-Step-Flow: Zustand, Fortschritt, Gates, Wiederaufnahme, „bestätigt/unbestätigt"-Propagation | M–L |
---
## 5. Was wirklich neu gebaut werden muss (verdichtete Liste)
1. **Wizard-Shell / State-Machine** (Flow, Fortschritt, Gates, Resume). 🟥 M–L
2. **Generisches Aufgaben-Modul** (2.3) — Keystone. 🟡→ M–L
3. **Generischer Validierungs-Workflow** (2.1) inkl. externer-Berater-Rolle. 🟡→ M
4. **Regel-/Mapping-Layer generalisiert** (Antwort→Controls/Assets/Risiken). 🟡→ L
5. **Scope-Objekt + Filter-Engine** (Schritt 1). 🟥 M
6. **Dynamischer Fragebogen** (Schritt 2). 🟥 M
7. **ISMS-Rollenmodell + Funktionstrennung** (Schritt 3). 🟥 M
8. **Control-Assessment / Reifegrad-Gap-Engine** (Schritt 7, SoA-Surface). 🟥 L
9. **Gap-Konsolidierung** (Schritt 8). 🟥 M
10. **Assessment-Readiness-Dashboard + Export** (Schritt 9, koppelt an DOCX/PDF-Export). 🟥 L
11. **Umsetzungshinweis-Content-Modell + Inline-Panel** (2.2). 🟡→ Backend/FE S, Content L
12. **Versionierung/Diff + Update-Propagation** (2.4; löst zugleich offenen destruktiven Re-Import). 🟡→ M–L
**Direkt wiederverwendbar (kaum/kein Neubau):** Assets/BIA · Risikoanalyse (Kern) · Richtlinien-Template-Engine · Control-Katalog VDA-ISA · AL2/AL3-Schalter · Vier-Augen-Freigabe (als Muster) · Coverage-Matrix · Lieferanten-Anforderungs-/Reifegrad-Engine (als Muster) · Audit-Log · Multi-Tenant/RBAC · Stammdaten→Variablen.
---
## 6. Größte Komplexitäten & Risiken (priorisiert)
1. **Fachcontent ist der kritische Pfad** (bestätigt der Fahrplan selbst): regelfähige Vorlagen, VDA-Risiko-Katalog, **Umsetzungshinweise je Teilanforderung**, Reifegrad-Logik. Muss **parallel und früh** von der Beratung erstellt werden — sonst blockiert er M3/M4/M5/M7.
2. **Regel-/Mapping-Engine (Generalisierung).** Heute: Klausel-Flags + Variablen + Lieferanten-Engine. Neu: eine Antwort steuert Dokument-Klauseln **und** Control-Relevanz **und** Risiko-/Asset-Bezug. Architektur-Kernrisiko — Regel-Syntax und Test­barkeit früh festzurren.
3. **Control-Assessment/Reifegrad (Schritt 7).** Das „SoA & Controls"-Modul ist bisher nur Platzhalter; hier entsteht die eigentliche Assessment-Oberfläche. Größter einzelner FE/Backend-Block.
4. **Versionierung & Update-Propagation.** Solange Re-Import destruktiv ist und Richtlinien keine echte Versionierung haben, sind laufende Kundeninstanzen bei Katalog-/Template-Updates gefährdet. Muss vor „Katalog lebt beim Kunden" gelöst sein.
5. **Keystones Aufgaben-Modul & Validierungs-Workflow** früh, sonst teurer Umbau (Schritte 3–9 hängen daran).
6. **Export/North-Star** (vorausgefüllter VDA-ISA-Katalog) koppelt an den offenen DOCX/PDF-Export.
---
## 7. Antworten auf die offenen Entscheidungspunkte des Fahrplans (Kap. 7), PO-Sicht
1. **Upload-Gap-Check:** Start mit **manueller Control-Zuordnung** (deckt sich mit dem bereits geplanten Word-Upload); automatischer Inhalt↔Anforderung-Abgleich später als KI-Ausbaustufe (koppelt an geplanten „KI-Wizard").
2. **Risiko-Methodik:** **mandantenspezifisch konfigurierbar**, aber zuvor die zwei bestehenden Matrizen (5×5 Risikoanalyse / 4×4 FB-80-04 im Richtlinienmodul) **auf eine zentrale Skala vereinheitlichen** (steht bereits als offener Punkt).
3. **Versionierung/Propagation:** **nicht-destruktives Update mit Diff** und expliziter Übernahme je Instanz (löst zugleich den destruktiven Re-Import). Voraussetzung für „Katalog beim Kunden".
4. **Validierungs-Granularität:** **pro Objekt** (einzelne Richtlinie/Risiko/Control-Bewertung) **plus** Modul-Gate — die Freigabe-Logik ist heute schon objektbezogen (Richtlinien) und wird generalisiert.
5. **Aufgaben-Modul:** existiert **nur teilweise** (Maßnahmen-Kanban, risikobezogen) → **muss generalisiert werden**; die Task-Erzeugung ist mitzuplanen (Keystone).
6. **Reifegrad-Vorschlag:** **regelbasiert vorgeschlagen, mit Pflicht zur Bestätigung** durch den Bearbeiter (entspricht dem vorhandenen Lieferanten-Reifegrad-Freigabe-Muster).
---
## 8. Empfohlene Reihenfolge (für die Aufgaben-Definition)
Angelehnt an M1–M7 des Fahrplans, sortiert nach Abhängigkeit und Wiederverwendung:
1. **Querschnitt-Keystones zuerst:** Aufgaben-Modul (2.3), Validierungs-Workflow (2.1), Versionierung/Diff (2.4), Wizard-Shell. *(sonst später teurer Umbau)*
2. **Regel-/Scope-Fundament:** Scope-Objekt + Filter (Schritt 1), Regel-/Mapping-Layer, Fragebogen (Schritt 2).
3. **Wiederverwendung einklinken:** Richtlinien (Schritt 4, größtenteils vorhanden), Assets (Schritt 5, vorhanden), Risiko (Schritt 6, Kern vorhanden + VDA-Katalog).
4. **Neuer Kern:** Control-Assessment/Reifegrad (Schritt 7), Gap-Konsolidierung (Schritt 8).
5. **Output:** Assessment-Readiness-Dashboard + Export (Schritt 9).
6. **Durchgängig parallel:** Fachcontent (SME/Beratung) + Umsetzungshinweise (2.2).
---
## 9. Nächster Schritt
Auf dieser Basis die **Entwickler-Aufgaben als Epics/Stories** definieren — vorgeschlagene Epic-Schnitte:
**E1** Aufgaben-Modul · **E2** Validierungs-Workflow · **E3** Wizard-Shell & Navigation · **E4** Scope & Regel-/Mapping-Layer · **E5** Fragebogen/Fakten · **E6** ISMS-Rollen & Funktionstrennung · **E7** Richtlinien-Anbindung (Upload/Control-Mapping) · **E8** Risiko-Katalog & -Anbindung · **E9** Control-Assessment/Reifegrad-Gap · **E10** Gap-Konsolidierung · **E11** Assessment-Readiness/Export · **E12** Versionierung/Propagation · **E13** Umsetzungshinweise (Content+Panel) · **C1** Fachcontent (querlaufend).
> Offen für die Feinspezifikation: Regel-Syntax, Aufgaben-Objekt-Schema, Reifegrad-Scoring-Formel, Export-Format des VDA-ISA-Katalogs. Diese vier zuerst festzurren — sie determinieren den Rest.
@@ -1,133 +0,0 @@
# Onboarding-Wizard — Entwickler-Backlog (2 Full-Stack-Lanes, parallel auf `dev`)
Grundlage: `Onboarding_Wizard_Fahrplan_Detail.md` (Berater), `STAND-dev-branch.md` (Ist-Stand `dev`, 2026-07-24), `Onboarding-Wizard-Machbarkeitsanalyse.md`.
Aufbau: **2 Entwickler**, je **vertikaler Feature-Slice** (BE+FE+Migration) auf eigenem **Feature-Branch unter `dev`**. Umfang: **kompletter Wizard**. Manueller Richtlinien-Upload: **UI/Modell jetzt, Datei-Speicher später** (Storage-Adapter gestubbt).
> Fachliche Zulieferungen (SME/Berater) sind je Epic mit **🧩 SME** markiert und in `Berater-Anweisung-Fachcontent.md` als Arbeitspakete C1–C9 ausformuliert.
---
## 0. Arbeitsmodell, Branching & Definition of Done
**Branching**
- Basis-Branch: **`dev`** (nicht `main`). Alle Feature-Branches zweigen von `dev` ab, PR-Ziel ist `dev`.
- Namensschema: **`dev/<lane><n>-<slug>`** — z. B. `dev/a1-wizard-shell`, `dev/b2-regel-engine`.
- **Dev A = Lane A** („Flow, Governance & Bewertung"), **Dev B = Lane B** („Engines, Inhalte & Ausgabe").
- Häufig auf `dev` rebasen (mind. täglich), kleine PRs je Epic, Review durch die jeweils andere Person.
**Definition of Done (jede Story)**
- `npx tsc --noEmit` → `npm run lint` → `npm run build` grün (der `prebuild`-Guard-Check läuft mit).
- Neue Server-Action-Datei ist in **`scripts/check-module-guards.ts`** eingetragen (Modul-Key oder `EXEMPT`).
- Neues tenant-gebundenes Modell in **`TENANT_MODELS`** (`src/server/db.ts`) **und** RLS-Policy in der Migration.
- Migration erstellt (Prisma-7-Flow) inkl. manuell angehängtem **RLS-DO-Block**; läuft via `migrate deploy`.
- Wenn Seed/Vorlagenpaket berührt: `python3 seed/isms-vorlagenpaket-v2/_verify.py` → **OK**.
- Browser-Verifikation der Kern-Flows; Demo-Daten/Seed weiterhin lauffähig.
**Geteilte „Hot Files" — nur koordiniert anfassen (Konflikt-Risiko):**
`prisma/schema.prisma` · Migrationsreihenfolge · `src/lib/modules.ts` · `scripts/check-module-guards.ts` · `src/server/db.ts` (`TENANT_MODELS`) · das Wizard-Shell-Step-Registry (Lane A liefert, Lane B registriert Steps).
→ Migrationen **nie gleichzeitig** ohne Absprache erzeugen; jede Lane hält ihre Migration isoliert und rebased vor dem Erstellen.
---
## 1. Zuerst festzurren — Foundation-Contracts (gemeinsam, VOR den Lanes)
Ein kurzer gemeinsamer Branch **`dev/foundation-contracts`** (1 Person federführend, andere reviewt), **zuerst nach `dev` gemergt**. Legt die vier Verträge fest, die alles andere determinieren:
1. **Aufgaben-Objekt-Schema** — Erweiterung des bestehenden `Task`/`TaskComment` (heute Typ `policy_approval`) um: `type` (`document_create` / `evidence_provide` / `technical` / `organizational` / `validation`), `owner`, `dueDate`, `priority`, `status`, **`resources` (JSON: tool/budget/personnel/time)**, **`origin` (auslösender Schritt)**, polymorphe Verknüpfung (`control` / `risk` / `document` / `asset`). RLS wie gehabt.
2. **Objekt-Validierungs-Status** — einheitliches Enum `offen → in_bearbeitung → zur_validierung → validiert | zurueckgewiesen(+Kommentar)` als wiederverwendbares Feld/Mixin über Objekttypen; Rolle **`external_validator`** (externer Berater).
3. **Regel-/Mapping-DSL** — Vertrag: `Bedingung(Antwort/Scope/Flag) → Wirkung(Klausel ein/aus · Control relevant/irrelevant · Risiko/Asset-Bezug · Aufgabe)`. JSON-basiert, testbar; baut auf vorhandenen Handlebars-Flags + Lieferanten-Anforderungs-Engine auf.
4. **Assessment-Readiness-Export-Format** — Datenschema des vorausgefüllten VDA-ISA-Katalogs (Control → Teilanforderungen → Reifegrad/Belege/offene Punkte/Status „bestätigt|unbestätigt").
**Aufwand:** M (Schema-/Typ-Definitionen + Migration `tasks`-Erweiterung). **DoD:** Typen/Interfaces + Task-Migration gemergt, damit beide Lanes darauf bauen.
---
## 2. Lane A — Dev A: „Flow, Governance & Bewertung"
| Epic | Branch | Hängt ab von | Aufwand | 🧩 SME |
|---|---|---|---|:--:|
| **A1 Wizard-Shell & Navigation** | `dev/a1-wizard-shell` | Foundation | M–L | – |
| **A2 Scoping + AL2/AL3 zentral (Adminportal) + Scope-Filter** | `dev/a2-scoping-admin-al` | A1 | M | 🧩 C1 |
| **A3 Validierungs-Workflow generalisieren** | `dev/a3-validation-workflow` | Foundation, B1 | M | – |
| **A4 ISMS-Rollen & Funktionstrennung (Schritt 3)** | `dev/a4-isms-rollen` | A1, A3 | M | 🧩 C7 |
| **A5 Asset-Anbindung Wizard (Schritt 5, Reuse)** | `dev/a5-assets-step` | A1 | S | – |
| **A6 Risiko-Katalog & Anbindung (Schritt 6)** | `dev/a6-risiko-katalog` | A1, B2 | M–L | 🧩 C4 |
| **A7 Control-Assessment & Reifegrad/Gap (Schritt 7, SoA)** | `dev/a7-control-assessment` | A2, B1, B2, B5 | L | 🧩 C5, C6 |
| **A8 Gap-Konsolidierung (Schritt 8)** | `dev/a8-gap-konsolidierung` | A6, A7, B1 | M | 🧩 C8 |
**A1 — Wizard-Shell & Navigation.** Neuer Bereich `src/app/(app)/onboarding/**` + `src/server/actions/onboarding.ts` (in `check-module-guards.ts` registrieren; neues Modul `onboarding` in `src/lib/modules.ts`). Multi-Step-State-Machine mit Fortschritt, **Gates** (Schritt „validiert" bevor weiter), **Wiederaufnahme**, und einem **Step-Registry**, in das Lane B ihre Schritt-Komponenten einklinkt (klare Datei-Trennung je Schritt). Persistenter Wizard-Fortschritt je Mandant. *Akzeptanz:* Flow ist resumierbar; Gates blockieren korrekt; Schritte sind als eigenständige Module registrierbar.
**A2 — Scoping + AL2/AL3 zentral im Adminportal.** (Explizite Anforderung.) Der **AL2/AL3-Schalter wird zentral im Superadmin-/Adminportal** gesetzt (`src/app/(platform)/admin/**` + `src/server/actions/admin.ts`/`platform.ts`) als Teil der Mandanten-Kern-Config — **einzige Quelle der Wahrheit**. Schritt 1 (Scoping) liest ihn **read-only** (nur Superadmin ändert), plus Prüfziele/Standorte/Geltungsbereich/Ausschlüsse → **Scope-Objekt**. Scope filtert nachgelagert Control-/Template-/Risiko-Sets (an vorhandene Coverage-Filter-Logik nach Assessment-Level andocken). *Akzeptanz:* AL nur im Adminportal änderbar; Änderung propagiert in Coverage/Zusatzanforderungen; nicht relevante Controls/Templates ausgeblendet. **🧩 C1** (AL2/AL3-Kennzeichnung + Prüfziel-Zuordnung je Control).
**A3 — Validierungs-Workflow generalisieren.** Den bestehenden Vier-Augen-Freigabe-/Task-Mechanismus (`policy_approval`) zu einem **generischen Objekt-Review** über alle Typen ausbauen (Modul/Richtlinie/Risiko/Control-Bewertung): Status-Feld (Foundation #2), Reviewer-Zuweisung inkl. **externer Berater**, Kommentare, „unbestätigt zählt nicht" in Auswertungen. Wiederverwendet `src/server/actions/tasks.ts` + `submitForApproval`. *Akzeptanz:* jedes Objekt trägt Review-Status; nur „validiert" zählt als bestätigt; externer Validierer-Rolle testbar.
**A4 — ISMS-Rollen & Funktionstrennung (Schritt 3).** ISMS-Rollenmodell (GF/ISB/DSB/IT) auf Basis vorhandener RBAC/RACI; **Funktionstrennungs-Prüfung** (z. B. ISB ≠ IT); Rollen-Platzhalter in Dokumente (ISB-Bestellung aus Vorlage). Konflikt → Hinweis/Aufgabe (A3/B1). *Akzeptanz:* Funktionstrennungs-Konflikt wird erkannt und als Aufgabe ausgewiesen. **🧩 C7**.
**A5 — Asset-Anbindung Wizard (Schritt 5).** Dünne Wizard-Schicht auf das bestehende Assets/BIA-Modul (C/I/A, Schutzbedarf, Eigentümer, Abhängigkeiten sind vorhanden). Trigger „Inventar unvollständig / kein Eigentümer" → Aufgabe (B1). *Akzeptanz:* Wizard nutzt Bestands-Assets; Trigger erzeugt Aufgabe.
**A6 — Risiko-Katalog & Anbindung (Schritt 6).** Bewertungs-/Register-Kern existiert (5×5, Behandlung, Control-Verknüpfung). Neu: **kuratierter Standard-/VDA-Risiko-Katalog** (Auswahl + eigene Ergänzung), Verknüpfung Asset/Control, Maßnahme→Aufgabe (B1). *Akzeptanz:* VDA-geforderte Risiken auswählbar; inakzeptables Risiko hat Behandlung; Maßnahmen erzeugen Aufgaben. **🧩 C4** (Kataloginhalt + Standardmaßnahmen).
**A7 — Control-Assessment & Reifegrad/Gap (Schritt 7, SoA).** Größter Block: baut die bislang fehlende **SoA-/Control-Oberfläche**. Je Control: Beleg-Verknüpfung (Dok/Risiko/Asset), **Reifegrad-Selbsteinschätzung** mit regelbasiertem Vorschlag (aus Belegen) **+ Pflichtbestätigung** (Muster: Lieferanten-Reifegrad-Freigabe), Markierung offener Teilanforderungen; unter Ziel → Aufgabe. Nutzt Coverage-Daten + Foundation-Export-Schema. *Akzeptanz:* jede relevante Teilanforderung adressiert; Reifegradvorschlag nachvollziehbar an Belege gekoppelt. **🧩 C5** (Reifegrad-Logik), **🧩 C6** (Umsetzungshinweis-Content, via B5).
**A8 — Gap-Konsolidierung (Schritt 8).** Aggregation/Dedup/Priorisierung (Muss/AL3 = hoch) aus Schritt 6+7 zu einer konsolidierten Gap-/Maßnahmenliste, abgeglichen mit Aufgaben (B1). *Akzeptanz:* keine Dopplungen; jeder offene Punkt hat Priorität + ggf. Aufgabe. **🧩 C8** (Priorisierungslogik/Quick-Wins).
---
## 3. Lane B — Dev B: „Engines, Inhalte & Ausgabe"
| Epic | Branch | Hängt ab von | Aufwand | 🧩 SME |
|---|---|---|---|:--:|
| **B1 Aufgaben-Modul erweitern** | `dev/b1-tasks-erweiterung` | Foundation | M | – |
| **B2 Regel-/Mapping-Layer** | `dev/b2-regel-engine` | Foundation | L | 🧩 C2 |
| **B3 Fragebogen/Fakten (Schritt 2)** | `dev/b3-fragebogen` | A1, B2 | M | 🧩 C2 |
| **B4 Richtlinien: Import-bei-Aktivierung + manueller Upload + Control-Mapping (Schritt 4 + Menü)** | `dev/b4-richtlinien-import-upload` | Foundation | M–L | 🧩 C3 |
| **B5 Umsetzungshinweise (Content-Modell + Inline-Panel)** | `dev/b5-umsetzungshinweise` | A1 | S (Code) | 🧩 C6 |
| **B6 Versionierung/Propagation** | `dev/b6-versionierung` | B4 | M–L | – |
| **B7 Assessment-Readiness & Export (Schritt 9)** | `dev/b7-readiness-export` | A7, A8 | L | 🧩 C9 |
**B1 — Aufgaben-Modul erweitern.** Das generische `Task`-Modell (heute `policy_approval`) um die Foundation-Felder erweitern; **Auto-Generierung** aus Triggern (Schritt 3/4/6/7/8, Zurückweisung) als **Vorschlag mit Bestätigung**; Ressourcenfelder editierbar; Verknüpfungen Control/Risiko/Dok/Asset; Anzeige/Filter im bestehenden Modul „Aufgaben" (`src/app/(app)/tasks/`). *Akzeptanz:* Trigger erzeugt Aufgabenvorschlag mit korrekten Verknüpfungen/Ressourcen; Bestätigung übernimmt.
**B2 — Regel-/Mapping-Layer.** Umsetzung der DSL (Foundation #3) als testbare Engine `src/lib/rules/**`: Antwort/Scope/Flag → Klausel-Ein/Ausblendung (Handlebars-Flags erweitern), betroffene Controls/Assets/Risiken, Aufgaben-Trigger. Isoliert + unit-getestet. *Akzeptanz:* Regelauswertung deterministisch, mit Testfällen belegt; Änderung einer Antwort propagiert sichtbar. **🧩 C2** (Antwort→Wirkung-Mapping).
**B3 — Fragebogen/Fakten (Schritt 2).** Dynamischer, **bedingter Fragebogen**; Antworten als **wiederverwendbare Faktenobjekte** (an vorhandene zentrale Variablen/Stammdaten andocken, ohne die Sperre der zentralen Variablen zu verletzen). Bedingte Sichtbarkeit über B2. *Akzeptanz:* Antworten wiederverwendbar; Änderung propagiert in abhängige Objekte/Platzhalter. **🧩 C2** (Fragenkatalog).
**B4 — Richtlinien: Import-bei-Aktivierung + manueller Upload + Control-Mapping.** (Explizite Anforderung.) Zwei Wege:
- **(a) Vorlagen-Import bei Modul-Aktivierung:** Wird das **Richtlinien-Modul im Superadmin/Adminportal aktiviert**, wird das Vorlagenpaket **mandantenweit importiert** — die vorhandene **nicht-destruktive** Logik (`prisma/import-policies.ts`, Diff/Upsert/`archivedAt`) als **serverseitig auslösbare Aktion/Job** kapseln (statt nur CLI/Seed). Zusätzlich Button „Vorlagen importieren/aktualisieren" (Admin bzw. `/policies`). Idempotent, Änderungsreport.
- **(b) Manueller Upload eigener Richtlinien direkt im Menü `/policies`:** Einstiegspunkt + Metadaten-/Block-Modell + **Control-Zuordnung** jetzt bauen; **Datei-Persistenz über einen Storage-Adapter-Interface stubben** (echtes Storage-Backend = Folge-Epic **S1**, außerhalb dieses Batches). Upload erfasst Datei-Referenz/Platzhalter + Control-Mapping in die Nachweislage. *Akzeptanz:* Modul-Aktivierung importiert Vorlagen nicht-destruktiv; unter `/policies` existiert „Eigene Richtlinie hochladen" mit Control-Zuordnung; Storage-Adapter ist gekapselt und später ohne UI-Änderung verdrahtbar. **🧩 C3** (regelfähige Vorlagen-Auszeichnung).
**B5 — Umsetzungshinweise (Content-Modell + Inline-Panel).** Strukturiertes Hinweis-Modell (org/tech/Nachweise/Vorlagen-Verweis/**Ressourcenindikation**), Scope-/Antwort-Filter (B2), kontextsensitives Inline-Panel an Teilanforderungen/Risiken/Templates. Code klein — **Inhalt ist der Aufwand**. *Akzeptanz:* passende Hinweise werden nach Scope/Antworten gefiltert eingeblendet; führen bei Maßnahme zu Aufgabe (B1). **🧩 C6**.
**B6 — Versionierung/Propagation.** Versionierung von Katalog/Templates/Risiko-Katalog + **Instanz-Referenz auf genutzte Version** + **Diff & gesteuerte Übernahme** bei Updates (löst zugleich den heute noch offenen Richtlinien-Diff und flankiert den nicht-destruktiven Re-Import). *Akzeptanz:* laufende Kundeninstanz bekommt bei Update einen Diff und übernimmt kontrolliert; nichts wird still überschrieben.
**B7 — Assessment-Readiness & Export (Schritt 9).** Reifegrad-Dashboard je Kapitel/gesamt, vorausgefüllte **VDA-ISA-Katalogsicht**, Maßnahmenplan; „bestätigt vs. unbestätigt" nach Validierungsstatus (A3); **Export** (koppelt an den offenen DOCX/PDF-Export). *Akzeptanz:* Kennzahlen stimmen mit Einzelbewertungen; Export vollständig/nachvollziehbar. **🧩 C9** (Auswertungs-/Interpretationstexte, Export-Layout).
---
## 4. Sequenz / Meilensteine (2 Lanes im Takt)
| Takt | Dev A (Lane A) | Dev B (Lane B) | Gate |
|---|---|---|---|
| **M0** | Foundation-Contracts (gemeinsam) | Foundation-Contracts (gemeinsam) | Contracts + `tasks`-Migration auf `dev` |
| **M1** | A1 Wizard-Shell | B1 Tasks-Erweiterung · B2 Regel-Engine (Kern) | Shell + Task-API stehen |
| **M2** | A2 Scoping/Admin-AL | B4 Richtlinien-Import/Upload · B3 Fragebogen | AL zentral · Import läuft |
| **M3** | A3 Validierung · A4 Rollen | B5 Umsetzungshinweise | Review generisch |
| **M4** | A5 Assets · A6 Risiko-Katalog | B6 Versionierung | Katalog/Risiko nutzbar |
| **M5** | A7 Control-Assessment (SoA) | (Puffer/Review A7) · Start B7 | Kern-Bewertung steht |
| **M6** | A8 Gap-Konsolidierung | B7 Readiness & Export | North-Star: Readiness-Export |
**Kürzester Pfad zur Assessment-Readiness:** M0→M1→M2→…→M6. Governance (A3/B1/B5/B6) läuft bewusst mit, nicht am Ende.
---
## 5. Explizite Anforderungen (Kurz-Referenz)
- **AL2/AL3 zentral im Adminportal:** → **A2** (einzige Quelle im Superadmin/Admin; Scoping liest read-only; treibt Coverage/AL3-Zusatzanforderungen).
- **Richtlinien-Vorlagen-Import bei Modul-Aktivierung + manueller Upload im Menü:** → **B4** (Import nicht-destruktiv on-enable; manueller Upload jetzt als UI/Modell/Control-Mapping, Datei-Speicher via Storage-Adapter später = Folge-Epic **S1**).
## 6. Außerhalb dieses Batches (Folge-Epics)
- **S1 Storage-Backend** (Coolify-Volume/MinIO) — schaltet echten Datei-Upload (B4b, Netzplan REG-NET, Nachweis-Upload) scharf.
- **Paket 4** (SMTP/Einladung), **NIS2-Modul**, **Admin Phase 2** (Impersonation/Plan-Limits) — unverändert im Backlog.
---
## 7. Nächster Schritt
Foundation-Contracts (Abschnitt 1) gemeinsam finalisieren und mergen → dann Lanes starten. Fachliche Zulieferung C1–C9 parallel anstoßen (siehe `Berater-Anweisung-Fachcontent.md`), sonst werden C2/C3/C4/C5/C6 zum kritischen Pfad für A2/A6/A7 und B2/B3/B4/B5.
@@ -1,174 +0,0 @@
# Onboarding-Wizard — Story-Backlog (umsetzungsreif, mit Fachcontent C1–C9)
Basis: `Wizard-Entwickler-Backlog.md` (2 Lanes) + Fachcontent `Fachcontent_Wizard_C1-C9` + Dev-Stand `dev` (2026-07-24).
Jede Story: **ID · Titel · (Lane/Branch) · User Story · Akzeptanzkriterien · Technik/Dateien · Fachcontent · Abhängigkeit**.
**Konventionen (für alle Stories):** DoD wie im Backlog (§0: `tsc`+`lint`+`build`/Guard-Check, `TENANT_MODELS`+RLS, Migration+RLS-DO-Block, `_verify.py`→OK bei Seed-Änderung, Browser-Verifikation). Story-Größe: S/M/L.
**Fachcontent-Referenzen:** C1 = Scoping/AL-Tabelle (412 Anf.), C2 = Fragenkatalog+Wirkung, C3 = Vorlagen-Annotation, C4 = Risikokatalog (39 Risiken), C5 = Reifegrad R0–R3, C6 = Umsetzungshinweise (~397 Blöcke), C7 = Rollen/FT-01…06, C8 = Priorisierung/Dedup, C9 = Auswertung/Export.
---
## F — Foundation-Contracts (`dev/foundation-contracts`, gemeinsam, zuerst mergen)
### F1 — Aufgaben-Objekt-Schema erweitern (M)
**Als** Entwickler **möchte ich** das bestehende `Task`/`TaskComment`-Modell um Wizard-Felder erweitern, **damit** alle Schritte Aufgaben einheitlich erzeugen.
- **AK:** `Task` besitzt `type` (`document_create|evidence_provide|technical|organizational|validation`), `owner`, `dueDate`, `priority`, `status`, `resources` (JSON: tool/budget/personnel/time), `origin` (Schritt), polymorphe Verknüpfung `control|risk|document|asset`. Bestehender Typ `policy_approval` bleibt lauffähig.
- **Technik:** `prisma/schema.prisma` (`Task`), Migration `tasks_wizard_fields`, `TENANT_MODELS`+RLS, `src/server/actions/tasks.ts` erweitern (in `check-module-guards.ts` registriert).
- **Fachcontent:** C2 §8 (Task-Trigger), C8 (Priorität/Dedup als Feldsemantik).
### F2 — Einheitlicher Objekt-Validierungsstatus + externe-Validierer-Rolle (M)
**Als** ISB/Berater **möchte ich** jedes bewertbare Objekt gleich validieren.
- **AK:** wiederverwendbares Status-Feld `offen|in_bearbeitung|zur_validierung|validiert|zurueckgewiesen(+Kommentar)`; Rolle `external_validator` in RBAC; „unbestätigt zählt nicht" ist als Query-Helfer verfügbar.
- **Technik:** Mixin/Enum in `schema.prisma`; RBAC-Recht; baut auf `submitForApproval`/`tasks.ts`.
- **Fachcontent:** C5 (Validierungsstatus je Beleg), C9 (bestätigt/unbestätigt).
### F3 — Regel-/Mapping-DSL-Vertrag (M)
**Als** Entwickler **möchte ich** einen testbaren Regel-Vertrag, **damit** Antworten/Scope/Flags Wirkungen auslösen.
- **AK:** JSON-Schema `Bedingung(Antwort|Scope|Flag) → Wirkung(Klausel|Control|Risiko|Asset|Aufgabe)`; Referenz-Testfälle aus C2 §5 (Q-FEAT-01…10) grün.
- **Technik:** `src/lib/rules/dsl.ts` (Typen) + Unit-Test-Harness. Noch keine UI.
- **Fachcontent:** C2 (Wirkungsmatrix), C1 (Scope-Bedingung-Spalte).
### F4 — Wizard-Variablen/Flags erweitern (`variables.schema.json`) (S)
**Als** Content-Owner **möchte ich** neue Flags/Variablen registriert haben, **damit** `_verify.py` sie kennt.
- **AK:** `FLAG_PROTOTYPE_PROTECTION`, `FLAG_ISB_EXTERNAL`, `FLAG_ISB_INTERNAL` + fehlende `ROLE_*`/`TECH_*` aus C2 ergänzt; `python3 _verify.py` → **OK**.
- **Technik:** `seed/isms-vorlagenpaket-v2/variables.schema.json`.
- **Fachcontent:** C0 (offene Punkte 2), C2 §3–6, C7 §3. **Abh.:** vor B2/B3/B4/A4.
---
## Lane A — Dev A
### A1 — Wizard-Shell & Navigation (`dev/a1-wizard-shell`)
**A1-1 Step-Registry & State-Machine (L).** Multi-Step-Flow mit Fortschritt, Gates, Wiederaufnahme; Schritte als registrierbare Module.
- **AK:** Fortschritt je Mandant persistent; ein Gate blockiert „weiter", bis das Vorgänger-Objekt `validiert` ist (F2); Steps sind per Registry einklinkbar (Lane B liefert Step-Inhalte).
- **Technik:** `src/app/(app)/onboarding/**`, `src/server/actions/onboarding.ts`, neues Modul `onboarding` in `src/lib/modules.ts`; Migration `onboarding_progress`.
**A1-2 Fortschritts-/Status-Dashboard-Kachel (S).** Kachel „Onboarding-Fortschritt" analog vorhandener Dashboard-Kachel.
### A2 — Scoping + AL2/AL3 zentral im Adminportal (`dev/a2-scoping-admin-al`)
**A2-1 AL2/AL3 zentral im Admin/Superadmin (M).** *(Explizite Anforderung.)*
- **AK:** Der Assessment-Level (AL2/AL3) wird **ausschließlich** im Admin-/Superadmin-Portal je Mandant gesetzt (einzige Quelle); im Scoping read-only; treibt bestehende Coverage-Filter + `FLAG_HIGH/VERY_HIGH_PROTECTION`.
- **Technik:** `src/app/(platform)/admin/**`, `src/server/actions/admin.ts`/`platform.ts`; Mandanten-`/settings` verliert die Änderungs-Kompetenz (nur Anzeige).
**A2-2 Scope-Objekt + Filter-Engine (M).**
- **AK:** Scoping erfasst Prüfziele (IS/Prototyp/Datenschutz), Standorte, Geltungsbereich, Ausschlüsse → Scope-Objekt; Filter blendet Anforderungen nach **AL + Flag + Prüfziel** exakt gemäß C1 aus (z. B. `HOCH` nur bei `FLAG_HIGH_PROTECTION`; 8.x nur bei Prototyp).
- **Technik:** Scope-Modell + `src/lib/scope-filter.ts`; Import der C1-Tabelle als Datenquelle je Anforderung.
- **Fachcontent:** **C1** (AL2/AL3 + Prüfziel + Scope-Bedingung je Anforderung, 412 Zeilen). **Abh.:** F3, A1.
### A3 — Validierungs-Workflow generalisieren (`dev/a3-validation-workflow`)
**A3-1 Generisches Objekt-Review (M).**
- **AK:** Richtlinie/Risiko/Control-Bewertung/Modul tragen Review-Status (F2); Reviewer-Zuweisung inkl. `external_validator`; Kommentare; Auswertung zählt nur `validiert`.
- **Technik:** Ausbau `src/server/actions/tasks.ts` + `submitForApproval`; generische Review-Komponente.
- **Fachcontent:** C9 §1 (bestätigt/unbestätigt). **Abh.:** F1, F2.
### A4 — ISMS-Rollen & Funktionstrennung (`dev/a4-isms-rollen`)
**A4-1 Rollenmodell + Platzhalter (M).**
- **AK:** Rollen `ROLE_MANAGEMENT/ISB/IT_LEAD/HR_LEAD/DPO` erfassbar (aus C2 Abschnitt B), befüllen Vorlagen-Platzhalter; ISB-Bestellung aus Vorlage generierbar.
- **Technik:** Rollen-Modell; Anbindung zentrale Variablen (`src/lib/policy-variables.ts`).
**A4-2 Funktionstrennungs-Prüfung FT-01…06 (M).**
- **AK:** Regeln **FT-01…FT-06** aus C7 implementiert; Konflikt/Lücke → Hinweis + Aufgabe (F1); Option „Trennung herstellen" **oder** „kompensierende Kontrolle dokumentieren".
- **Fachcontent:** **C7** (Rollen, FT-Regeln, `VA-00_ISB-Bestellung.md`, Flags `FLAG_ISB_EXTERNAL/INTERNAL`). **Abh.:** F4, A3.
### A5 — Asset-Anbindung Wizard (`dev/a5-assets-step`)
**A5-1 Asset-Schritt auf Bestandsmodul (S).**
- **AK:** Schritt 5 nutzt vorhandenes Assets/BIA (C/I/A, Schutzbedarf, Eigentümer); Trigger „unvollständig/kein Eigentümer" → Aufgabe (F1). Keine Doppel-Datenhaltung.
### A6 — Risiko-Katalog & Anbindung (`dev/a6-risiko-katalog`)
**A6-1 Risiko-Katalog-Datenmodell + Import (M).**
- **AK:** 39 Katalog-Risiken (11 Kategorien) importiert mit ID `R-<KAT>-<nr>`, Controls, Asset-Typen, Standardmaßnahmen, Default `E/S`; erweiterbar; „nicht anwendbar"-Begründung möglich.
- **Technik:** Risiko-Katalog-Seed + Auswahl-UI im vorhandenen Risikomodul; 5×5 wie C4.
**A6-2 Maßnahme → Aufgabe + Restrisiko (M).**
- **AK:** ausgewähltes Risiko → Bewertung (5×5) → Behandlung → Standardmaßnahme erzeugt Aufgabe (F1), verknüpft Risiko+Control; Restrisiko > Akzeptanz erfordert dokumentierte Akzeptanz (VA-09).
- **Fachcontent:** **C4** (Katalog + Bewertungslogik + Standardmaßnahmen). **Abh.:** F1, B2.
### A7 — Control-Assessment & Reifegrad/Gap (`dev/a7-control-assessment`, SoA)
**A7-1 Control-/SoA-Oberfläche (L).**
- **AK:** je relevantem Control: Belege verknüpfen (Dok/Risiko/Asset), Teilanforderungen sichtbar (aus C1/`mapping.json`), offene Teilanforderungen markiert; Inline-Umsetzungshinweise (B5/C6).
**A7-2 Reifegrad-Engine R0–R3 + Pflichtbestätigung (L).**
- **AK:** Reifegrad-Vorschlag exakt nach **C5**-Regeln (R0/R1a-c/R2/R3, Sonderfälle, Aktualitätsregel ≤12 Mon., Deckelung bei Widerspruch); Vorschlag ist **an Belege gekoppelt und nachvollziehbar**; Bearbeiter-Bestätigung Pflicht; Zielreifegrad nach C5 §3 (AL2→2, AL3→3, HOCH/SEHR HOCH→3).
- **AK:** Reifegrad < Ziel / offene Teilanforderung → Aufgabe (F1).
- **Technik:** `src/app/(app)/soa/**` (bislang Platzhalter) + `src/server/actions/soa.ts`; Reifegrad-Berechnung `src/lib/maturity.ts` (unit-getestet gegen C5-Beispiele).
- **Fachcontent:** **C5** (Reifegradlogik), **C6** (Umsetzungshinweise). **Abh.:** A2, B1, B2, B5.
### A8 — Gap-Konsolidierung (`dev/a8-gap-konsolidierung`)
**A8-1 Aggregation + Dedup + Priorisierung (M).**
- **AK:** offene Punkte aus Schritt 6/7 zusammengeführt; **Dedup-Schlüssel** (Control+Teilanforderung / verknüpfte Aufgabe) nach C8 §2; Priorität **Hoch/Mittel/Niedrig** nach C8 §1 mit Zusatzsortierung (betroffene Controls, Risikohöhe, Aufwand); keine Dopplungen; jeder Punkt hat Priorität + ggf. Aufgabe.
**A8-2 Quick-Win-Kennzeichnung (S).**
- **AK:** Quick-Win-Kriterien aus C8 §3; im Maßnahmenplan hervorgehoben („erst Quick-Wins").
- **Fachcontent:** **C8**. **Abh.:** A6, A7, B1.
---
## Lane B — Dev B
### B1 — Aufgaben-Modul erweitern (`dev/b1-tasks-erweiterung`)
**B1-1 Auto-Generierung mit Bestätigung (M).**
- **AK:** Trigger aus Schritt 3/4/6/7/8 + Zurückweisung erzeugen **Aufgabenvorschlag** (Typ/Verknüpfung/Ressourcen aus F1); Bearbeiter bestätigt/verwirft; Anzeige/Filter im Modul „Aufgaben" (`src/app/(app)/tasks/`).
- **Fachcontent:** C2 §8 (konkrete Trigger). **Abh.:** F1.
### B2 — Regel-/Mapping-Engine (`dev/b2-regel-engine`)
**B2-1 Engine-Kern + Klausel-Flags (L).**
- **AK:** DSL (F3) ausgewertet: Antwort/Scope/Flag → Klausel ein/aus (Handlebars-Flags), Control-Relevanz, Risiko-/Asset-Bezug, Aufgabe; deterministisch, unit-getestet gegen **C2** Q-FEAT-01…10.
- **Technik:** `src/lib/rules/**`; koppelt an vorhandene `{{#if FLAG}}`-Renderer + Lieferanten-Anforderungs-Engine.
**B2-2 Propagation bei Antwortänderung (M).**
- **AK:** Änderung einer Antwort propagiert sichtbar in abhängige Objekte/Platzhalter/Controls.
- **Fachcontent:** **C2** (Wirkungsmatrix), C1 (Scope-Bedingungen). **Abh.:** F3, F4.
### B3 — Fragebogen/Fakten (`dev/b3-fragebogen`)
**B3-1 Dynamischer, bedingter Fragebogen (M).**
- **AK:** Abschnitte A–F aus C2 mit Antworttypen + Anzeige-Bedingungen; Antworten als wiederverwendbare Faktenobjekte; Baseline-Fragen (E) als vorbelegte Defaults aus `Technische-Sicherheits-Baseline.md` (nur bestätigen).
- **AK:** zentrale Variablen bleiben serverseitig gesperrt (nur `/settings`), Fragebogen respektiert das.
- **Technik:** `src/app/(app)/onboarding/steps/context/**` (im A1-Registry); Faktenmodell + Migration.
- **Fachcontent:** **C2** (Fragen A–F, Baseline-Defaults). **Abh.:** A1, B2, F4.
### B4 — Richtlinien: Import-bei-Aktivierung + manueller Upload + Control-Mapping (`dev/b4-richtlinien-import-upload`)
**B4-1 Vorlagen-Import bei Modul-Aktivierung (M).** *(Explizite Anforderung.)*
- **AK:** Aktiviert der Superadmin das Richtlinien-Modul, wird das Paket mandantenweit **nicht-destruktiv** importiert (bestehende Logik `prisma/import-policies.ts` als Server-Action/Job gekapselt); Button „Vorlagen importieren/aktualisieren" in Admin und `/policies`; idempotent, Änderungsreport.
**B4-2 Manueller Upload eigener Richtlinien im Menü (M).** *(Explizite Anforderung; Storage später.)*
- **AK:** unter `/policies` „Eigene Richtlinie hochladen" mit **Pflicht-Control-Zuordnung** (Multi-Select Control-Katalog, optional Anforderungs-IDs, siehe C3 §4); Metadaten/Block-Modell angelegt; Datei-Persistenz über gestubbtes **Storage-Adapter-Interface** (echtes Backend = Folge-Epic S1); Zuordnung fließt als `<!-- REQ -->`-Äquivalent in die Nachweislage (Schritt 7).
**B4-3 Proto/DS-Vorlagen P01/D01 + mapping.json (M).**
- **AK:** neue Vorlagen `P01_Prototypenschutz.md` (+ `VA-20`) und `D01_Datenschutz.md` nach C3-Konvention angelegt; `mapping.json`-Einträge im gleichen Schema (8.x/9.x); `FLAG_PROTOTYPE_PROTECTION` genutzt; `_verify.py` (um neue Anker erweitert) → **OK**.
- **Fachcontent:** **C3** (Annotationskonvention + P01/D01), C1/C6 (Dekomposition 8.x/9.x). **Abh.:** F4.
### B5 — Umsetzungshinweise (`dev/b5-umsetzungshinweise`)
**B5-1 Hinweis-Datenmodell + Import (S Code).**
- **AK:** Hinweis-Objekt je Teilanforderung mit Feldern **organisatorisch/technisch/Nachweise/Vorlage/Ressourcen/AL-Filter**; Import der ~397 C6-Blöcke; Baseline-Referenzen (`BL-*`) statt harter Werte.
**B5-2 Kontextsensitives Inline-Panel (S).**
- **AK:** Panel an Teilanforderung/Risiko/Template; gefiltert nach Scope/Antworten (B2) und AL; Ressourcen-Hinweis mit Beschaffungsbedarf → Aufgabe (F1).
- **Fachcontent:** **C6** (Hinweise), C0 offener Punkt 3 (Baseline-Codes vor Verdrahtung gegen `Technische-Sicherheits-Baseline.md` normalisieren). **Abh.:** A1.
### B6 — Versionierung/Propagation (`dev/b6-versionierung`)
**B6-1 Versionierung Katalog/Templates/Risiko + Instanz-Referenz (M).**
- **AK:** Kundeninstanz referenziert genutzte Template-/Katalog-Version; Änderungen erzeugen Diff.
**B6-2 Gesteuerte Übernahme (Diff) (M).**
- **AK:** bei Update erhält die Instanz einen Diff mit kontrollierter Übernahme; nichts wird still überschrieben (ergänzt nicht-destruktiven Re-Import + löst offenen Richtlinien-Diff). **Abh.:** B4.
### B7 — Assessment-Readiness & Export (`dev/b7-readiness-export`)
**B7-1 Reifegrad-Dashboard + Interpretationstexte (M).**
- **AK:** Aggregation je Kapitel/gesamt (Ø, „unbestätigt zählt nicht"); Textbänder nach C9 §1; Kennzahlen (Anteil bestätigt, offene Punkte je Priorität, Abdeckung je Prüfziel); dynamische „nächste Schritte" (C9 §2).
**B7-2 VDA-ISA-Katalog-Export (L).**
- **AK:** Export-Layout exakt nach **C9 §3** (Felder Control-ID/Frage/Reifegrad/Status/Umsetzungsbeschreibung MUSS→SOLL→HOCH→SEHR HOCH/Belege/offene Punkte); Reihenfolge IS→Proto→DS; „unbestätigt" markiert; **XLSX** (Katalog+Kennzahlen) + DOCX/PDF-Management-Summary (koppelt an bestehenden Export); Zusatzartefakte (Maßnahmenplan, Nachweisregister) nach C9 §4.
- **Fachcontent:** **C9**. **Abh.:** A7, A8.
---
## Content-Integration & offene Fachpunkte (aus C0)
| ID | Story | Owner | Abh. |
|---|---|---|---|
| **X1** | Neue `FLAG_*`/Variablen in `variables.schema.json` (siehe F4) | B (mit SME) | vor B2/B3/B4/A4 |
| **X2** | P01/D01 + `VA-20` Vorlagen + `mapping.json`-Einträge (B4-3) | B (mit SME) | C3 |
| **X3** | Baseline-`BL-*`-Codes aus C6 gegen `Technische-Sicherheits-Baseline.md` normalisieren | B5 (mit SME) | vor B5-Verdrahtung |
| **X4** | ISB-Freigabe aller neuen/angepassten Fachtexte (VA/Richtlinien/P01/D01) | ISB (fachlich, kein Code) | vor Produktivsetzung |
---
## Sprint-Vorschlag (2 Lanes)
- **Sprint 0:** F1–F4 (gemeinsam) → mergen.
- **Sprint 1:** A1 · B1 + B2-1.
- **Sprint 2:** A2 (+C1) · B4 (+C3) + B3 (+C2).
- **Sprint 3:** A3 · A4 (+C7) · B5 (+C6).
- **Sprint 4:** A5 · A6 (+C4) · B6.
- **Sprint 5:** A7 (+C5/C6).
- **Sprint 6:** A8 (+C8) · B7 (+C9).
> „Definition of Ready" je Story: zugehöriges C-Paket eingespielt und (wo Seed) `_verify.py` → **OK**. Fehlt der Fachcontent, bleibt die Story blockiert (kritischer Pfad: C2 → C1/C3 → C5/C6).
@@ -1,35 +0,0 @@
# Fachcontent-Zulieferung C1–C9 — Übersicht
Fachliche Zulieferung (SME/Berater) für den Onboarding-Wizard, gemäß `BeraterAnweisungFachcontent.md` und `WizardEntwicklerBacklog.md`. Alle Pakete sind ID-konsistent zum bestehenden Vorlagenpaket `isms-vorlagenpaket-v2` (`mapping.json`, `variables.schema.json`, Baseline `BL-*`). Prüfziele: Informationssicherheit (Paketbasis), Prototypenschutz (8.x) und Datenschutz (9.x) als Erweiterung.
## Lieferpakete
| WP | Datei | Schaltet frei (Backlog) | Inhalt |
|---|---|---|---|
| **C1** | `C1_Scoping-AL-Pruefziel.md` | A2 | AL2/AL3-Kennzeichnung + Prüfziel + Scope-Bedingung je Anforderung — **412 Anforderungen** (316 IS + 72 Proto + 24 DS) |
| **C2** | `C2_Fragenkatalog-Wirkung.md` | B2, B3 | Fragenkatalog (A–F) + Antwort→Wirkung (Variable/Flag/Control/Risiko/Aufgabe), gekoppelt an `variables.schema.json` |
| **C3** | `C3_Vorlagen-Annotation.md` | B4 | Annotationskonvention (REQ/IMPL-Anker, `{{#if FLAG}}`, Baseline-Variablen), Ist-Bestätigung (`_verify.py → OK`), Erweiterung P01/D01 |
| **C4** | `C4_Risikokatalog.md` | A6 | Standard-/VDA-Risiko-Katalog (**39 Risiken, 11 Kategorien**) mit Controls, Assets, Standardmaßnahmen, Default-5×5-Einschätzung |
| **C5** | `C5_Reifegradlogik.md` | A7 | Generische Beleg→Reifegrad-Regel + control-spezifische Tabelle (alle 45 IS-Controls + Proto/DS-Gruppen), Zielreifegrad, Offene-Punkt-Kriterium |
| **C6** | `C6_Umsetzungshinweise.md` | B5, A7 | Umsetzungshinweise je Teilanforderung (**~397 Blöcke über 79 Controls**): org/tech, Nachweise, Vorlage, Ressourcen, AL-Filter |
| **C7** | `C7_Rollen-Funktionstrennung.md` | A4 | Soll-Rollenmodell, Funktionstrennungs-Regeln (FT-01…06), ISB-Bestellungs-Vorlage im Paket-Stil |
| **C8** | `C8_Priorisierung-Gap.md` | A8 | Prioritätsregeln (Hoch/Mittel/Niedrig), Dedup-Kriterien, Quick-Win-Definition |
| **C9** | `C9_Auswertung-Export.md` | B7 | Reifegrad-Interpretationstexte, nächste Schritte, VDA-ISA-Export-Layout (bestätigt/unbestätigt) |
## Konsistenz-Anker
- **Anforderungs-IDs** = `mapping.json`-IDs (z. B. `4.1.2-H1`); Proto/DS neu im gleichen Schema (`8.1.1-M1`, `9.1.1-M1`).
- **Flags/Variablen** = `variables.schema.json` (Single Source of Truth). Neue Flags (`FLAG_PROTOTYPE_PROTECTION`, `FLAG_ISB_EXTERNAL/INTERNAL`) sind in C3/C7 vermerkt und dort zuerst zu ergänzen.
- **Technische Werte** = Baseline `BL-*` (C6 referenziert, nie hart kodiert).
- **Vorlagenpaket** unverändert; `python3 seed/isms-vorlagenpaket-v2/_verify.py` → **OK**.
## Offene Punkte / Abhängigkeiten für die Umsetzung
1. **Proto/DS-Vorlagen** (`P01`, `D01`) und zugehörige `mapping.json`-Einträge sind noch anzulegen (C3 Abschnitt 3) — die Anforderungsdekomposition + Hinweise (C1/C5/C6) liegen bereits vor.
2. **Neue `FLAG_*`** vor Nutzung in `variables.schema.json` ergänzen, damit `_verify.py` sie kennt.
3. **Baseline-IDs in C6**: Agenten haben teils sprechende `BL-*`-Codes ergänzt, die über den dokumentierten Satz (BL-IAM/CRY/OPS/NET/EP/PHY/HR/SUP/DEL/GOV/PROJ) hinausgehen — vor Verdrahtung gegen `Technische-Sicherheits-Baseline.md` normalisieren.
4. **ISB-Freigabe** der neuen/angepassten Fachtexte ist der abschließende fachliche Schritt (steht im Backlog offen auf `dev`).
## Reihenfolge (kritischer Pfad)
C2 → C1/C3 (schalten B2/B3 und A2/B4 frei) → C4/C5/C6 (A6/A7/B5) → C7/C8/C9. Entspricht der Meilenstein-Sequenz M0–M6 des Backlogs.
@@ -1,444 +0,0 @@
# C1 — Scoping-Grundlage: AL2/AL3-Kennzeichnung + Prüfziel-Zuordnung je Anforderung
> **Schaltet frei:** A2 (Scoping / Admin-AL / Scope-Filter). **Grundlage:** `mapping.json` (Informationssicherheit, 316 Anforderungen) + Prototypenschutz/Datenschutz-Dekomposition (Erweiterung).
## Methodik (Zuordnungslogik)
Der Assessment-Level (AL) wird **zentral im Adminportal** gesetzt (Backlog A2) und ist read-only im Scoping. Die Zuordnung folgt der VDA-ISA-/TISAX-Systematik:
- **MUSS / SOLL** → Bestandteil von **AL2 und AL3** (Grundabsicherung; SOLL über `FLAG_INCLUDE_SHOULD` für Ziel-Reifegrad 3).
- **HOCH** (Zusatz hoher Schutzbedarf) → relevant bei **hohem Schutzbedarf** (typisch AL3), Flag `FLAG_HIGH_PROTECTION`.
- **SEHR HOCH** (Zusatz sehr hoher Schutzbedarf) → relevant bei **sehr hohem Schutzbedarf** (AL3), Flag `FLAG_VERY_HIGH_PROTECTION`.
- **Prüfziel** steuert, ob ein ganzes Kapitel überhaupt im Scope ist (Informationssicherheit / Prototypenschutz / Datenschutz).
- **Scope-Bedingung** = das Feature-Flag bzw. die Bedingung aus `mapping.json` (`condition`); ist keine gesetzt, gilt „immer im Scope" (sobald das Prüfziel aktiv ist).
Der Scope-Filter (A2) blendet je nach AL + Flags + Prüfziel die nicht relevanten Anforderungen aus. Spalte „Scope-Bedingung" ist maschinenlesbar an die `FLAG_*` aus `variables.schema.json` gekoppelt.
## Prüfziel Informationssicherheit (316 Anforderungen)
| Control | Anforderungs-ID | Typ | AL2 | AL3 | Prüfziel | Scope-Bedingung (Flag) |
|---|---|---|:--:|:--:|---|---|
| 1.1.1 | 1.1.1-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.1.1 | 1.1.1-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.1.1 | 1.1.1-M3 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.1.1 | 1.1.1-M4 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.1.1 | 1.1.1-M5 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.1.1 | 1.1.1-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.1.1 | 1.1.1-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.1.1 | 1.1.1-S3 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.1.1 | 1.1.1-S4 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.2.1 | 1.2.1-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.2.1 | 1.2.1-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.2.1 | 1.2.1-M3 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.2.1 | 1.2.1-M4 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.2.1 | 1.2.1-M5 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.2.1 | 1.2.1-M6 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.2.2 | 1.2.2-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.2.2 | 1.2.2-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.2.2 | 1.2.2-M3 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.2.2 | 1.2.2-M4 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.2.2 | 1.2.2-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.2.2 | 1.2.2-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.2.2 | 1.2.2-H1 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 1.2.3 | 1.2.3-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.2.3 | 1.2.3-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.2.3 | 1.2.3-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.2.3 | 1.2.3-S3 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.2.3 | 1.2.3-H1 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 1.3.1 | 1.3.1-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.3.1 | 1.3.1-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.3.1 | 1.3.1-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.3.2 | 1.3.2-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.3.2 | 1.3.2-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.3.2 | 1.3.2-M3 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.3.2 | 1.3.2-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.3.3 | 1.3.3-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.3.3 | 1.3.3-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.3.3 | 1.3.3-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.3.3 | 1.3.3-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.3.3 | 1.3.3-S3 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.3.3 | 1.3.3-S4 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.3.4 | 1.3.4-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.3.4 | 1.3.4-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.3.4 | 1.3.4-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.3.4 | 1.3.4-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.3.4 | 1.3.4-S3 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.3.4 | 1.3.4-S4 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.3.4 | 1.3.4-S5 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.3.4 | 1.3.4-V1 | SEHR HOCH | – | Ja | Informationssicherheit | `FLAG_VERY_HIGH_PROTECTION` |
| 1.4.1 | 1.4.1-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.4.1 | 1.4.1-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.4.1 | 1.4.1-M3 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.4.1 | 1.4.1-M4 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.4.1 | 1.4.1-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.4.1 | 1.4.1-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.4.1 | 1.4.1-S3 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.4.1 | 1.4.1-S4 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.5.1 | 1.5.1-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.5.1 | 1.5.1-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.5.1 | 1.5.1-M3 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.5.1 | 1.5.1-M4 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.5.1 | 1.5.1-M5 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.5.1 | 1.5.1-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.5.2 | 1.5.2-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.5.2 | 1.5.2-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.5.2 | 1.5.2-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.6.1 | 1.6.1-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.6.1 | 1.6.1-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.6.1 | 1.6.1-M3 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.6.1 | 1.6.1-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.6.1 | 1.6.1-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.6.1 | 1.6.1-S3 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.6.1 | 1.6.1-S4 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.6.1 | 1.6.1-S5 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.6.1 | 1.6.1-S6 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.6.1 | 1.6.1-V1 | SEHR HOCH | – | Ja | Informationssicherheit | `FLAG_VERY_HIGH_PROTECTION` |
| 1.6.2 | 1.6.2-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.6.2 | 1.6.2-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.6.2 | 1.6.2-M3 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.6.2 | 1.6.2-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.6.2 | 1.6.2-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.6.2 | 1.6.2-S3 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.6.2 | 1.6.2-H1 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 1.6.2 | 1.6.2-H2 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 1.6.2 | 1.6.2-H3 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 1.6.2 | 1.6.2-H4 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 1.6.2 | 1.6.2-H5 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 1.6.2 | 1.6.2-V1 | SEHR HOCH | – | Ja | Informationssicherheit | `FLAG_VERY_HIGH_PROTECTION` |
| 1.6.3 | 1.6.3-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.6.3 | 1.6.3-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.6.3 | 1.6.3-M3 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 1.6.3 | 1.6.3-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.6.3 | 1.6.3-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.6.3 | 1.6.3-S3 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.6.3 | 1.6.3-S4 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.6.3 | 1.6.3-S5 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.6.3 | 1.6.3-S6 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 1.6.3 | 1.6.3-H1 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 1.6.3 | 1.6.3-H2 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 1.6.3 | 1.6.3-H3 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 1.6.3 | 1.6.3-H4 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 1.6.3 | 1.6.3-H5 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 1.6.3 | 1.6.3-V1 | SEHR HOCH | – | Ja | Informationssicherheit | `FLAG_VERY_HIGH_PROTECTION` |
| 2.1.1 | 2.1.1-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 2.1.1 | 2.1.1-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 2.1.1 | 2.1.1-M3 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 2.1.1 | 2.1.1-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 2.1.1 | 2.1.1-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 2.1.2 | 2.1.2-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 2.1.2 | 2.1.2-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 2.1.2 | 2.1.2-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 2.1.2 | 2.1.2-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 2.1.2 | 2.1.2-S3 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 2.1.3 | 2.1.3-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 2.1.3 | 2.1.3-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 2.1.3 | 2.1.3-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 2.1.3 | 2.1.3-S3 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 2.1.3 | 2.1.3-S4 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 2.1.3 | 2.1.3-S5 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 2.1.3 | 2.1.3-S6 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 2.1.4 | 2.1.4-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 2.1.4 | 2.1.4-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 2.1.4 | 2.1.4-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 2.1.4 | 2.1.4-H1 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 3.1.1 | 3.1.1-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 3.1.1 | 3.1.1-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 3.1.1 | 3.1.1-M3 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 3.1.1 | 3.1.1-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 3.1.1 | 3.1.1-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 3.1.1 | 3.1.1-S3 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 3.1.1 | 3.1.1-S4 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 3.1.1 | 3.1.1-S5 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 3.1.1 | 3.1.1-H1 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 3.1.4 | 3.1.4-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 3.1.4 | 3.1.4-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 3.1.4 | 3.1.4-H1 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 4.1.1 | 4.1.1-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 4.1.1 | 4.1.1-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 4.1.1 | 4.1.1-H1 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 4.1.2 | 4.1.2-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 4.1.2 | 4.1.2-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 4.1.2 | 4.1.2-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 4.1.2 | 4.1.2-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 4.1.2 | 4.1.2-S3 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 4.1.2 | 4.1.2-H1 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 4.1.2 | 4.1.2-V1 | SEHR HOCH | – | Ja | Informationssicherheit | `FLAG_VERY_HIGH_PROTECTION` |
| 4.1.3 | 4.1.3-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 4.1.3 | 4.1.3-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 4.1.3 | 4.1.3-M3 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 4.1.3 | 4.1.3-M4 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 4.1.3 | 4.1.3-M5 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 4.1.3 | 4.1.3-M6 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 4.1.3 | 4.1.3-M7 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 4.1.3 | 4.1.3-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 4.1.3 | 4.1.3-S10 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 4.1.3 | 4.1.3-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 4.1.3 | 4.1.3-S3 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 4.1.3 | 4.1.3-S4 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 4.1.3 | 4.1.3-S5 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 4.1.3 | 4.1.3-S6 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 4.1.3 | 4.1.3-S7 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 4.1.3 | 4.1.3-S8 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 4.1.3 | 4.1.3-S9 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 4.2.1 | 4.2.1-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 4.2.1 | 4.2.1-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 4.2.1 | 4.2.1-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 4.2.1 | 4.2.1-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 4.2.1 | 4.2.1-S3 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 4.2.1 | 4.2.1-S4 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 4.2.1 | 4.2.1-S5 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 4.2.1 | 4.2.1-H1 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 4.2.1 | 4.2.1-V1 | SEHR HOCH | – | Ja | Informationssicherheit | `FLAG_VERY_HIGH_PROTECTION` |
| 4.2.1 | 4.2.1-V2 | SEHR HOCH | – | Ja | Informationssicherheit | `FLAG_VERY_HIGH_PROTECTION` |
| 5.1.1 | 5.1.1-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.1.1 | 5.1.1-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.1.1 | 5.1.1-H1 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 5.1.2 | 5.1.2-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.1.2 | 5.1.2-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.1.2 | 5.1.2-M3 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.1.2 | 5.1.2-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.1.2 | 5.1.2-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.1.2 | 5.1.2-S3 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.1.2 | 5.1.2-H1 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 5.1.2 | 5.1.2-V1 | SEHR HOCH | – | Ja | Informationssicherheit | `FLAG_VERY_HIGH_PROTECTION` |
| 5.2.1 | 5.2.1-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.2.1 | 5.2.1-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.2.1 | 5.2.1-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.2.1 | 5.2.1-S3 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.2.1 | 5.2.1-S4 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.2.1 | 5.2.1-H1 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 5.2.2 | 5.2.2-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.2.2 | 5.2.2-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.2.2 | 5.2.2-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.2.3 | 5.2.3-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.2.3 | 5.2.3-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.2.3 | 5.2.3-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.2.3 | 5.2.3-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.2.3 | 5.2.3-S3 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.2.3 | 5.2.3-S4 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.2.3 | 5.2.3-S5 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.2.3 | 5.2.3-S6 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.2.3 | 5.2.3-S7 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.2.3 | 5.2.3-S8 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.2.4 | 5.2.4-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.2.4 | 5.2.4-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.2.4 | 5.2.4-M3 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.2.4 | 5.2.4-M4 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.2.4 | 5.2.4-M5 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.2.4 | 5.2.4-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.2.4 | 5.2.4-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.2.4 | 5.2.4-S3 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.2.4 | 5.2.4-H1 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 5.2.4 | 5.2.4-H2 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 5.2.4 | 5.2.4-V1 | SEHR HOCH | – | Ja | Informationssicherheit | `FLAG_VERY_HIGH_PROTECTION` |
| 5.2.5 | 5.2.5-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.2.5 | 5.2.5-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.2.5 | 5.2.5-M3 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.2.5 | 5.2.5-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.2.5 | 5.2.5-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.2.5 | 5.2.5-S3 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.2.6 | 5.2.6-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.2.6 | 5.2.6-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.2.6 | 5.2.6-M3 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.2.6 | 5.2.6-M4 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.2.6 | 5.2.6-M5 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.2.6 | 5.2.6-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.2.6 | 5.2.6-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.2.6 | 5.2.6-S3 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.2.6 | 5.2.6-H1 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 5.2.6 | 5.2.6-V1 | SEHR HOCH | – | Ja | Informationssicherheit | `FLAG_VERY_HIGH_PROTECTION` |
| 5.2.7 | 5.2.7-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.2.7 | 5.2.7-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.2.7 | 5.2.7-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.2.7 | 5.2.7-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.2.7 | 5.2.7-H1 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 5.2.8 | 5.2.8-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.2.8 | 5.2.8-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.2.8 | 5.2.8-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.2.8 | 5.2.8-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.2.8 | 5.2.8-S3 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.2.8 | 5.2.8-H1 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 5.2.8 | 5.2.8-H2 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 5.2.8 | 5.2.8-H3 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 5.2.8 | 5.2.8-H4 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 5.2.8 | 5.2.8-H5 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 5.2.8 | 5.2.8-H6 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 5.2.8 | 5.2.8-H7 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 5.2.8 | 5.2.8-V1 | SEHR HOCH | – | Ja | Informationssicherheit | `FLAG_VERY_HIGH_PROTECTION` |
| 5.2.8 | 5.2.8-V2 | SEHR HOCH | – | Ja | Informationssicherheit | `FLAG_VERY_HIGH_PROTECTION` |
| 5.2.8 | 5.2.8-V3 | SEHR HOCH | – | Ja | Informationssicherheit | `FLAG_VERY_HIGH_PROTECTION` |
| 5.2.9 | 5.2.9-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.2.9 | 5.2.9-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.2.9 | 5.2.9-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.2.9 | 5.2.9-H1 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 5.2.9 | 5.2.9-H2 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 5.2.9 | 5.2.9-V1 | SEHR HOCH | – | Ja | Informationssicherheit | `FLAG_VERY_HIGH_PROTECTION` |
| 5.2.9 | 5.2.9-V2 | SEHR HOCH | – | Ja | Informationssicherheit | `FLAG_VERY_HIGH_PROTECTION` |
| 5.2.9 | 5.2.9-V3 | SEHR HOCH | – | Ja | Informationssicherheit | `FLAG_VERY_HIGH_PROTECTION` |
| 5.3.1 | 5.3.1-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.3.1 | 5.3.1-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.3.1 | 5.3.1-M3 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.3.1 | 5.3.1-M4 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.3.1 | 5.3.1-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.3.1 | 5.3.1-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.3.1 | 5.3.1-S3 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.3.1 | 5.3.1-S4 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.3.1 | 5.3.1-S5 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.3.1 | 5.3.1-V1 | SEHR HOCH | – | Ja | Informationssicherheit | `FLAG_VERY_HIGH_PROTECTION` |
| 5.3.2 | 5.3.2-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.3.2 | 5.3.2-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.3.2 | 5.3.2-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.3.2 | 5.3.2-S3 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.3.2 | 5.3.2-H1 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 5.3.3 | 5.3.3-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.3.4 | 5.3.4-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.3.4 | 5.3.4-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 5.3.4-KI | 5.3.4-KI-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.3.4-KI | 5.3.4-KI-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.3.4-KI | 5.3.4-KI-M3 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 5.3.4-KI | 5.3.4-KI-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 6.1.1 | 6.1.1-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 6.1.1 | 6.1.1-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 6.1.1 | 6.1.1-M3 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 6.1.1 | 6.1.1-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 6.1.1 | 6.1.1-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 6.1.1 | 6.1.1-H1 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 6.1.1 | 6.1.1-H2 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 6.1.1 | 6.1.1-H3 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 6.1.1 | 6.1.1-V1 | SEHR HOCH | – | Ja | Informationssicherheit | `FLAG_VERY_HIGH_PROTECTION` |
| 6.1.1 | 6.1.1-V2 | SEHR HOCH | – | Ja | Informationssicherheit | `FLAG_VERY_HIGH_PROTECTION` |
| 6.1.2 | 6.1.2-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 6.1.2 | 6.1.2-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 6.1.2 | 6.1.2-M3 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 6.1.2 | 6.1.2-M4 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 6.1.2 | 6.1.2-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 6.1.2 | 6.1.2-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 6.1.2 | 6.1.2-S3 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 6.1.2 | 6.1.2-S4 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 6.1.2 | 6.1.2-S5 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 6.1.3 | 6.1.3-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 6.1.3 | 6.1.3-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 6.1.3 | 6.1.3-M3 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 6.1.3 | 6.1.3-M4 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 6.1.3 | 6.1.3-M5 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 6.1.3 | 6.1.3-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 6.1.3 | 6.1.3-S2 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 6.1.3 | 6.1.3-H1 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 6.1.3 | 6.1.3-H2 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 6.1.3 | 6.1.3-H3 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 6.1.3 | 6.1.3-H4 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 6.1.3 | 6.1.3-H5 | HOCH | – | Ja | Informationssicherheit | `FLAG_HIGH_PROTECTION` |
| 7.1.1 | 7.1.1-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 7.1.1 | 7.1.1-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 7.1.1 | 7.1.1-S1 | SOLL | Ja | Ja | Informationssicherheit | `FLAG_INCLUDE_SHOULD` |
| 7.1.2 | 7.1.2-M1 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 7.1.2 | 7.1.2-M2 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
| 7.1.2 | 7.1.2-M3 | MUSS | Ja | Ja | Informationssicherheit | immer im Scope |
## Prüfziel Prototypenschutz (72 Anforderungen) — Erweiterung
| Control | Anforderungs-ID | Typ | AL2 | AL3 | Prüfziel | Scope-Bedingung (Flag) |
|---|---|---|:--:|:--:|---|---|
| 8.1.1 | 8.1.1-M1 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.1.1 | 8.1.1-S1 | SOLL | Ja | Ja | Prototypenschutz | `FLAG_INCLUDE_SHOULD` |
| 8.1.2 | 8.1.2-M1 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.1.2 | 8.1.2-S1 | SOLL | Ja | Ja | Prototypenschutz | `FLAG_INCLUDE_SHOULD` |
| 8.1.3 | 8.1.3-M1 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.1.3 | 8.1.3-S1 | SOLL | Ja | Ja | Prototypenschutz | `FLAG_INCLUDE_SHOULD` |
| 8.1.3 | 8.1.3-S2 | SOLL | Ja | Ja | Prototypenschutz | `FLAG_INCLUDE_SHOULD` |
| 8.1.4 | 8.1.4-M1 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.1.4 | 8.1.4-S1 | SOLL | Ja | Ja | Prototypenschutz | `FLAG_INCLUDE_SHOULD` |
| 8.1.4 | 8.1.4-S2 | SOLL | Ja | Ja | Prototypenschutz | `FLAG_INCLUDE_SHOULD` |
| 8.1.4 | 8.1.4-H1 | HOCH | – | Ja | Prototypenschutz | `FLAG_HIGH_PROTECTION` |
| 8.1.5 | 8.1.5-M1 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.1.5 | 8.1.5-H1 | HOCH | – | Ja | Prototypenschutz | `FLAG_HIGH_PROTECTION` |
| 8.1.6 | 8.1.6-M1 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.1.6 | 8.1.6-M2 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.1.6 | 8.1.6-M3 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.1.7 | 8.1.7-M1 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.1.7 | 8.1.7-M2 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.1.7 | 8.1.7-M3 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.1.7 | 8.1.7-M4 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.1.8 | 8.1.8-M1 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.1.8 | 8.1.8-H1 | HOCH | – | Ja | Prototypenschutz | `FLAG_HIGH_PROTECTION` |
| 8.2.1 | 8.2.1-M1 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.1 | 8.2.1-M2 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.1 | 8.2.1-M3 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.1 | 8.2.1-M4 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.2 | 8.2.2-M1 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.2 | 8.2.2-M2 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.2 | 8.2.2-M3 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.2 | 8.2.2-M4 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.2 | 8.2.2-M5 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.2 | 8.2.2-M6 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.3 | 8.2.3-M1 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.3 | 8.2.3-M2 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.3 | 8.2.3-M3 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.3 | 8.2.3-M4 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.3 | 8.2.3-M5 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.3 | 8.2.3-M6 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.3 | 8.2.3-M7 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.4 | 8.2.4-M1 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.4 | 8.2.4-M2 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.4 | 8.2.4-M3 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.5 | 8.2.5-M1 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.5 | 8.2.5-M2 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.5 | 8.2.5-M3 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.6 | 8.2.6-M1 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.6 | 8.2.6-M2 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.6 | 8.2.6-M3 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.6 | 8.2.6-M4 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.6 | 8.2.6-M5 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.7 | 8.2.7-M1 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.2.7 | 8.2.7-M2 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.3.1 | 8.3.1-M1 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.3.1 | 8.3.1-M2 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.3.1 | 8.3.1-M3 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.3.1 | 8.3.1-M4 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.3.2 | 8.3.2-M1 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.4.1 | 8.4.1-M1 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.4.1 | 8.4.1-M2 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.4.1 | 8.4.1-M3 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.4.2 | 8.4.2-M1 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.4.2 | 8.4.2-M2 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.4.3 | 8.4.3-M1 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.4.3 | 8.4.3-M2 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.4.3 | 8.4.3-M3 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.5.1 | 8.5.1-M1 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.5.1 | 8.5.1-M2 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.5.1 | 8.5.1-M3 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.5.2 | 8.5.2-M1 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.5.2 | 8.5.2-M2 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.5.2 | 8.5.2-M3 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
| 8.5.2 | 8.5.2-M4 | MUSS | Ja | Ja | Prototypenschutz | Prüfziel Prototypenschutz aktiv |
> Hinweis Scope Prototypenschutz: Prüfziel bezieht sich in der aktuellen Konfiguration auf **Prototypenteile/-komponenten**; Controls 8.4.x (Test-/Erprobung) und 8.5.x (Ausstellungen/Film) sind per Scope-Regel `n.a.` und werden ausgeblendet.
## Prüfziel Datenschutz (24 Anforderungen) — Erweiterung
| Control | Anforderungs-ID | Typ | AL2 | AL3 | Prüfziel | Scope-Bedingung (Flag) |
|---|---|---|:--:|:--:|---|---|
| 9.1.1 | 9.1.1-M1 | MUSS | Ja | Ja | Datenschutz | Prüfziel Datenschutz aktiv (`FLAG_PERSONAL_DATA`) |
| 9.2.1 | 9.2.1-M1 | MUSS | Ja | Ja | Datenschutz | Prüfziel Datenschutz aktiv (`FLAG_PERSONAL_DATA`) |
| 9.2.1 | 9.2.1-M2 | MUSS | Ja | Ja | Datenschutz | Prüfziel Datenschutz aktiv (`FLAG_PERSONAL_DATA`) |
| 9.2.1 | 9.2.1-M3 | MUSS | Ja | Ja | Datenschutz | Prüfziel Datenschutz aktiv (`FLAG_PERSONAL_DATA`) |
| 9.2.1 | 9.2.1-M4 | MUSS | Ja | Ja | Datenschutz | Prüfziel Datenschutz aktiv (`FLAG_PERSONAL_DATA`) |
| 9.2.1 | 9.2.1-M5 | MUSS | Ja | Ja | Datenschutz | Prüfziel Datenschutz aktiv (`FLAG_PERSONAL_DATA`) |
| 9.2.1 | 9.2.1-M6 | MUSS | Ja | Ja | Datenschutz | Prüfziel Datenschutz aktiv (`FLAG_PERSONAL_DATA`) |
| 9.3.1 | 9.3.1-M1 | MUSS | Ja | Ja | Datenschutz | Prüfziel Datenschutz aktiv (`FLAG_PERSONAL_DATA`) |
| 9.4.1 | 9.4.1-M1 | MUSS | Ja | Ja | Datenschutz | Prüfziel Datenschutz aktiv (`FLAG_PERSONAL_DATA`) |
| 9.4.1 | 9.4.1-M2 | MUSS | Ja | Ja | Datenschutz | Prüfziel Datenschutz aktiv (`FLAG_PERSONAL_DATA`) |
| 9.5.1 | 9.5.1-M1 | MUSS | Ja | Ja | Datenschutz | Prüfziel Datenschutz aktiv (`FLAG_PERSONAL_DATA`) |
| 9.5.2 | 9.5.2-M1 | MUSS | Ja | Ja | Datenschutz | Prüfziel Datenschutz aktiv (`FLAG_PERSONAL_DATA`) |
| 9.5.2 | 9.5.2-M2 | MUSS | Ja | Ja | Datenschutz | Prüfziel Datenschutz aktiv (`FLAG_PERSONAL_DATA`) |
| 9.5.3 | 9.5.3-M1 | MUSS | Ja | Ja | Datenschutz | Prüfziel Datenschutz aktiv (`FLAG_PERSONAL_DATA`) |
| 9.5.3 | 9.5.3-M2 | MUSS | Ja | Ja | Datenschutz | Prüfziel Datenschutz aktiv (`FLAG_PERSONAL_DATA`) |
| 9.5.3 | 9.5.3-M3 | MUSS | Ja | Ja | Datenschutz | Prüfziel Datenschutz aktiv (`FLAG_PERSONAL_DATA`) |
| 9.6.1 | 9.6.1-M1 | MUSS | Ja | Ja | Datenschutz | Prüfziel Datenschutz aktiv (`FLAG_PERSONAL_DATA`) |
| 9.6.2 | 9.6.2-M1 | MUSS | Ja | Ja | Datenschutz | Prüfziel Datenschutz aktiv (`FLAG_PERSONAL_DATA`) |
| 9.6.2 | 9.6.2-M2 | MUSS | Ja | Ja | Datenschutz | Prüfziel Datenschutz aktiv (`FLAG_PERSONAL_DATA`) |
| 9.6.2 | 9.6.2-M3 | MUSS | Ja | Ja | Datenschutz | Prüfziel Datenschutz aktiv (`FLAG_PERSONAL_DATA`) |
| 9.7.1 | 9.7.1-M1 | MUSS | Ja | Ja | Datenschutz | Prüfziel Datenschutz aktiv (`FLAG_PERSONAL_DATA`) |
| 9.7.2 | 9.7.2-M1 | MUSS | Ja | Ja | Datenschutz | Prüfziel Datenschutz aktiv (`FLAG_PERSONAL_DATA`) |
| 9.8.1 | 9.8.1-M1 | MUSS | Ja | Ja | Datenschutz | Prüfziel Datenschutz aktiv (`FLAG_PERSONAL_DATA`) |
| 9.8.1 | 9.8.1-M2 | MUSS | Ja | Ja | Datenschutz | Prüfziel Datenschutz aktiv (`FLAG_PERSONAL_DATA`) |
@@ -1,110 +0,0 @@
# C2 — Fragenkatalog + Antwort→Wirkung-Mapping
> **Schaltet frei:** B2 (Regel-/Mapping-Engine) und B3 (Fragebogen, Schritt 2). **Kritischer Pfad.**
> **Grundlage:** `variables.schema.json` (Single Source of Truth der Wizard-Variablen und `FLAG_*`), `mapping.json` (`condition`-Flags je Anforderung), `Technische-Sicherheits-Baseline.md` (`BL-*`-Parameter).
## 1. Prinzip
Jede Frage erzeugt eine **Wirkung** auf genau vier Kanäle (DSL-Vertrag aus dem Backlog, Foundation #3):
1. **Variable/Platzhalter** — füllt einen `{{NAME}}`-Wert (z. B. `ORG_NAME`, `MFA_SCOPE`).
2. **Feature-Flag** — setzt ein `FLAG_*` true/false, das `{{#if FLAG_X}}`-Blöcke in Vorlagen und die Control-Relevanz steuert.
3. **Control/Risiko-Bezug** — schaltet Controls in den Scope und schlägt Katalog-Risiken (C4) vor.
4. **Aufgabe** — erzeugt bei bestimmten Antworten einen Aufgabenvorschlag (Aufgaben-Modul).
Antworttypen: `text`, `single-select`, `multi-select`, `boolean`, `number/duration`. Baseline-Parameter (Abschnitt E) sind **vorbelegte Defaults** aus der Baseline; die Frage dient nur der Bestätigung/Anpassung, nicht der Ersteingabe.
Anzeige-Bedingungen referenzieren zuvor gesetzte Flags (bedingter Fragebogen).
---
## 2. Abschnitt A — Organisation & Geltungsbereich
| Frage-ID | Frage | Antworttyp | Anzeige-Bedingung | Wirkung |
|---|---|---|---|---|
| Q-ORG-01 | Vollständiger Name der Organisation? | text | immer | Variable `ORG_NAME` |
| Q-ORG-02 | Kurzname/Abkürzung? | text | immer | Variable `ORG_SHORT` |
| Q-ORG-03 | Geltungsbereich – Kurzlabel? | text | immer | Variable `ISMS_SCOPE` |
| Q-ORG-04 | Geltungsbereich – Beschreibung (Standorte, Bereiche, Systeme, Ausschlüsse)? | text (lang) | immer | Variable `ISMS_SCOPE_DESCRIPTION`; speist Scope-Objekt (Schritt 1) |
| Q-ORG-05 | Verarbeitet die Organisation personenbezogene Daten? | boolean | immer | Flag `FLAG_PERSONAL_DATA`; schaltet Prüfziel Datenschutz (9.x) + Controls 7.1.2 · Risiken R-DSGVO-* |
| Q-ORG-06 | Werden Prototypen/schutzbedürftige Entwicklungsobjekte verarbeitet? | boolean | immer | schaltet Prüfziel Prototypenschutz (8.x); bei „nein" 8.x ausgeblendet |
## 3. Abschnitt B — Rollen (→ Schritt 3, Paket C7)
| Frage-ID | Frage | Antworttyp | Anzeige-Bedingung | Wirkung |
|---|---|---|---|---|
| Q-ROLE-01 | Oberste Leitung (Name/Funktion)? | text | immer | Variable `ROLE_MANAGEMENT` |
| Q-ROLE-02 | Informationssicherheitsbeauftragte(r)/CISO? intern/extern? | text + single-select | immer | Variable `ROLE_ISB`; Funktionstrennungsprüfung (C7); bei fehlend → Aufgabe „ISB bestellen" |
| Q-ROLE-03 | IT-Leitung / IT-Verantwortung (intern/extern)? | text | immer | Variable `ROLE_IT_LEAD`; Input Funktionstrennung ISB≠IT (C7) |
| Q-ROLE-04 | Personalleitung? | text | immer | Variable `ROLE_HR_LEAD` |
| Q-ROLE-05 | Datenschutzbeauftragte(r)? | text | `FLAG_PERSONAL_DATA` | Variable `ROLE_DPO` |
## 4. Abschnitt C — Governance & Betriebsrahmen
| Frage-ID | Frage | Antworttyp | Anzeige-Bedingung | Wirkung |
|---|---|---|---|---|
| Q-GOV-01 | Name des eingesetzten ISMS-Tools? | text | immer | Variable `TOOL_NAME` |
| Q-GOV-02 | Ticket-/Workflow-System (Dokumentationsort)? | text | immer | Variable `TOOL_TICKET` (BL-IAM-07, BL-OPS-02/09) |
| Q-GOV-03 | Verzeichnis-/IAM-System? | text | immer | Variable `TOOL_IAM` (BL-IAM-06/07) |
| Q-GOV-04 | Revisions-/Prüfzyklus? | single-select (jährlich/…) | immer | Variable `REVIEW_CYCLE` (BL-HR-01, BL-GOV-01) |
| Q-GOV-05 | Ziel-Reifegrad – SOLL-Anforderungen einbeziehen? | boolean (Default ja) | immer | Flag `FLAG_INCLUDE_SHOULD`; blendet alle `[SOLL]`-Blöcke ein/aus |
## 5. Abschnitt D — Feature-Fragen (steuern `FLAG_*`, Klauseln, Controls, Risiken)
Diese Fragen sind der Kern der Regel-Engine: jede Antwort blendet Vorlagenklauseln ein/aus, schaltet Controls in den Scope und schlägt Risiken vor.
| Frage-ID | Frage | Antworttyp | Anzeige-Bedingung | Wirkung (Flag · Klausel/Control · Risiko/Aufgabe) |
|---|---|---|---|---|
| Q-FEAT-01 | Schutzbedarf im Scope – höchste Stufe? (normal / hoch / sehr hoch) | single-select | immer | `FLAG_HIGH_PROTECTION` (hoch|sehr hoch), `FLAG_VERY_HIGH_PROTECTION` (sehr hoch), abgeleitet `FLAG_ELEVATED_PROTECTION`; blendet `[HOCH]`/`[SEHR HOCH]`-Anforderungen + `-elev`-Umsetzungstexte ein |
| Q-FEAT-02 | Werden Cloud-Dienste genutzt? | boolean | immer | `FLAG_CLOUD_USED`; R12-Klauseln + Controls 5.3.2/5.3.4/6.1.3; Risiken R-CLOUD-* |
| Q-FEAT-03 | Werden KI-/GenAI-Dienste genutzt? | boolean | immer | `FLAG_AI_USED`; R12-KI-Klauseln + Control 5.3.4-KI · VA-11; Risiko R-AI-* |
| Q-FEAT-04 | Produktions-/OT-Umgebung vorhanden? | boolean | immer | `FLAG_OT_USED`; BL-NET-01 OT-Segmentierung; Risiken R-OT-* |
| Q-FEAT-05 | Eigene Software-Entwicklung? | boolean | immer | `FLAG_DEV_INHOUSE`; R11-Klauseln + Controls 5.3.1 · VA-16; Risiko R-DEV-* |
| Q-FEAT-06 | Mobiles Arbeiten / Homeoffice zugelassen? | boolean | immer | `FLAG_MOBILE_WORK`; R06-Klauseln + Control 2.1.4 |
| Q-FEAT-07 | Mobile Endgeräte / Datenträger im Einsatz? | boolean | immer | `FLAG_MOBILE_DEVICES`; R06/BL-EP-*; Control 3.1.4; Risiko R-PHY-mobile |
| Q-FEAT-08 | Eigene PKI / Zertifikatsverwaltung? | boolean | immer | `FLAG_CRYPTO_PKI`; BL-CRY-05 PKI-Klausel; Control 5.1.1 |
| Q-FEAT-09 | Externe IT-Dienstleister genutzt? | boolean | immer | `FLAG_EXTERNAL_IT`; R13-Klauseln + Controls 6.1.1/6.1.3 · VA-10; Risiko R-SUP-*; bei „ja" → Aufgabe „Dienstleister-Selbstauskunft einholen" |
| Q-FEAT-10 | Zugriff auf Kundensysteme (z. B. OEM)? | boolean | immer | `FLAG_CUSTOMER_SYSTEMS`; Klauseln „Konten in Kundensystemen" in R08/R13 (Controls 4.1.3/4.2.1/6.1.x) |
## 6. Abschnitt E — Technische Baseline (Defaults bestätigen/anpassen)
Vorbelegt aus `Technische-Sicherheits-Baseline.md`. Je Frage wird der Default angezeigt; Änderung schreibt die zugehörige Variable. Anzeige gruppiert; nur relevante bei gesetztem Flag.
| Frage-ID | Frage (Parameter) | Variable / Baseline-ID | Anzeige-Bedingung |
|---|---|---|---|
| Q-BL-01 | Passwort-Mindestlänge / Komplexität / Rotation | `PW_MIN_LENGTH`,`PW_COMPLEXITY`,`PW_ROTATION` (BL-IAM-01) | immer |
| Q-BL-02 | MFA-Geltungsbereich | `MFA_SCOPE` (BL-IAM-02) | immer |
| Q-BL-03 | Sitzungs-Timeout | `SESSION_TIMEOUT` (BL-IAM-03) | immer |
| Q-BL-04 | Kontosperrung | `ACCOUNT_LOCKOUT` (BL-IAM-04) | immer |
| Q-BL-05 | Rezertifizierungs-Frequenz | `RECERT_FREQ` (BL-IAM-05) | immer |
| Q-BL-06 | Mindest-TLS / zulässige Algorithmen | `TLS_MIN`,`CRYPTO_ALGO` (BL-CRY-01/02) | immer |
| Q-BL-07 | Patch-SLAs (kritisch/hoch/standard) | `PATCH_SLA_CRIT/HIGH/STD` (BL-OPS-01) | immer |
| Q-BL-08 | Schwachstellenscan-Frequenz | `VULN_SCAN_FREQ` (BL-OPS-02) | immer |
| Q-BL-09 | Malware-Update-Frequenz | `MALWARE_UPDATE` (BL-OPS-03) | immer |
| Q-BL-10 | Log-Aufbewahrung | `LOG_RETENTION` (BL-OPS-04) | immer |
| Q-BL-11 | Backup-Schema / Aufbewahrung / Testfrequenz | `BACKUP_SCHEME/RETENTION/TEST_FREQ` (BL-OPS-05/06) | immer |
| Q-BL-12 | Penetrationstest-Frequenz | `PENTEST_FREQ` (BL-OPS-08) | `FLAG_ELEVATED_PROTECTION` |
| Q-BL-13 | Eingesetzte Lösungen: MFA/Malware/Backup/SIEM/MDM/VPN | `TECH_MFA/MALWARE/BACKUP/SIEM/MDM/VPN` | jeweils bei zugehörigem Flag |
| Q-BL-14 | Krypto-Standardvorgabe | `TECH_CRYPTO` (BL-CRY-02) | immer |
## 7. Abschnitt F — Dokument-Metadaten
| Frage-ID | Frage | Variable |
|---|---|---|
| Q-DOC-01 | Dokument-Version (Default 1.0) | `DOC_VERSION` |
| Q-DOC-02 | Dokument-Datum | `DOC_DATE` |
| Q-DOC-03 | Dokument-Status (Entwurf/In Freigabe/Freigegeben) | `DOC_STATUS` |
---
## 8. Aufgaben-Trigger aus Antworten (Auszug)
| Auslösende Antwort | Aufgabe (Typ) |
|---|---|
| Q-ROLE-02 „ISB nicht benannt" | `organizational` – ISB bestellen (verknüpft Control 1.2.2) |
| Q-ROLE-02/03 ISB = IT-Verantwortung | `organizational` – Funktionstrennung herstellen/kompensieren (C7) |
| Q-FEAT-09 „externe IT-Dienstleister = ja" | `evidence_provide` – Selbstauskunft/TISAX-Nachweis einholen (Control 6.1.1) |
| Q-FEAT-02/03 Cloud/KI = ja, ohne Freigabeverfahren | `document_create` – Cloud-/KI-Freigabeverfahren aktivieren (VA-11) |
| Q-BL-11 „kein Wiederherstellungstest" | `technical` – Restore-Test etablieren (BL-OPS-06, Control 5.2.9) |
> Hinweis: Die vollständige Antwort→Wirkung-Matrix wird maschinenlesbar als Regelobjekte (B2-DSL) hinterlegt; diese Tabelle ist die fachliche Spezifikation dafür. Neue `FLAG_*` bitte zuerst in `variables.schema.json` ergänzen (Single Source of Truth), dann Frage + Wirkung hier.
@@ -1,49 +0,0 @@
# C3 — Regelfähige Vorlagen-Auszeichnung (Annotationskonvention + Verify + Erweiterung)
> **Schaltet frei:** B4 (Richtlinien – Import + Upload + Control-Mapping). **Grundlage:** `isms-vorlagenpaket-v2/` (34 Vorlagen, `mapping.json`, `variables.schema.json`, `_verify.py`).
## 1. Ist-Zustand (Informationssicherheit) — vollständig annotiert
Das Vorlagenpaket ist für die **Informationssicherheit** bereits regelfähig ausgezeichnet und konsistent:
- **34 Vorlagen** – Leitlinie `L00`, Richtlinien `R01–R14`, Verfahren `VA-01 … VA-19`, plus `Technische-Sicherheits-Baseline.md`, `Nachweisregister_zentral.md`, `ISA-Mapping-Matrix.md`.
- **316 Anforderungen** in `mapping.json` (312 ISA + 4 kundenspezifisch KI), je mit stabiler ID, `type`, `level`, `policy`, `condition`-Flag, `req_anchor`/`impl_anchor`, `verfahren`.
- **`python3 _verify.py` → OK** (Render ohne offene Platzhalter für `FLAG_INCLUDE_SHOULD` an/aus, keine verwaisten Anker, keine Handlebars-Reste). **Dieser Lauf ist die Abnahmebedingung jeder Änderung.**
Für Informationssicherheit ist C3 damit als „bestätigt/konsistent" zu betrachten; die SME-Aufgabe ist Pflege + die Erweiterung um Prototypenschutz/Datenschutz (Abschnitt 3).
## 2. Annotationskonvention (verbindlich für alle Vorlagen)
| Konstrukt | Bedeutung | Beispiel |
|---|---|---|
| `{{VARIABLE}}` | Variable aus `variables.schema.json` | `{{ORG_NAME}}`, `{{MFA_SCOPE}}` |
| `{{#if FLAG_X}} … {{/if}}` | Bedingter Block (Feature-Flag/Reifegrad/Schutzbedarf) | `{{#if FLAG_HIGH_PROTECTION}} … {{/if}}` |
| `[MUSS]`/`[SOLL]`/`[HOCH]`/`[SEHR HOCH]` | Sichtbare Kennzeichnung der Anforderungsstufe | `- **[SOLL]** …` |
| `<!-- REQ <id> -->` | Hidden-Anker vor einer Einzelanforderung | `<!-- REQ 4.1.2-H1 -->` |
| `<!-- IMPL <control> -->` | Hidden-Anker vor dem Umsetzungstext (Control-gebündelt) | `<!-- IMPL 4.1.2 -->` |
| `<!-- IMPL <control>-elev -->` | Umsetzungsvariante für erhöhten Schutzbedarf | `<!-- IMPL 4.1.2-elev -->` |
| `{{LINK:ZIEL}}` | Laufzeit-Link (Dokument/Nachweisregister) | `{{LINK:R08#4.1.2}}` |
| Verfahrensanker `<!-- FULFILLS <ids> | POLICY <Rxx> -->` | VA erfüllt Anforderungen | in `VA-*` |
**Regeln für eine regelfähige Auszeichnung**
1. Jede Einzelanforderung erhält genau einen `<!-- REQ <id> -->`-Anker, dessen ID exakt der `mapping.json`-`id` entspricht.
2. `[SOLL]` immer in `{{#if FLAG_INCLUDE_SHOULD}}`, `[HOCH]` in `{{#if FLAG_HIGH_PROTECTION}}`, `[SEHR HOCH]` in `{{#if FLAG_VERY_HIGH_PROTECTION}}`. Der `condition`-Wert in `mapping.json` und der `{{#if}}` im Dokument müssen übereinstimmen.
3. Feature-abhängige Klauseln (Cloud/KI/OT/Dev/Mobil/PKI/Extern/Kundensysteme) in den passenden `{{#if FLAG_*}}`-Block.
4. Konkrete Zahlenwerte nie hart schreiben, sondern über Baseline-Variable (`{{PW_MIN_LENGTH}}` …) referenzieren.
5. `<!-- … -->`-Anker im Dokument belassen (im Lesemodus unsichtbar); nur der PDF-/Druckexport entfernt sie.
6. Nach jeder Änderung `python3 _verify.py` → **OK**.
## 3. Erweiterung: Prototypenschutz (P01) + Datenschutz (D01)
Das Paket ist „VDA ISA 2027 (Information Security)". Für die Prüfziele **Prototypenschutz (8.x)** und **Datenschutz (9.x)** sind Vorlagen + `mapping.json`-Einträge neu anzulegen — nach identischer Konvention. Vorschlag:
- **Neue Vorlage `P01_Prototypenschutz.md`** (Richtlinie Prototypenschutz) + Verfahren `VA-20_Prototypen-Zutritt-und-Transport`. Deckt 8.1.x (physische Sicherheit/Perimeter/Zonen/Zutritt/Einbruch/Besucher/Mandantentrennung), 8.2.x (Geheimhaltung/Unterauftragnehmer/Schulung/Klassifizierung/Bildaufzeichnung), 8.3.x (Transport/Lagerung). 8.4.x/8.5.x nur bei erweitertem Prüfziel.
- **Neue Vorlage `D01_Datenschutz.md`** (Richtlinie Datenschutz) — kann `R14 Compliance & Datenschutz` erweitern/aufteilen; Verfahren `VA-18 Datenschutz-und-Compliance-Pflege` ist vorhanden. Deckt 9.1.x–9.8.x.
- Je neuer Anforderung ein `mapping.json`-Eintrag im gleichen Schema (`id`,`policy`=P01/D01,`control`,`level`,`type`,`is_isa`=true,`req_anchor`,`impl_anchor`,`condition`,`requirement`,`verfahren`). Anforderungs-IDs siehe C1/C6-Dekomposition (`8.1.1-M1` …, `9.1.1-M1` …).
- Neue `FLAG_*` bei Bedarf zuerst in `variables.schema.json` (z. B. `FLAG_PROTOTYPE_PROTECTION`), damit `_verify.py` sie kennt; Prototypenschutz-Klauseln in `{{#if FLAG_PROTOTYPE_PROTECTION}}`.
- `_verify.py` um die neuen Verzeichnisse/Anker erweitern, dann → **OK**.
## 4. Control-Zuordnung beim manuellen Upload (B4b)
Für hochgeladene **eigene** Richtlinien (kein Template): Pflichtfeld „belegt Controls" (Multi-Select aus dem Control-Katalog). Optional je Control die abgedeckten Anforderungs-IDs. Diese Zuordnung fließt wie ein `<!-- REQ -->`-Anker in die Nachweislage (Schritt 7) — ohne Tailoring, aber mit voller Reifegrad-/Gap-Wirkung. Ein späterer Gap-Check (Dokumentinhalt ↔ Anforderungstext) ist als Option vorgesehen (offener Entscheidungspunkt).
@@ -1,169 +0,0 @@
# C4 — Standard-Risikokatalog (VDA ISA / TISAX)
**Paket:** C4 — Kuratierter Risiko-Katalog für den Onboarding-Wizard
**Modul:** Risikomanagement
**Referenzen:** R03 Risikomanagement · VA-09 Risikomanagement-Verfahren · VDA ISA (IS 1.x–7.x, Prototypenschutz 8.x, Datenschutz 9.x) · Baseline-Parameter BL-*
---
## 1. Nutzung im Wizard-Schritt „Risikomanagement"
Dieser Katalog wird dem Kunden im Onboarding-Wizard als kuratierte Vorauswahl typischer Informationssicherheits- und TISAX-Risiken angeboten. Der Ablauf:
1. **Auswahl** — Der Kunde markiert die für seinen Scope zutreffenden Risiken. Nicht zutreffende Risiken (z. B. Prototypenschutz ohne physische Musterteile, Entwicklung ohne eigene Softwareentwicklung) werden abgewählt oder als „nicht anwendbar" begründet.
2. **Ergänzung** — Der Kunde ergänzt eigene, organisationsspezifische Risiken.
3. **Bewertung** — Jedes ausgewählte Risiko wird nach der 5×5-Matrix bewertet (Eintrittswahrscheinlichkeit × Schadenshöhe). Die im Katalog hinterlegte **Default-Einschätzung** ist ein Startwert und vom Kunden anzupassen.
4. **Maßnahmenableitung** — Aus dem bewerteten Risiko werden Maßnahmen abgeleitet (Standardmaßnahmen sind vorbelegt). Maßnahmen erzeugen im System **Aufgaben** mit Verantwortlichen und Fristen.
5. **Restrisiko** — Nach Maßnahmenumsetzung erfolgt eine erneute Bewertung (Netto-/Restrisiko). Restrisiken oberhalb der Akzeptanzschwelle erfordern eine dokumentierte Risikoakzeptanz durch die Leitung (siehe VA-09).
### Bewertungslogik (5×5)
| Achse | Stufen | Kurzdefinition |
|-------|--------|----------------|
| **Eintrittswahrscheinlichkeit (E)** | 1 sehr gering · 2 gering · 3 mittel · 4 hoch · 5 sehr hoch | Erwartete Häufigkeit im Betrachtungszeitraum (i. d. R. 12 Monate) |
| **Schadenshöhe (S)** | 1 vernachlässigbar · 2 gering · 3 spürbar · 4 hoch · 5 existenzbedrohend | Auswirkung auf Vertraulichkeit/Integrität/Verfügbarkeit, Vertrag, Reputation, Recht |
**Risikowert = E × S** (1–25). Klassifizierung gemäß VA-09:
- **1–4 gering** (grün) — beobachten, ggf. akzeptieren
- **5–9 mittel** (gelb) — Maßnahmen empfohlen
- **10–15 hoch** (orange) — Maßnahmen verbindlich, Termin gesetzt
- **16–25 sehr hoch** (rot) — Sofortmaßnahmen, Eskalation an Leitung
Die Default-Einschätzung je Risiko ist als **E/S** angegeben (z. B. „E3/S4"). Sie geht von einem typischen KMU-Zuliefererbetrieb ohne bereits umgesetzte Maßnahmen aus (Brutto-/Ausgangsrisiko).
### ID-Schema
`R-<KATEGORIE>-<lfd. Nr.>` — Kategorien: ORG (Organisation/Governance), HR (Personal), PHY (Physisch), IAM (Identitäts-/Zugriffsmanagement), CRY (Kryptografie), OPS (Betrieb/IT), NET (Netzwerk), SUP (Lieferanten/Cloud), DEV (Entwicklung), PROTO (Prototypenschutz), DSGVO (Datenschutz).
---
## 2. Organisation / Governance
| Risiko-ID | Titel | Beschreibung | Betroffene Controls (VDA ISA) | Asset-Typen | Standardmaßnahme(n) | Default (E/S) |
|-----------|-------|--------------|-------------------------------|-------------|---------------------|---------------|
| R-ORG-01 | Fehlende/veraltete IS-Leitlinie & Verantwortlichkeiten | Es existiert keine von der Leitung verabschiedete Informationssicherheitsleitlinie oder Rollen (ISB/CISO) sind nicht benannt, sodass Steuerung und Verbindlichkeit fehlen. | 1.1.1, 1.2.1, 1.3.1 | Richtlinien, Organisation | IS-Leitlinie nach R03 erstellen/aktualisieren, ISB benennen, jährliches Management-Review etablieren | E3/S4 — ohne Governance keine wirksame Steuerung; Schaden trifft gesamtes ISMS |
| R-ORG-02 | Kein funktionierendes Risikomanagement | Risiken werden nicht systematisch erfasst, bewertet oder behandelt; TISAX-Anforderung an Risikoprozess ist nicht erfüllt. | 1.4.1 | Prozesse, Dokumentation | Risikomanagement-Verfahren VA-09 einführen, Risiko-Register führen, Reviews terminieren | E3/S4 — Kernanforderung TISAX; Assessment-Abweichung wahrscheinlich |
| R-ORG-03 | Fehlendes Asset- und Informationsklassifizierungsschema | Werte (Informationen, Systeme) sind nicht inventarisiert oder klassifiziert, sodass Schutzbedarf und Maßnahmen nicht zielgerichtet zugeordnet werden können. | 1.3.2, 1.3.3, 5.2.x | Informationswerte, Inventar | Asset-Inventar aufbauen, Klassifizierungsschema (intern/vertraulich/streng vertraulich) einführen und kennzeichnen | E3/S3 — Grundlage vieler Controls; ohne Klassifizierung Fehlschutz |
| R-ORG-04 | Unzureichende IS-Vorgabendokumentation / veraltete Verfahren | Verfahrensanweisungen und Richtlinien sind nicht vorhanden, veraltet oder werden nicht gelebt, was zu inkonsistentem Handeln führt. | 1.2.x, 1.5.1 | Dokumentation | Dokumentenlenkung nach R03 etablieren, Review-Zyklus (jährlich), Freigabe- und Versionskontrolle | E3/S3 — Nachweisführung im Assessment gefährdet |
| R-ORG-05 | Fehlendes Vorfalls- / Incident-Management | Sicherheitsvorfälle werden nicht erkannt, gemeldet, dokumentiert oder ausgewertet, wodurch Schäden eskalieren und Lernen ausbleibt. | 1.6.1 | Prozesse | Incident-Response-Prozess einführen, Meldewege und Eskalation definieren, Vorfallregister führen | E3/S4 — verzögerte Reaktion vergrößert Schaden erheblich |
| R-ORG-06 | Fehlendes Business Continuity / Notfallmanagement | Für Ausfälle kritischer Prozesse/IT existieren keine Notfall- und Wiederanlaufpläne, sodass Betriebsunterbrechungen unkontrolliert verlaufen. | 1.6.x, 7.x | Prozesse, IT-Systeme | BCM-Konzept und Wiederanlaufpläne erstellen, Notfallübungen jährlich, Bezug zu BL-OPS-05 (Backup) | E2/S4 — selten, aber hoher Schaden bei Eintritt |
---
## 3. Personal
| Risiko-ID | Titel | Beschreibung | Betroffene Controls (VDA ISA) | Asset-Typen | Standardmaßnahme(n) | Default (E/S) |
|-----------|-------|--------------|-------------------------------|-------------|---------------------|---------------|
| R-HR-01 | Mangelndes Sicherheitsbewusstsein der Mitarbeitenden | Beschäftigte sind nicht regelmäßig geschult und fallen auf Phishing/Social Engineering herein oder handeln fahrlässig. | 2.1.2, 2.1.3 | Personal, Informationen | Awareness-Programm und jährliche Schulungen einführen, Phishing-Simulationen, Schulungsnachweise dokumentieren | E4/S3 — Mensch häufigster Angriffsvektor |
| R-HR-02 | Fehlende Vertraulichkeits-/Geheimhaltungsvereinbarungen | Mit Mitarbeitenden, Zeitarbeit oder Externen sind keine NDAs abgeschlossen, sodass der Schutz vertraulicher Informationen rechtlich nicht abgesichert ist. | 2.1.1 | Verträge, Personal | NDA/Verpflichtung auf Vertraulichkeit in Onboarding-Prozess verankern, Bestand nachpflegen | E3/S3 — häufige Lücke bei Externen/Zeitarbeit |
| R-HR-03 | Unsicheres On-/Offboarding (Berechtigungen bei Austritt) | Bei Eintritt, Wechsel oder Austritt werden Zugänge und Assets nicht zeitnah vergeben/entzogen, sodass verwaiste Konten und Datenmitnahme entstehen. | 2.1.x, 3.1.1 | Konten, Endgeräte | On-/Offboarding-Checkliste mit HR/IT, Fristen für Kontoentzug und Asset-Rückgabe, regelmäßiger Abgleich | E3/S3 — verwaiste Konten sind typischer Auditfund |
| R-HR-04 | Innentäter / Datenmitnahme durch Mitarbeitende | Berechtigte Personen entwenden oder missbrauchen vorsätzlich Informationen (z. B. Konstruktionsdaten) zum eigenen Vorteil oder für Wettbewerber. | 2.1.x, 5.2.x, 3.1.x | Informationen, geistiges Eigentum | Need-to-know umsetzen, Protokollierung sensibler Zugriffe, DLP-Ansätze, arbeitsrechtliche Regelungen | E2/S4 — selten, aber sehr hoher Schaden für IP |
---
## 4. Physische Sicherheit
| Risiko-ID | Titel | Beschreibung | Betroffene Controls (VDA ISA) | Asset-Typen | Standardmaßnahme(n) | Default (E/S) |
|-----------|-------|--------------|-------------------------------|-------------|---------------------|---------------|
| R-PHY-01 | Unberechtigter Zutritt zu Betriebs-/Sicherheitsbereichen | Fehlende Zutrittskontrolle ermöglicht Unbefugten Zugang zu Büros, Serverräumen oder Fertigung, mit Diebstahl- und Manipulationsrisiko. | 4.1.1, 4.1.2 | Gebäude, IT-Systeme | Zonenkonzept, Zutrittskontrollsystem/Schließplan, Besucherregelung mit Begleitung und Protokoll | E3/S3 — Grundschutzanforderung, oft lückenhaft |
| R-PHY-02 | Unzureichender Schutz von Server-/Technikräumen | Serverräume sind nicht ausreichend gegen Zutritt, Brand, Wasser, Strom-/Klimaausfall geschützt, was zu Ausfall oder Datenverlust führt. | 4.1.x, 7.x | IT-Infrastruktur | Technikraum absichern (Zutritt, USV, Klima, Brand-/Wasserschutz), Umgebungsüberwachung | E2/S4 — geringe Häufigkeit, hoher Ausfallschaden |
| R-PHY-03 | Diebstahl/Verlust von Datenträgern und Dokumenten | Papierunterlagen, USB-Medien oder Datenträger mit vertraulichen Informationen gehen verloren oder werden entwendet. | 4.1.x, 5.2.x | Datenträger, Dokumente | Clean-Desk-Policy, verschließbare Schränke, sichere Entsorgung (Schredder/zertifiziert), Medienverschlüsselung | E3/S3 — alltägliches Restrisiko |
---
## 5. Identitäts- und Zugriffsmanagement (IAM)
| Risiko-ID | Titel | Beschreibung | Betroffene Controls (VDA ISA) | Asset-Typen | Standardmaßnahme(n) | Default (E/S) |
|-----------|-------|--------------|-------------------------------|-------------|---------------------|---------------|
| R-IAM-01 | Fehlberechtigungen / überzogene Zugriffsrechte | Zugriffsrechte folgen nicht dem Need-to-know- und Least-Privilege-Prinzip; Sammelrechte und Rechteakkumulation entstehen. | 3.1.1, 3.1.2, 3.1.3 | Konten, Anwendungen, Daten | Rollen-/Rechtekonzept (RBAC), regelmäßige Rezertifizierung der Berechtigungen, Trennung von Funktionen | E4/S3 — häufigster Auditfund im IAM |
| R-IAM-02 | Schwache Authentisierung / fehlende MFA | Zugänge (insb. remote, Admin, Cloud) sind nur durch schwache Passwörter geschützt, wodurch Kontoübernahmen leicht möglich sind. | 3.1.4, 3.1.5 | Konten, Zugänge | Passwortrichtlinie (BL-IAM-*), MFA für Remote-/Admin-/Cloud-Zugänge verpflichtend, SSO | E4/S4 — Credential-Angriffe sehr häufig und wirksam |
| R-IAM-03 | Ungesicherte / geteilte privilegierte Konten | Administrative und Sammelkonten werden geteilt genutzt und nicht überwacht, sodass Handlungen nicht zurechenbar sind. | 3.1.x | Admin-Konten | Privileged-Access-Management, personalisierte Admin-Konten, Protokollierung, getrennte Admin-Arbeitsplätze | E3/S4 — Missbrauch privilegierter Rechte hochwirksam |
---
## 6. Kryptografie
| Risiko-ID | Titel | Beschreibung | Betroffene Controls (VDA ISA) | Asset-Typen | Standardmaßnahme(n) | Default (E/S) |
|-----------|-------|--------------|-------------------------------|-------------|---------------------|---------------|
| R-CRY-01 | Fehlende Verschlüsselung mobiler Geräte / Datenträger | Notebooks, Smartphones und Wechselmedien sind nicht verschlüsselt, sodass bei Verlust/Diebstahl Daten unmittelbar lesbar sind. | 5.1.1, 5.1.2 | Endgeräte, Datenträger | Full-Disk-Encryption (BitLocker/FileVault) verpflichtend, MDM-erzwungene Verschlüsselung, Medienrichtlinie | E3/S4 — Verlustfall führt sonst direkt zu Datenabfluss |
| R-CRY-02 | Unsichere Datenübertragung (fehlende Transportverschlüsselung) | Vertrauliche Daten werden unverschlüsselt (E-Mail, FTP, HTTP) übertragen und können abgefangen werden. | 5.1.1 | Kommunikation, Daten | TLS erzwingen, sichere Austauschwege/Portale, E-Mail-Verschlüsselung für vertrauliche Inhalte | E3/S3 — Abfangrisiko bei Zulieferaustausch |
| R-CRY-03 | Unzureichendes Schlüssel-/Zertifikatsmanagement | Kryptografische Schlüssel und Zertifikate werden nicht sicher verwaltet oder laufen unbemerkt ab, was zu Ausfällen oder Kompromittierung führt. | 5.1.x | Schlüssel, Zertifikate | Krypto-Richtlinie, zentrale Schlüssel-/Zertifikatsverwaltung, Ablaufüberwachung, sichere Aufbewahrung | E2/S3 — schleichender, aber wirkungsvoller Ausfall |
---
## 7. Betrieb / IT
| Risiko-ID | Titel | Beschreibung | Betroffene Controls (VDA ISA) | Asset-Typen | Standardmaßnahme(n) | Default (E/S) |
|-----------|-------|--------------|-------------------------------|-------------|---------------------|---------------|
| R-OPS-01 | Fehlendes/mangelhaftes Patch- und Schwachstellenmanagement | Betriebssysteme und Anwendungen werden nicht zeitnah aktualisiert, sodass bekannte Schwachstellen ausnutzbar bleiben. | 5.2.4, 5.2.5 | IT-Systeme, Software | Patchmanagement-Prozess mit Fristen nach Kritikalität, Schwachstellenscans, Vulnerability-Tracking | E4/S4 — meistgenutztes Einfallstor, hohe Angriffsfläche |
| R-OPS-02 | Schadsoftware / Ransomware-Befall | Malware gelangt über E-Mail, Wechselmedien oder Web ins Netz und verschlüsselt oder exfiltriert Daten. | 5.2.3 | IT-Systeme, Daten | Endpoint-Protection/EDR flächendeckend, E-Mail-/Web-Filter, Makro-Restriktionen, Awareness (R-HR-01) | E4/S5 — hohe Häufigkeit, potenziell existenzbedrohend |
| R-OPS-03 | Backup-/Wiederanlauf-Versagen | Datensicherungen fehlen, sind unvollständig, nicht getestet oder mitverschlüsselbar, sodass eine Wiederherstellung im Ernstfall scheitert. | 7.x | Daten, IT-Systeme | Backup-Konzept nach BL-OPS-05 (3-2-1, Offline-/Immutable-Kopie), regelmäßige Restore-Tests, RTO/RPO definieren | E3/S5 — im Ransomware-Fall entscheidend für Überleben |
| R-OPS-04 | Fehlende Protokollierung und Überwachung (Logging/Monitoring) | Sicherheitsrelevante Ereignisse werden nicht protokolliert oder ausgewertet, sodass Angriffe unentdeckt bleiben. | 5.2.6 | IT-Systeme, Logs | Zentrales Logging, Log-Auswertung/SIEM-Ansatz, Aufbewahrungsfristen, Alarmierung bei Auffälligkeiten | E3/S3 — verlängert Entdeckungszeit erheblich |
| R-OPS-05 | Schatten-IT / nicht genehmigte Software & Dienste | Mitarbeitende nutzen nicht freigegebene Cloud-Dienste oder Software, wodurch Daten unkontrolliert abfließen und Schwachstellen entstehen. | 5.2.x, 6.1.x | Anwendungen, Daten | Freigabeprozess für Software/Dienste, Anwendungsinventar, technische Restriktionen, Awareness | E3/S3 — verbreitet, schwer sichtbar |
| R-OPS-06 | Fehlkonfiguration / fehlendes Change-Management | Systeme werden ohne kontrollierte Änderungsprozesse betrieben, sodass Fehlkonfigurationen Sicherheitslücken und Ausfälle verursachen. | 5.2.1, 5.2.2 | IT-Systeme, Konfiguration | Härtungs-/Konfigurationsvorgaben (Baselines), Change-Management-Prozess, Test vor Produktivsetzung | E3/S3 — Fehlkonfiguration häufige Ausfall-/Angriffsursache |
| R-OPS-07 | Verlust/Diebstahl mobiler Endgeräte | Notebooks, Smartphones oder Tablets gehen unterwegs verloren oder werden gestohlen und ermöglichen Zugriff auf Daten und Systeme. | 5.2.x, 5.1.1 | Endgeräte, Daten | MDM mit Remote-Wipe, Geräteverschlüsselung (R-CRY-01), Bildschirmsperre, Verlustmeldeprozess | E3/S3 — mobiles Arbeiten erhöht Häufigkeit |
| R-OPS-08 | Fehlende sichere Entsorgung / Wiederverwendung von IT | Ausgemusterte Geräte und Datenträger werden ohne sichere Löschung entsorgt oder weitergegeben, sodass Restdaten abfließen. | 5.2.x, 4.1.x | Datenträger, Endgeräte | Prozess zur sicheren Löschung/Vernichtung, zertifizierte Entsorgung, Nachweisführung | E2/S3 — selten, aber unbemerkter Datenabfluss |
---
## 8. Netzwerk
| Risiko-ID | Titel | Beschreibung | Betroffene Controls (VDA ISA) | Asset-Typen | Standardmaßnahme(n) | Default (E/S) |
|-----------|-------|--------------|-------------------------------|-------------|---------------------|---------------|
| R-NET-01 | Unsichere Netzsegmentierung / flaches Netz | Fehlende Trennung zwischen Office-, Produktions-/OT- und Gastnetzen ermöglicht laterale Ausbreitung von Angriffen. | 5.2.x | Netzwerk, IT-Systeme | Netzsegmentierung (VLAN/Zonen), Firewall-Regelwerk, Trennung OT/IT und Gast-WLAN | E3/S4 — begünstigt großflächige Kompromittierung |
| R-NET-02 | Unsicherer Remote-/VPN-Zugang | Fernzugriffe sind nicht ausreichend abgesichert (kein MFA, offene Ports, veraltete VPN-Gateways) und werden angegriffen. | 5.2.x, 3.1.4 | Zugänge, Netzwerk | Gehärtetes VPN mit MFA, Zugriff nach Least-Privilege, Patching der Gateways, Zugriffprotokollierung | E3/S4 — Remote-Zugänge sind bevorzugtes Angriffsziel |
| R-NET-03 | Unzureichender Perimeter-/Firewall-Schutz | Fehlende oder falsch konfigurierte Firewalls und exponierte Dienste erlauben direkte Angriffe aus dem Internet. | 5.2.x | Netzwerk, IT-Systeme | Firewall mit restriktivem Regelwerk, regelmäßige Regel-Reviews, Minimierung exponierter Dienste, externe Scans | E3/S4 — exponierte Dienste werden automatisiert angegriffen |
---
## 9. Lieferanten / Cloud
| Risiko-ID | Titel | Beschreibung | Betroffene Controls (VDA ISA) | Asset-Typen | Standardmaßnahme(n) | Default (E/S) |
|-----------|-------|--------------|-------------------------------|-------------|---------------------|---------------|
| R-SUP-01 | Lieferanten/Dienstleister ohne IS-Nachweis | Externe Partner mit Zugriff auf Informationen/Systeme verfügen über kein nachgewiesenes IS-Niveau (z. B. TISAX/ISO 27001), was Risiken in die Lieferkette trägt. | 6.1.1, 6.1.2 | Verträge, Lieferanten | Lieferantenbewertung, IS-Anforderungen und Nachweise vertraglich fordern, TISAX-Label bei sensiblen Daten | E3/S4 — Kettenrisiko, TISAX-relevant für Weitergabe |
| R-SUP-02 | Unzureichende vertragliche IS-Regelungen mit Dienstleistern | Verträge enthalten keine Vorgaben zu Vertraulichkeit, Rückgabe/Löschung, Audit- und Meldepflichten. | 6.1.x | Verträge | Standard-IS-Vertragsklauseln/Anlage, Regelungen zu Löschung, Subunternehmern, Meldung von Vorfällen | E3/S3 — Nachweislücke bei Audit |
| R-SUP-03 | Abhängigkeit von Cloud-Diensten / Datenabfluss in die Cloud | Vertrauliche Daten liegen bei Cloud-Anbietern ohne ausreichende Kontrolle über Standort, Zugriff und Ausfallabsicherung. | 6.1.x, 9.x | Cloud-Dienste, Daten | Cloud-Nutzungsrichtlinie, Anbieterprüfung (Zertifikate, Standort), Verschlüsselung, AVV bei Personenbezug | E3/S4 — Kontrollverlust und Lock-in |
| R-SUP-04 | Kompromittierung über die Software-Lieferkette | Manipulierte Updates, Bibliotheken oder Fernwartungszugänge von Dienstleistern führen zu Kompromittierungen. | 6.1.x, 5.2.x | Software, Zugänge | Bezugsquellen prüfen, Signaturen verifizieren, Fernwartungszugänge kontrollieren und protokollieren | E2/S4 — selten, aber weitreichende Wirkung |
---
## 10. Entwicklung
| Risiko-ID | Titel | Beschreibung | Betroffene Controls (VDA ISA) | Asset-Typen | Standardmaßnahme(n) | Default (E/S) |
|-----------|-------|--------------|-------------------------------|-------------|---------------------|---------------|
| R-DEV-01 | Abfluss von Entwicklungs-/Konstruktionsdaten | Vertrauliche Entwicklungsdaten (CAD, Spezifikationen, Quellcode) gelangen unkontrolliert nach außen (Cloud, Mail, private Geräte). | 5.2.x, 8.x, 6.1.x | Entwicklungsdaten, IP | Klassifizierung und Zugriffsbeschränkung (Need-to-know), sichere Austauschwege, DLP, Repository-Absicherung | E3/S5 — Kernwert des Zulieferers, hoher IP-Schaden |
| R-DEV-02 | Unsichere Software-Entwicklung (fehlender Secure SDLC) | Sicherheitsanforderungen werden in Entwicklung und Tests nicht berücksichtigt, sodass verwundbare Produkte/Software entstehen. | 5.2.x | Quellcode, Anwendungen | Secure-Coding-Vorgaben, Code-Reviews/SAST, Sicherheitstests, Trennung Entwicklungs-/Produktivumgebung | E3/S3 — je nach Entwicklungsanteil relevant |
| R-DEV-03 | Testdaten mit Echt-/Produktivdaten | In Entwicklungs- und Testumgebungen werden ungeschützte Produktiv- oder Personendaten verwendet und exponiert. | 5.2.x, 9.x | Testdaten, Personendaten | Anonymisierung/Pseudonymisierung von Testdaten, Zugriffsbeschränkung Testumgebung, Löschkonzept | E3/S3 — verbreitete Praxis, auch datenschutzrelevant |
---
## 11. Prototypenschutz
| Risiko-ID | Titel | Beschreibung | Betroffene Controls (VDA ISA) | Asset-Typen | Standardmaßnahme(n) | Default (E/S) |
|-----------|-------|--------------|-------------------------------|-------------|---------------------|---------------|
| R-PROTO-01 | Unberechtigter Zutritt zu Prototypen-/Musterbereichen | Unbefugte erlangen Zugang zu Bereichen mit Prototypen, Musterteilen oder Vorserien und können diese einsehen, fotografieren oder entwenden. | 8.1.x, 8.2.x | Prototypen, Räumlichkeiten | Sicherheitszonen mit Zutrittskontrolle, Begleitpflicht, Kamera-/Fotografierverbot, Besucherregelung | E3/S4 — TISAX-Prototypenschutz Kernrisiko |
| R-PROTO-02 | Unzureichender Schutz bei Erprobung/Transport von Prototypen | Bei Fahrzeugerprobung, Foto/Film oder Transport werden Prototypen unzureichend getarnt/gesichert und Dritten zugänglich. | 8.3.x, 8.4.x | Prototypen, Fahrzeuge | Tarnung/Abdeckung, gesicherte Transporte, Regelungen für Erprobung und Foto/Film, Geheimhaltungszonen | E2/S4 — seltener, aber öffentlichkeitswirksamer Schaden |
| R-PROTO-03 | Informationsabfluss zu geschützten Produkten/Projekten | Informationen zu geheimhaltungsbedürftigen Fahrzeugprojekten (Bilder, Daten, Termine) gelangen an Unbefugte oder in soziale Medien. | 8.1.x, 8.2.x | Projektinformationen | Verschärfte Klassifizierung/Kennzeichnung, Need-to-know, Schulung Prototypenschutz, Mobilgeräte-Restriktionen | E3/S4 — Reputations- und Vertragsschaden beim OEM |
---
## 12. Datenschutz
| Risiko-ID | Titel | Beschreibung | Betroffene Controls (VDA ISA) | Asset-Typen | Standardmaßnahme(n) | Default (E/S) |
|-----------|-------|--------------|-------------------------------|-------------|---------------------|---------------|
| R-DSGVO-01 | Datenschutzverstoß / meldepflichtige Datenpanne | Personenbezogene Daten werden unrechtmäßig offengelegt, verloren oder verarbeitet, was zu Meldepflicht (Art. 33/34), Bußgeld und Reputationsschaden führt. | 9.x | Personendaten | Datenschutz-Managementprozess, Datenpannen-Meldeprozess (72 h), technische/organisatorische Maßnahmen (TOM) | E3/S4 — hohe Bußgeld- und Meldepflichtrelevanz |
| R-DSGVO-02 | Fehlende Auftragsverarbeitungsverträge (AVV) | Für Dienstleister, die personenbezogene Daten verarbeiten, fehlen AVV nach Art. 28 DSGVO, sodass Verarbeitung unrechtmäßig ist. | 9.x, 6.1.x | Verträge, Personendaten | AVV-Prozess etablieren, Bestand prüfen und nachholen, Dienstleister mit Personenbezug erfassen | E3/S3 — häufige Lücke, aufsichtsrelevant |
| R-DSGVO-03 | Fehlendes Verarbeitungsverzeichnis / keine Rechtsgrundlagen | Verarbeitungstätigkeiten sind nicht dokumentiert (Art. 30) oder ohne Rechtsgrundlage, sodass Nachweispflichten verletzt werden. | 9.x | Dokumentation, Personendaten | Verzeichnis von Verarbeitungstätigkeiten führen, Rechtsgrundlagen prüfen, Löschkonzept/Fristen definieren | E3/S3 — Rechenschaftspflicht, Auditfund |
| R-DSGVO-04 | Unzulässige Übermittlung in Drittländer | Personenbezogene Daten werden ohne geeignete Garantien in Drittländer (z. B. über Cloud-Dienste) übermittelt. | 9.x, 6.1.x | Personendaten, Cloud-Dienste | Datenflüsse prüfen, Standardvertragsklauseln/Transfer-Impact-Assessment, EU-Verarbeitung bevorzugen | E2/S4 — komplex, hohe aufsichtsrechtliche Relevanz |
---
## 13. Hinweise zur Pflege des Katalogs
- Der Katalog ist eine **Startvorlage**; jede Organisation passt Auswahl, Beschreibung, Controls-Zuordnung und Bewertung an ihren Scope an.
- Controls-Angaben referenzieren die **Struktur** der VDA ISA (Kapitel 1–9). Die genauen Unter-Control-Nummern sind bei Nutzung gegen die aktuell gültige VDA-ISA-Version zu verifizieren.
- Standardmaßnahmen verweisen, wo möglich, auf vorhandene Vorlagen/Verfahren (R03, VA-09) und Baseline-Parameter (z. B. **BL-OPS-05** Backup). Weitere BL-* sind bei Umsetzung zu ergänzen.
- Jede aus einem Risiko abgeleitete Maßnahme sollte eine **Aufgabe** mit Verantwortlichem, Termin und Statusverfolgung erzeugen (VA-09).
_Ende Paket C4 — Standard-Risikokatalog._
@@ -1,199 +0,0 @@
# Paket C5 — Reifegrad-Logik je Control (Onboarding-Wizard, Schritt 7 „Control-Assessment")
**Zweck:** Regelbasierte, nachvollziehbare Herleitung eines **Reifegrad-Vorschlags (0–3)** je Control aus den im Wizard vorhandenen Belegen (verknüpfte Dokumente, Risiken, Assets) und deren Validierungsstatus. Der Vorschlag ist **nicht bindend** — die Bestätigung/Überschreibung durch den Bearbeiter ist Pflicht (Vier-Augen-Logik über die Rolle des ISB/Assessors).
**Grundlage:** VDA-ISA-Reifegradmodell 0–3
- **0 — unvollständig:** Anforderung nicht oder nur zufällig erfüllt, keine belastbaren Belege.
- **1 — durchgeführt/informell:** Ergebnis wird erreicht, aber ohne festgelegte, dokumentierte Vorgehensweise.
- **2 — gesteuert/dokumentiert:** Vorgehen ist definiert, dokumentiert (Richtlinie **und** Verfahren) und mit Nachweisen belegt.
- **3 — etabliert/integriert:** Vorgehen ist in die Organisation integriert und seine **Wirksamkeit wird über die Zeit nachgewiesen** (wiederkehrende Reviews/Protokolle/Tests).
---
## 1. Belegtypen und Validierungsstatus (Datenmodell-Bezug)
Der Wizard kennt je Control folgende verknüpfbare Belegklassen:
| Belegklasse | Herkunft im Fachcontent | Beispiel |
|---|---|---|
| **Zuständige Richtlinie (P)** | Feld `policy` (L00, R01–R14) | Leitlinie, Richtlinie |
| **Zugehöriges Verfahren (V)** | Feld `verfahren` (VA-01 … VA-19) | Verfahrensanweisung / Prozess |
| **Operativer Nachweis (N)** | control-spezifisch (Protokoll, Report, Register, Ticket, Testnachweis) | Review-Protokoll, Auditbericht |
| **Asset-Verknüpfung (A)** | Asset-Inventar | zugeordnete Informationswerte/Systeme |
| **Risiko-Verknüpfung (R)** | Risikoregister | zugeordnete Risiken + Risk Owner |
**Validierungsstatus je Beleg:** `fehlt` → `verknüpft (unvalidiert)` → `validiert` (durch Bearbeiter bestätigt, vorhanden, aktuell). Ein Beleg zählt für Reifegrad-Sprünge nach ≥2 nur, wenn er **`validiert`** ist.
**Aktualitätsregel (für N):** Ein operativer Nachweis gilt als „aktuell", wenn er innerhalb des für das Control definierten Review-Zyklus liegt (Standard: ≤ 12 Monate; anlassbezogene Verfahren: letzter relevanter Vorfall/Change abgedeckt). Veraltete Nachweise zählen wie `verknüpft (unvalidiert)`.
---
## 2. Allgemeine Regeltabelle „Belegkonstellation → Reifegrad-Vorschlag" (generisch, für alle Controls)
| # | Belegkonstellation | Vorschlag | Begründung |
|---|---|---|---|
| R0 | Keine Belege verknüpft **oder** nur unvalidierte Fragmente ohne zuständige Richtlinie | **0** | Kein belastbarer Nachweis einer geregelten Vorgehensweise. |
| R1a | Zuständige Richtlinie (P) verknüpft, aber **nicht validiert** | **1** | Regelungsabsicht erkennbar, aber nicht bestätigt/gelebt (informell). |
| R1b | Richtlinie (P) validiert, **aber** ein für das Control **gefordertes Verfahren (V) fehlt** oder ist unvalidiert | **1** | Richtlinie allein steuert die operative Umsetzung noch nicht. |
| R1c | Nur operativer Nachweis (N) vorhanden, **ohne** validierte Richtlinie | **1** | Tätigkeit wird durchgeführt, aber nicht dokumentiert gesteuert. |
| R2 | Richtlinie (P) **validiert** **UND** alle geforderten Verfahren (V) **validiert** **UND** (falls einschlägig) Asset-/Risiko-Verknüpfung vorhanden | **2** | Vorgehen ist dokumentiert und gesteuert, Nachweise liegen strukturell vor. |
| R3 | Zusätzlich zu R2: mindestens **ein operativer Wirksamkeitsnachweis (N) validiert und aktuell** (Review-/Audit-/Test-/Schulungs-/Rezertifizierungsprotokoll) | **3** | Wirksamkeit wird über die Zeit belegt, Vorgehen ist integriert. |
**Sonderfälle:**
- Controls **ohne** zugeordnetes Verfahren im Fachcontent (`verfahren` = leer): Die operative Steuerung ist in der Richtlinie selbst verankert. Für Grad 2 tritt an die Stelle von (V) eine **validierte, dokumentierte Umsetzungsregelung/Konfiguration (N-basisch)**; Grad 3 erfordert weiterhin einen **wiederkehrenden Wirksamkeitsnachweis**.
- Controls, die nur `SOLL` enthalten (z. B. 5.3.3): MUSS-Ebene ist definitionsgemäß erfüllt; Bewertung startet effektiv bei 1 und folgt ansonsten der Tabelle.
- **Nachweis widerspricht Richtlinie / offene Findings im Nachweis:** Vorschlag wird um einen Grad gedeckelt (max. 1) und ein offener Punkt erzeugt.
---
## 3. Zielreifegrad-Regel
| Assessment-Scope (Kundenkonfiguration) | Standard-Zielreifegrad |
|---|---|
| **AL 3** / normaler Schutzbedarf (MUSS + SOLL im Scope) | **3** |
| **AL 2** / reiner **MUSS-Scope** | **2** |
| Control mit HOCH/SEHR-HOCH-Anforderungen im Scope (hoher/sehr hoher Schutzbedarf) | **3** (Grad 3 wird zur Pflicht, keine Absenkung) |
- Der Zielreifegrad wird **einmal je Assessment** aus dem Scope (AL2/AL3, Schutzbedarf-Flags `FLAG_INCLUDE_SHOULD`, `FLAG_HIGH_PROTECTION`, `FLAG_VERY_HIGH_PROTECTION`) abgeleitet und je Control angezeigt.
- In der Control-Tabelle (Abschnitt 5) ist der **Standard-Zielreifegrad = 3** ausgewiesen; er sinkt automatisch auf **2**, sobald der Kunde im Onboarding einen reinen MUSS-/AL2-Scope wählt.
---
## 4. „Offener-Punkt"-Kriterium (Gap/Aufgabe entsteht, wenn …)
Ein **offener Punkt (Gap → Aufgabe)** wird automatisch erzeugt, wenn **eine** der folgenden Bedingungen zutrifft:
1. **Vorschlag < Zielreifegrad** (Reifegradlücke). → Aufgabe: „Belege/Umsetzung bis Zielgrad X anheben".
2. **Pflichtbeleg fehlt:** zuständige Richtlinie (P) oder ein gefordertes Verfahren (V) ist `fehlt`/unvalidiert. → Aufgabe: Dokument erstellen/verknüpfen/validieren.
3. **Operativer Nachweis fehlt oder ist veraltet**, obwohl Zielgrad 3. → Aufgabe: Review/Audit/Test durchführen und dokumentieren.
4. **Asset-/Risiko-Verknüpfung fehlt** bei Controls, die auf Inventar/Risikoregister aufsetzen (z. B. 1.3.x, 1.4.1, 5.2.8). → Aufgabe: Verknüpfung herstellen.
5. **Nachweis mit Findings/Widerspruch** (Deckelung nach Abschnitt 2). → Aufgabe: Abweichung beheben.
6. **Bearbeiter überschreibt Vorschlag nach unten** oder markiert „nicht bestätigt". → Aufgabe mit Begründungspflicht.
Jeder offene Punkt trägt: Control-ID, fehlende Belegklasse, Ist-Grad, Zielgrad, empfohlene Maßnahme, Verantwortlichen (Default: ISB).
---
## 5. Control-spezifische Tabelle — IS-Controls (alle 45)
**Legende Richtlinien (`policy`):** L00 = Informationssicherheits-Leitlinie · R01 = ISMS-Organisation/Rollen · R02 = Umgang mit Informationswerten (Asset-Mgmt) · R03 = Risikomanagement & Wirksamkeitsprüfung · R04 = Incident-/Notfall-/Kontinuitätsmanagement · R05 = Personalsicherheit · R06 = Mobiles Arbeiten/Mobile Geräte · R07 = Physische Sicherheit/Zonenkonzept · R08 = Zugriffssteuerung (IAM) · R09 = Kryptografie/Kommunikationssicherheit · R10 = IT-Betriebssicherheit · R11 = Systementwicklung/Beschaffung · R12 = Cloud/externe IT-Dienste & KI · R13 = Lieferantenmanagement · R14 = Compliance/Recht & Datenschutz.
**Legende Verfahren (`verfahren`, abgeleitete Bezeichnungen):** VA-01 Ereignisbehandlung · VA-02 Notfall-/Kontinuitätsmgmt · VA-03 Benutzer-/Berechtigungsverwaltung · VA-04 Change-Management · VA-05 Backup & Wiederherstellung · VA-06 Schwachstellen-/Patch-/Audit-Mgmt · VA-07 Kryptografie/sichere Übertragung · VA-08 Asset-Inventarisierung & Klassifizierung · VA-09 Risikomanagement · VA-10 Lieferantensteuerung · VA-11 Cloud-/KI-Freigabe · VA-12 Schulung & Sensibilisierung · VA-13 Protokollierung/Monitoring · VA-14 Personalprozess · VA-15 Interne Audits/Compliance-Prüfung · VA-17 Zutrittsmanagement · VA-16 Sichere Entwicklung/Beschaffung · VA-18 Rechts-/Datenschutzkataster · VA-19 Projektklassifizierung.
Format: **Reifegrad-2-Bedingung** = Vorgehen dokumentiert & gesteuert · **Reifegrad-3-Bedingung** = zusätzlich validierter, aktueller Wirksamkeitsnachweis.
| Control | Erwartete Belege (Richtlinie / Verfahren / operativer Nachweis) | Reifegrad-2-Bedingung | Reifegrad-3-Bedingung | Ziel |
|---|---|---|---|---|
| **1.1.1** IS-Leitlinie | L00 / — / Managementfreigabe + Veröffentlichungsnachweis (Intranet), Änderungskommunikation | L00 validiert, Freigabe u. Bereitstellung dokumentiert | Wiederkehrender Leitlinien-Review (≤12 M.) + Nachweis Änderungskommunikation | 3 |
| **1.2.1** ISMS/Geltungsbereich | R01 / — / Anwendbarkeitserklärung (SoA)/ISA-Katalog, Management-Review-Protokoll | R01 validiert, Scope + SoA dokumentiert | Regelmäßige Managementbewertung mit Wirksamkeitsaussage | 3 |
| **1.2.2** Rollen & Verantwortlichkeiten | R01 / — / Rollen-/Verantwortungsmatrix, ISB-Ernennung, Qualifikationsnachweis | R01 validiert, Rollen zugewiesen u. dokumentiert | Periodische Überprüfung der Rollenbesetzung/Qualifikation | 3 |
| **1.2.3** Projektklassifizierung | R01 / VA-19 / ausgefüllte Projektklassifizierung + Projekt-Risikobewertung | R01 + VA-19 validiert | Nachweis wiederholter Klassifizierung/Neubewertung über Projekte | 3 |
| **1.3.1** Identifizierung Informationswerte | R02 / VA-08 / aktuelles Asset-/Informationswert-Inventar (**A**) | R02 + VA-08 validiert, Inventar (A) verknüpft | Nachweis regelmäßiger Inventarpflege/-abgleich | 3 |
| **1.3.2** Klassifizierung Informationswerte | R02 / VA-08 / Klassifizierungsschema + klassifizierte Assets (**A**) | R02 + VA-08 validiert, Schema angewandt | Regelmäßige Überprüfung/Neubewertung der Klassifizierung | 3 |
| **1.3.3** Externe IT-Dienste | R02 / — / Freigabe-/Bestandsliste externer Dienste, Freigabeverfahren | R02 validiert + dokumentierte Freigabeliste u. -regelung | Regelmäßige Prüfung, dass nur freigegebene Dienste genutzt werden | 3 |
| **1.3.4** Software-Freigabe | R02 / — / Softwarefreigabeliste/Whitelist, Versions-/Patchstand | R02 validiert + dokumentierte Freigabeliste | Regelmäßige Überprüfung der Softwarefreigaben | 3 |
| **1.4.1** Risikomanagement | R03 / VA-09 / Risikoregister mit Risk Ownern (**R**), Maßnahmenplan | R03 + VA-09 validiert, Risikoregister (R) verknüpft | Regelmäßige/anlassbezogene Neubewertung + Maßnahmen-Tracking | 3 |
| **1.5.1** Compliance-/Richtlinienprüfung | R03 / VA-15 / Prüfplan + Prüfaufzeichnungen/Ergebnisse | R03 + VA-15 validiert, Prüfplan vorhanden | Durchgeführte Prüfungen dokumentiert + Korrekturmaßnahmen verfolgt | 3 |
| **1.5.2** Unabhängige Überprüfung | R03 / VA-15 / Bericht unabhängiger/kompetenter Stelle, Managementbericht | R03 + VA-15 validiert | Durchgeführte unabhängige Prüfung + Maßnahmenverfolgung nachgewiesen | 3 |
| **1.6.1** Meldung Sicherheitsereignisse | R04 / VA-01 / dokumentierte Meldewege/-kanäle, Meldeformular/Ticketsystem | R04 + VA-01 validiert, Meldewege bekannt | Nachweis genutzter Meldewege (Tickets) + ggf. Test der Meldung | 3 |
| **1.6.2** Behandlung Sicherheitsereignisse | R04 / VA-01 / Incident-Log/Tickethistorie mit Lessons Learned | R04 + VA-01 validiert | Bearbeitete Vorfälle + Lessons-Learned-Rückfluss nachgewiesen | 3 |
| **1.6.3** Krisen-/Notfallmanagement | R04 / VA-02 / Krisen-/Notfallplan, Krisenstab, Übungs-/Testprotokoll | R04 + VA-02 validiert, Plan u. Rollen dokumentiert | Durchgeführte Krisenübung/Test + Aktualisierung der Planung | 3 |
| **2.1.1** Personalprüfung (Eignung/Identität) | R05 / VA-14 / Einstellungsprozess, dokumentierte Identitätsprüfung | R05 + VA-14 validiert | Nachweis gelebter Prüfung über Einstellungen (Stichprobe) | 3 |
| **2.1.2** Verpflichtungen (NDA/Richtlinien) | R05 / VA-14 / unterzeichnete Vertraulichkeits-/Richtlinienverpflichtungen | R05 + VA-14 validiert, Verpflichtungen vorhanden | Nachweis flächendeckender, aktueller Verpflichtungen | 3 |
| **2.1.3** Schulung & Sensibilisierung | R05 / VA-12 / Schulungskonzept + Teilnahmenachweise | R05 + VA-12 validiert, Konzept genehmigt | Regelmäßig durchgeführte Schulungen + Teilnahmedokumentation | 3 |
| **2.1.4** Mobiles Arbeiten | R06 / — / kommunizierte Regelung, Sensibilisierungsnachweis | R06 validiert + dokumentierte Anforderungen | Nachweis Sensibilisierung + Überprüfung der Umsetzung | 3 |
| **3.1.1** Sicherheitszonenkonzept | R07 / VA-17 / Zonenkonzept + Umsetzungsnachweis, Zutritts-/Besucherverfahren | R07 + VA-17 validiert, Zonenkonzept umgesetzt | Regelmäßige Überprüfung von Zonen/Zutritt/Besuchermgmt | 3 |
| **3.1.4** Mobile Geräte/Datenträger | R06 / — / Regelung + Geräteregistrierung/MDM-Nachweis | R06 validiert + dokumentierte Anforderungen/Registrierung | Nachweis Umsetzung (MDM/Verschlüsselung) + Überprüfung | 3 |
| **4.1.1** Identifikationsmittel (Lebenszyklus) | R08 / VA-03 / Ausgabe-/Rückgabe-/Sperrprozess, Nachweis kontrollierte Erstellung | R08 + VA-03 validiert | Nachweis gelebter Sperr-/Rückgabeprozesse (z. B. Verlustfall) | 3 |
| **4.1.2** Benutzerauthentifizierung | R08 / — / Authentifizierungskonzept, techn. Umsetzung (Passwortpolicy/MFA) | R08 validiert + dokumentiertes Auth-Konzept | Nachweis wirksamer Umsetzung (MFA-/Policy-Report) + Review | 3 |
| **4.1.3** Verwaltung Benutzerkonten | R08 / VA-03 / Kontenverwaltungsprozess, Konten-Rezertifizierung | R08 + VA-03 validiert | Regelmäßige Konten-Überprüfung/Rezertifizierung nachgewiesen | 3 |
| **4.2.1** Verwaltung Zugriffsrechte | R08 / VA-03 / Berechtigungskonzept, Rechte-Review/Rezertifizierung | R08 + VA-03 validiert | Regelmäßige Rechte-Rezertifizierung nachgewiesen | 3 |
| **5.1.1** Kryptografie | R09 / VA-07 / Kryptokonzept, Nachweis eingesetzter Verfahren | R09 + VA-07 validiert, Konzept umgesetzt | Regelmäßige Überprüfung der Krypto-Verfahren/Stand der Technik | 3 |
| **5.1.2** Netzdienste/sichere Übertragung | R09 / VA-07 / Netzdienst-Inventar, Verschlüsselungsnachweis | R09 + VA-07 validiert, Dienste dokumentiert | Nachweis wirksamer (Transport-)Verschlüsselung + Überprüfung | 3 |
| **5.2.1** Change-Management | R10 / VA-04 / Change-Prozess, Change-Tickets/CAB-Protokolle | R10 + VA-04 validiert | Nachweis gelebter Changes inkl. IS-Bewertung + Verifikation | 3 |
| **5.2.2** Trennung Entw./Test/Prod | R10 / — / Risikobewertung + Segmentierungsnachweis | R10 validiert + dokumentierte Segmentierung | Überprüfung der Trennung/Segmentierung im Betrieb | 3 |
| **5.2.3** Schutz vor Schadsoftware | R10 / — / AV-Konzept, AV-Konsole/Status-Report | R10 validiert + dokumentierte Schutzmaßnahmen | Aktueller AV-Statusreport + regelmäßige Überprüfung | 3 |
| **5.2.4** Protokollierung/Logging | R10 / VA-13 / Logging-Konzept, Log-Review-Nachweis | R10 + VA-13 validiert | Nachweis regelmäßiger Log-Auswertung/Eskalation | 3 |
| **5.2.5** Umgang mit Schwachstellen | R10 / VA-04, VA-06 / Schwachstellen-/Patchprozess, Scan-/Patchreports | R10 + VA-04 + VA-06 validiert | Aktuelle Scan-/Patchreports + Risikobehandlung nachgewiesen | 3 |
| **5.2.6** Prüfung von IT-Systemen (Audit) | R10 / VA-06 / Prüfanforderungen, Audit-/Pentestberichte | R10 + VA-06 validiert | Durchgeführte System-/Dienstprüfungen + Maßnahmen nachgewiesen | 3 |
| **5.2.7** Netzwerkmanagement | R10 / — / Netzkonzept/Segmentierung, Umsetzungsnachweis | R10 validiert + dokumentiertes Netzkonzept | Überprüfung von Netzmgmt/Segmentierung im Betrieb | 3 |
| **5.2.8** Kontinuität IT-Dienste (BCM) | R04 / VA-02 / kritische Dienste identifiziert (**A**), Kontinuitätsplan, Testprotokoll | R04 + VA-02 validiert, kritische Dienste (A) erfasst | Durchgeführter Kontinuitäts-/Wiederherstellungstest nachgewiesen | 3 |
| **5.2.9** Backup & Wiederherstellung | R10 / VA-05 / Backup-/Restore-Konzept, Restore-Testprotokoll | R10 + VA-05 validiert, Konzepte vorhanden | Durchgeführter Restore-Test (regelmäßig) nachgewiesen | 3 |
| **5.3.1** Sichere Systementwicklung/Beschaffung | R11 / VA-16 / IS-Anforderungen, Abnahme-/Testnachweise | R11 + VA-16 validiert | Nachweis durchgeführter Abnahmetests/Security-Tests | 3 |
| **5.3.2** Sicherheit Netzdienste | R11 / VA-16 / SLA/Absicherungsverfahren, Überwachungsnachweis | R11 + VA-16 validiert | Nachweis Überwachung/Qualitätssicherung der Netzdienste | 3 |
| **5.3.3** Beendigung IT-Dienste (nur SOLL) | R11 / — / vertraglich geregelter Beendigungsprozess, Nachweis Rückgabe/Löschung | R11 validiert + dokumentierter Beendigungsprozess | Nachweis gelebter Beendigung (Datenrückgabe/-löschung) | 3 |
| **5.3.4** Mandantentrennung (Cloud) | R12 / VA-11 / Trennungskonzept des Anbieters, Nachweis | R12 + VA-11 validiert | Regelmäßige Überprüfung/Anpassung des Trennungskonzepts | 3 |
| **5.3.4-KI** KI-/GenAI-Nutzung | R12 / VA-11 / KI-Nutzungsregelung + Freigabeliste, vertragl. Trainings-Ausschluss | R12 + VA-11 validiert, Freigabeliste + Datenklassen | Human-in-the-Loop-/Dokumentationsnachweis + Überprüfung | 3 |
| **6.1.1** Lieferantenmanagement | R13 / VA-10 / Lieferantenbewertung, Verträge + Nachweise/Zertifikate | R13 + VA-10 validiert, Bewertung + Verträge | Regelmäßige Überprüfung Nachweise/Vertragserfüllung (Monitoring) | 3 |
| **6.1.2** Vertraulichkeitsvereinbarungen | R13 / VA-10 / NDA-Vorlagen + abgeschlossene NDAs, Gültigkeitsüberwachung | R13 + VA-10 validiert, Vorlagen + NDAs | Regelmäßige Überprüfung/Verlängerung + gelebte NDA-Nutzung | 3 |
| **6.1.3** Geteilte Verantwortlichkeiten (Shared Resp.) | R13 / VA-10 / Verantwortungsmatrix, Nachweis Erfüllung durch Dienstleister | R13 + VA-10 validiert, Matrix dokumentiert | Nachweis, dass Dienstleister Verantwortung erfüllen (Prüfung) | 3 |
| **7.1.1** Rechtliche/vertragliche Vorgaben | R14 / VA-18 / Rechts-/Compliance-Kataster, Aktualisierungsnachweis | R14 + VA-18 validiert, Kataster vorhanden | Regelmäßige Aktualisierung des Katasters + Umsetzungsnachweis | 3 |
| **7.1.2** Datenschutz-relevante IS-Anforderungen | R14 / VA-18 / dokumentierte DS-Anforderungen, Umsetzungsnachweis im ISMS | R14 + VA-18 validiert, Anforderungen bestimmt | Nachweis Umsetzung/Überprüfung im ISMS-Kontext | 3 (MUSS-only¹) |
¹ 7.1.2 enthält nur MUSS-Anforderungen: In reinem AL2-/MUSS-Scope Zielreifegrad **2**; ansonsten 3.
---
## 6. Proto (8.x) und Datenschutz (9.x) — kompakte Gruppenlogik
Für Prototypenschutz und Datenschutz sind im Fachcontent **keine Verfahren (VA)** hinterlegt; die Anforderungen sind überwiegend **MUSS** und stark auftraggeber-/rechtsgetrieben. Die Reifegradlogik wird daher vereinfacht: Für Grad 2 tritt an die Stelle des Verfahrens die **dokumentierte, umgesetzte Regelung** (Konzept/Prozess/Vereinbarung); Grad 3 erfordert einen **wiederkehrenden Wirksamkeits-/Umsetzungsnachweis**.
**Generische Regel Proto/DS:**
- **0** — keine Regelung/kein Konzept belegt.
- **1** — Konzept/Vereinbarung vorhanden, aber unvalidiert oder nur informell umgesetzt.
- **2** — Konzept/Prozess/Vereinbarung **validiert und umgesetzt** (physische Maßnahme, Vertrag, dokumentierter Prozess).
- **3** — zusätzlich **validierter, aktueller Nachweis der gelebten Umsetzung** (Auditbericht, Alarmverfolgung, Schulungsnachweis, Besucherprotokoll, DSFA-Register, VVT-Pflege, Auftragskontrollen).
**Zielreifegrad Proto/DS:** überwiegend **2** (reine MUSS-Anforderungen); für Gruppen mit SOLL/HOCH-Anteil bzw. hohem Schutzbedarf **3**.
### 6.1 Prototypenschutz (8.x)
| Gruppe | Controls | Erwartete Belege (Richtlinie/Regelung / operativer Nachweis) | Grad-2-Bedingung | Grad-3-Bedingung | Ziel |
|---|---|---|---|---|---|
| **8.1 Physische Sicherheit** | 8.1.1–8.1.8 | Sicherheitskonzept Prototypenschutz (Außenhaut, Zutritt, Einblickschutz, EMA/Alarmverfolgung, Besuchermgmt, Mandantentrennung) / Umsetzungs- u. Alarmverfolgungsnachweise, Besucherprotokolle | Sicherheitskonzept validiert + Schutzmaßnahmen umgesetzt | Wiederkehrende Überprüfung der phys. Maßnahmen + Alarmverfolgung/Besuchermgmt nachgewiesen | 2–3² |
| **8.2 Organisation/Vertrag/Personal** | 8.2.1–8.2.7 | NDAs (Firma + Personen), Zutrittsvergabeprozess, Schulungskonzept Prototypen, Bild-/Geräteregelungen / unterzeichnete NDAs, Schulungsnachweise, Zutrittslogs | Vereinbarungen/Prozesse validiert u. dokumentiert | Nachweis gelebter Umsetzung (Schulungen, Zutritts-Reviews, Nachweise Unterauftragnehmer) | 2 |
| **8.3 Transport/Lagerung** | 8.3.1–8.3.2 | Prozess Einholung Auftraggebervorgaben, freigegebene Logistiker / Nachweis Einhaltung/Meldeprozess | Prozesse validiert u. implementiert | Nachweis gelebter Transport-/Lagerkontrollen u. Ereignismeldung | 2 |
| **8.4 Tarnung/Erprobung** | 8.4.1–8.4.3 | Tarnungs-/Erprobungsregeln, Meldeprozess Beschädigungen / Nachweis Bekanntgabe u. Einhaltung | Regeln/Prozesse validiert u. bekannt | Nachweis gelebter Einhaltung + Meldungen bei Vorfällen | 2 |
| **8.5 Ausstellungen/Foto-Film** | 8.5.1–8.5.2 | Prozess Auftraggebervorgaben, freigegebene Sicherheitskonzepte / Freigabenachweise, Verhaltensregeln | Prozesse + freigegebene Konzepte validiert | Nachweis gelebter Umsetzung je Veranstaltung/Shooting | 2 |
² 8.1.4/8.1.5/8.1.8 mit HOCH-Anteil (schutzbedürftige Fahrzeuge): Zielreifegrad 3.
### 6.2 Datenschutz (9.x)
| Gruppe | Controls | Erwartete Belege (Richtlinie/Regelung / operativer Nachweis) | Grad-2-Bedingung | Grad-3-Bedingung | Ziel |
|---|---|---|---|---|---|
| **9.1 Richtlinie DS** | 9.1.1 | DS-Richtlinie (freigegeben) / Aktualisierungs-/Freigabenachweis | DS-Richtlinie validiert u. freigegeben | Regelmäßige Aktualisierung/Review nachgewiesen | 2 |
| **9.2 Verantwortlichkeiten DS** | 9.2.1 | DSB-Bestellung/DS-Funktion, Kontaktveröffentlichung, Ressourcen / Kontroll- u. Berichtsdokumentation | Bestellung/Funktion + Eingliederung validiert | Nachweis ausgeübter Kontrollpflichten + Managementbericht | 2 |
| **9.3 Verarbeitungsverzeichnis** | 9.3.1 | VVT (Art. 30), Prozessbeschreibung/Zuständigkeiten / VVT-Pflegenachweis | VVT + Prozess validiert | Regelmäßige VVT-Pflege/Aktualität nachgewiesen | 2 |
| **9.4 DSFA** | 9.4.1 | Kriterien/Liste DSFA-pflichtiger Verarbeitungen, DSFA-Verfahren / durchgeführte DSFA | DSFA-Verfahren + Zuständigkeiten validiert | Durchgeführte DSFA + Maßnahmen nachgewiesen | 2 |
| **9.5 Datenübermittlung/AV** | 9.5.1–9.5.3 | AV-Verträge (Art. 28), Transferinstrumente, Drittland-Garantien / Nachweis Verträge/Prüfungen | Prozesse + Verträge validiert | Nachweis Überprüfung Einhaltung + Drittland-Erfassung (TIA) | 2 |
| **9.6 Betroffenenrechte/Vorfälle** | 9.6.1–9.6.2 | Verfahren Betroffenenanfragen, DS-Vorfall-/Notfallplan, Schulung / Bearbeitungs-/Meldenachweise | Verfahren validiert u. dokumentiert | Nachweis fristgerechter Bearbeitung + Meldeprozesse gelebt | 2 |
| **9.7 Personal DS** | 9.7.1–9.7.2 | Vertraulichkeitsverpflichtungen, DS-Schulungskonzept / Verpflichtungs- u. Schulungsnachweise | Verpflichtung + Schulungskonzept validiert | Regelmäßige DS-Schulungen + Verpflichtungen nachgewiesen | 2 |
| **9.8 Weisungen AV** | 9.8.1 | Verfahren Weisungsumgang, Datentrennung nach Auftrag / Dokumentation Weisungen | Verfahren + Maßnahmen validiert | Nachweis dokumentierter/umgesetzter Weisungen + Trennung | 2 |
---
## 7. Umsetzungshinweis Wizard (Pseudologik)
```
ziel = ableiteZiel(scope) // AL3 -> 3, AL2/MUSS -> 2, HOCH/V-HOCH -> 3
p = statusRichtlinie(control.policy)
v = min(statusAllerVerfahren(control.verfahren)) // leer -> „n/a" (durch N-basisch ersetzt)
n = statusOperativerNachweis(control) // inkl. Aktualitätsprüfung
a = statusAssetVerknuepfung(control) // nur falls einschlägig
r = statusRisikoVerknuepfung(control) // nur falls einschlägig
if p != validiert: vorschlag = (p == verknuepft) ? 1 : 0
elif verfahrenGefordert && v != validiert: vorschlag = 1
elif einschlaegigA && a != verknuepft: vorschlag = 1
else: vorschlag = 2
if vorschlag == 2 && n == validiert_aktuell: vorschlag = 3
if nachweisMitFindings: vorschlag = min(vorschlag, 1)
if vorschlag < ziel || pflichtbelegFehlt || (ziel==3 && n!=validiert_aktuell):
erzeugeOffenenPunkt(control, ist=vorschlag, ziel, fehlendeBelege)
// Pflicht: Bearbeiter bestätigt oder überschreibt vorschlag (mit Begründung)
```
**Kernprinzip:** Der Wizard schlägt vor, der Bearbeiter entscheidet. Jeder Vorschlag ist über die verknüpften, validierten Belege vollständig nachvollziehbar; jede Lücke zwischen Vorschlag und Zielreifegrad wird automatisch in einen offenen Punkt (Aufgabe) überführt.
File diff suppressed because it is too large Load Diff
@@ -1,61 +0,0 @@
# C7 — ISMS-Soll-Rollenmodell + Funktionstrennung (+ Bestellungs-Vorlage)
> **Schaltet frei:** A4 (ISMS-Rollen & Funktionstrennung, Schritt 3). **Grundlage:** `variables.schema.json` (`ROLE_*`), Richtlinie `R01_ISMS-Organisation-und-Rollen`.
## 1. Soll-Rollenmodell
| Rolle | Variable | Kernverantwortung | Besetzung |
|---|---|---|---|
| Oberste Leitung | `ROLE_MANAGEMENT` | Gesamtverantwortung ISMS, Ressourcen, Freigabe von Leitlinie/Richtlinien, Managementbewertung | intern (GF) |
| Informationssicherheitsbeauftragte(r) | `ROLE_ISB` | Aufbau/Pflege/Weiterentwicklung ISMS, Beratung der Leitung, Risikomanagement, Audits, Meldeweg; berichtet direkt an die Leitung | intern **oder** extern |
| IT-Leitung / IT-Verantwortung | `ROLE_IT_LEAD` | Betrieb, technische Umsetzung der Maßnahmen | intern oder extern (Dienstleister) |
| Personalleitung | `ROLE_HR_LEAD` | Personalsicherheit, On-/Offboarding, Awareness | intern |
| Datenschutzbeauftragte(r) | `ROLE_DPO` | Datenschutz-Compliance (bei Verarbeitung personenbezogener Daten) | intern oder extern |
Bezug: Controls 1.2.1/1.2.2 (Organisation der IS), 2.1.x (Personal), 7.1.2 (Datenschutz). Rollen-Variablen befüllen Platzhalter in allen Vorlagen (Verantwortlich/Freigabe).
## 2. Funktionstrennungs-Regeln (Prüfung im Wizard)
| Regel-ID | Bedingung | Ergebnis | Begründung |
|---|---|---|---|
| FT-01 | `ROLE_ISB` = `ROLE_IT_LEAD` (dieselbe Person, beide intern) | **Konflikt** → Hinweis + Aufgabe | ISB muss die IT unabhängig überwachen können; Selbstkontrolle unzulässig |
| FT-02 | `ROLE_ISB` intern **und** direkt der IT-Leitung unterstellt | **Konflikt (schwach)** → Hinweis | Weisungsunabhängigkeit/Berichtsweg an Leitung gefährdet |
| FT-03 | `ROLE_ISB` = `ROLE_MANAGEMENT` | **Konflikt** → Hinweis + Aufgabe | Leitung kann eigene ISMS-Verantwortung nicht selbst überwachen |
| FT-04 | `ROLE_ISB` nicht benannt | **Lücke** → Aufgabe „ISB bestellen" (Control 1.2.2) | ISB ist Muss-Anforderung |
| FT-05 | `ROLE_DPO` fehlt trotz `FLAG_PERSONAL_DATA` | **Lücke** → Aufgabe „DSB benennen/prüfen" | Datenschutz-Organisation erforderlich |
| FT-06 | Antragsteller = Genehmiger von Zugriffsrechten (aus IAM-Daten) | **Konflikt** → Hinweis | Vier-Augen bei Berechtigungsvergabe (Control 4.2.1) |
**Kompensierende Kontrollen** (wenn Trennung z. B. bei Kleinorganisation nicht möglich): externer ISB (GEFIM-Modell), Vier-Augen mit der Leitung, dokumentierte Ausnahmegenehmigung + verstärkte Protokollierung. Der Wizard bietet bei Konflikt „Trennung herstellen" **oder** „kompensierende Kontrolle dokumentieren" (→ Aufgabe bzw. Nachweis).
## 3. Bestellungs-/Ernennungs-Vorlage (Paket-Stil)
Neue Vorlage `VA-00_ISB-Bestellung.md` bzw. Textbaustein in `R01`, mit Platzhaltern und Ankern:
```markdown
# Bestellung Informationssicherheitsbeauftragte(r)
| Feld | Wert |
|------|------|
| Organisation | {{ORG_NAME}} |
| Bestellte Person / Funktion | {{ROLE_ISB}} |
| Bestellt durch | {{ROLE_MANAGEMENT}} |
| Besetzung | {{#if FLAG_ISB_EXTERNAL}}extern (Dienstleistervertrag){{/if}}{{#if FLAG_ISB_INTERNAL}}intern{{/if}} |
| Version / Datum / Status | {{DOC_VERSION}} / {{DOC_DATE}} / {{DOC_STATUS}} |
<!-- REQ 1.2.2-M1 -->
Die Organisation {{ORG_NAME}} bestellt {{ROLE_ISB}} mit Wirkung zum {{DOC_DATE}} zum/zur Informationssicherheitsbeauftragten.
## Aufgaben und Befugnisse
- Aufbau, Pflege und Weiterentwicklung des ISMS gemäß VDA ISA.
- Beratung der Leitung; jährliche Überprüfung von Richtlinien und Wirksamkeit.
- Koordination von Risikomanagement, Schulungen, Audits, Maßnahmen.
- Melde-/Eskalationsrecht direkt an {{ROLE_MANAGEMENT}}; Zugang zu relevanten Informationen/Systemen.
## Freigabe
| Rolle | Name | Datum, Unterschrift |
|-------|------|---------------------|
| Leitung | {{ROLE_MANAGEMENT}} | |
| ISB | {{ROLE_ISB}} | |
```
Neue Flags bei Bedarf in `variables.schema.json`: `FLAG_ISB_EXTERNAL` / `FLAG_ISB_INTERNAL` (aus Q-ROLE-02). Nach Anlage `_verify.py` → **OK**.
@@ -1,37 +0,0 @@
# C8 — Priorisierungslogik Gap + Quick-Wins
> **Schaltet frei:** A8 (Gap-Konsolidierung, Schritt 8). **Grundlage:** Ergebnisse aus Schritt 6 (Risiko) und 7 (Control-Assessment), Aufgaben-Modul.
## 1. Prioritätsregeln (deterministisch)
Priorität eines offenen Punkts = höchste zutreffende Regel:
| Priorität | Bedingung |
|---|---|
| **Hoch** | Offene **MUSS**-Anforderung (Reifegrad < 2) · **oder** offene AL3-Zusatzanforderung (`HOCH`/`SEHR HOCH`) bei entsprechendem Schutzbedarf · **oder** Behandlungsmaßnahme zu einem Risiko oberhalb der Akzeptanzlinie · **oder** fehlende Muss-Rolle/Funktionstrennungs-Konflikt (C7 FT-01/03/04) |
| **Mittel** | Offene **SOLL**-Anforderung · Reifegrad = 2 aber unter Zielreifegrad 3 · Nachweis vorhanden aber nicht validiert · Dokument im Entwurf/nur als Vorlage |
| **Niedrig** | Redaktioneller/formaler Punkt · Optimierung ohne Reifegrad-Wirkung · Nachweis nur zu aktualisieren |
Zusatzgewichtung (Sortierung innerhalb gleicher Priorität): (1) Anzahl betroffener Controls, (2) Risikohöhe des verknüpften Risikos, (3) Aufwand aufsteigend (Quick-Wins zuerst).
## 2. Deduplizierung
Offene Punkte aus Schritt 6 und 7 werden zusammengeführt. Dedup-Schlüssel = (Control + Teilanforderungs-ID) bzw. (verknüpfte Maßnahme/Aufgabe). Regeln:
- Gleiche Teilanforderung aus mehreren Quellen → **ein** Punkt, höchste Priorität gewinnt, Quellen werden verknüpft.
- Ein offener Punkt, für den bereits eine Aufgabe existiert → keine neue Aufgabe, bestehende verknüpfen.
- Risiko-Maßnahme und Control-Gap, die dieselbe Maßnahme adressieren (z. B. „Patchmanagement einführen" für Risiko R-OPS-03 und Controls 5.2.3/5.2.5) → zusammenführen, alle Bezüge an die eine Aufgabe hängen.
## 3. Quick-Wins
Ein offener Punkt ist **Quick-Win**, wenn: geringer Aufwand (organisatorisch/redaktionell, kein Tool-/Budgetbedarf) **und** Reifegrad-Wirkung ≥ +1 auf mindestens ein Control **und** keine Abhängigkeit von anderer offener Maßnahme. Typische Quick-Wins: Freigabe/Validierung eines bereits erstellten Dokuments, Befüllen einer vorhandenen Vorlage (Konten-Review, Rezertifizierung), Rollen-/Funktionstrennungs-Dokumentation, Redaktionslücken schließen. Der Wizard hebt Quick-Wins im Maßnahmenplan gesondert hervor (Reihenfolge-Empfehlung „erst Quick-Wins, dann Hoch-Aufwand").
## 4. Beispiel-Priorisierung
| Offener Punkt | Regel | Priorität | Quick-Win? |
|---|---|---|---|
| Kein Patchmanagement (Controls 5.2.3/5.2.5, Risiko R-OPS-03) | MUSS < 2 + Risiko | Hoch | nein (Tool) |
| Restore-Test nicht durchgeführt (5.2.9, BL-OPS-06) | MUSS-Nachweis fehlt | Hoch | ja (organisatorisch) |
| ISB = IT-Verantwortung (FT-01) | Funktionstrennungs-Konflikt | Hoch | teils (extern/kompensieren) |
| SOLL Berechtigungs-Review nur als Vorlage (4.2.1) | SOLL, nicht validiert | Mittel | ja |
| Zonenbezeichnung inkonsistent (3.1.1) | redaktionell | Niedrig | ja |
@@ -1,49 +0,0 @@
# C9 — Auswertungs-/Interpretationstexte + Export-Layout
> **Schaltet frei:** B7 (Assessment-Readiness & Export, Schritt 9). **Grundlage:** validierte Control-Bewertungen (Schritt 7), Gap-/Maßnahmenliste (Schritt 8), Validierungsstatus (A3).
## 1. Reifegrad-Dashboard — Interpretationstexte
Aggregation je Kapitel und gesamt (Durchschnitt der Control-Reifegrade, „unbestätigt" zählt nicht als erfüllt). Textbänder nach Gesamtreifegrad:
| Reifegrad (Ø) | Band | Interpretationstext |
|---|---|---|
| < 1,5 | Aufbau | „Das ISMS befindet sich im Aufbau. Wesentliche Richtlinien/Verfahren sind noch zu erstellen oder freizugeben. Fokus: Muss-Anforderungen und Grundstruktur." |
| 1,5 – < 2,5 | Etabliert im Aufbau | „Grundlegende Prozesse sind vorhanden und teils dokumentiert. Für Assessment-Reife fehlen v. a. Nachweise der gelebten Anwendung und Validierungen." |
| 2,5 – < 3,0 | Assessment-nah | „Das ISMS ist überwiegend etabliert. Wenige offene Punkte und Nachweise trennen von der Assessment-Reife (AL-Ziel). Fokus: Hoch-Punkte schließen, Wirksamkeitsnachweise ergänzen." |
| = 3,0 | Assessment-reif | „Die bewerteten Controls erfüllen den Zielreifegrad. Empfehlung: Stichprobenvalidierung und Aktualität der Nachweise vor dem Assessment sicherstellen." |
Zusätzliche Kennzahlen: Anteil bestätigter (validierter) Controls, Anzahl offener Punkte je Priorität, Abdeckung je Prüfziel, „höchstes erreichbares Ergebnis" = Zielreifegrad.
## 2. Empfohlene nächste Schritte (dynamisch)
Regelbasiert eingeblendet:
- Offene **Hoch**-Punkte vorhanden → „Vor dem Assessment zwingend: die N Hoch-Punkte im Maßnahmenplan abarbeiten (siehe Aufgaben)."
- Unvalidierte Objekte vorhanden → „X Objekte warten auf Validierung durch ISB/Berater — bis dahin als ‚unbestätigt' gewertet."
- Prüfziel Prototypenschutz aktiv & 8.x < Ziel → „Prototypenspezifische Nachweise ergänzen (physische Schutzmaßnahmen, Zutritt, Transport)."
- Nachweis-Upload-Iteration noch offen → „Operative Nachweise (Screenshots/Protokolle) in der Folgeiteration hochladen."
## 3. VDA-ISA-Katalog-Export — Layout
**Struktur (je Prüfziel ein Tabellenblatt/Abschnitt, Reihenfolge Informationssicherheit → Prototypenschutz → Datenschutz):**
| Feld | Inhalt | Quelle |
|---|---|---|
| Control-ID | z. B. 4.1.2 | Katalog |
| Kontrollfrage / Ziel | aus Katalog | Katalog |
| Reifegrad | 0–3 (bestätigt) bzw. „na" | Schritt 7 |
| Status | bestätigt / unbestätigt | A3-Validierung |
| Umsetzungsbeschreibung | je Teilanforderung: **Anforderung (fett)** + Umsetzung (normal), Reihenfolge MUSS→SOLL→HOCH→SEHR HOCH | Schritt 7 + Vorlagen-IMPL |
| Belege | verknüpfte Dokumente/Verfahren/Risiken/Assets | Schritt 4–7 |
| Offene Punkte | je Control aus Gap-Liste | Schritt 8 |
**Darstellungsregeln:** Anforderungstext im Originalwortlaut, **fett**; Umsetzung normal darunter mit Quellenangabe (Dokument, Version/Stand, Kapitel) — konsistent zur bisherigen Ausarbeitung. „Unbestätigt" sichtbar markieren (z. B. Kennzeichnung/Farbe). Ergänzungshinweise/offene Punkte **nicht** im Katalog, sondern in der separaten Maßnahmen-/Schwachstellenliste.
**Exportformate:** XLSX (VDA-ISA-Katalogsicht + Ergebnisblatt mit Reifegrad-Kennzahlen), zusätzlich DOCX/PDF für Management-Zusammenfassung. Der Export koppelt an den bestehenden DOCX/PDF-Export der App.
## 4. Zusatzartefakte im Export
- **Maßnahmenplan** (aus C8): priorisierte offene Punkte + verknüpfte Aufgaben, Quick-Wins hervorgehoben.
- **Nachweisregister** (aus `Nachweisregister_zentral.md`): je Anforderung der zugeordnete Nachweis/Status.
- **Management-Zusammenfassung**: Stärken, Reifegrad-Überblick, Hoch-Punkte, empfohlene Schritte vor dem Assessment.
@@ -1,233 +0,0 @@
# Onboarding-Wizard ISMS – Detaillierter Fahrplan & Entwickler-Handlungsanweisung
**Produkt:** ISMS-Applikation – Onboarding-Wizard
**Fokus der ersten Ausbaustufe:** TISAX / VDA ISA
**Ziel (North Star):** Assessment-Readiness – am Ende des Wizards liegt ein vorausgefüllter VDA-ISA-Katalog inkl. Reifegrad-Ersteinschätzung und Maßnahmenplan vor.
**Modus:** Kunde bearbeitet eigenständig; ISB oder externer Berater (falls gebucht) validiert an definierten Gates.
**Tailoring:** Template- und regelbasiert; eigene Richtlinien optional hochladbar; die mitgelieferten Vorlagen sind optional.
**Status:** Detailausarbeitung zur Verifizierung vor der technischen Umsetzung.
---
## 1. Lesehinweis & Konventionen
Dieses Dokument beschreibt je Wizard-Schritt: Zweck, Ein-/Ausgaben, Verarbeitungslogik, erzeugte Objekte, kontextsensitive Umsetzungshinweise, Aufgaben-Trigger, Validierung, benötigte Entwicklungs-Ressourcen und Akzeptanzkriterien. Es ist eine fachliche Handlungsanweisung, keine technische Spezifikation – Technologiewahl, konkrete Schemata und UI-Design folgen in der Umsetzung.
**Aufwands-/Ressourcengröße (T-Shirt):** S = klein, M = mittel, L = groß. Bezieht sich auf den Entwicklungsaufwand des jeweiligen Bausteins, nicht auf die Kundenlaufzeit.
**Rollen im Wizard**
| Rolle | Aufgabe |
|---|---|
| Bearbeiter (Kunde) | Durchläuft die Schritte, gibt Fakten ein, wählt/erstellt Dokumente, bewertet Controls und Risiken |
| Validierer (ISB intern oder externer Berater) | Prüft und gibt Module/Objekte frei; ohne Freigabe fließt Inhalt als „unbestätigt" in die Auswertung |
| Systemadministrator (Vorbedingung) | Richtet Mandant, Nutzer und Rollen ein – **außerhalb** des Wizards |
---
## 2. Querschnittsmechaniken (gelten in allen Schritten)
Diese vier Mechaniken sind kein eigener Schritt, sondern durchziehen den gesamten Wizard und sollten früh als wiederverwendbare Bausteine gebaut werden.
### 2.1 Validierungs-Workflow
Jedes bearbeitbare Objekt (Modul, Richtlinie, Risiko, Control-Bewertung) trägt einen Status: `offen → in Bearbeitung → zur Validierung → validiert` (bzw. `zurückgewiesen` mit Kommentar). Nur validierte Inhalte zählen in der Assessment-Readiness-Auswertung als „bestätigt". Der Validierer erhält je Objekt Kontext, Kommentar- und Freigabefunktion. Ist kein externer Berater gebucht, übernimmt der interne ISB die Validierung.
**Ressourcen:** Backend (Statusmodell, Rechte) M · Frontend (Review-Ansicht, Kommentare) M.
### 2.2 Umsetzungshinweise („So setzen Sie diese Anforderung um")
An jeder VDA-ISA-Teilanforderung (und optional an Risiken/Templates) hängt redaktioneller Hinweis-Content, der bei der Bearbeitung kontextsensitiv eingeblendet wird. Ein Hinweis umfasst typischerweise: organisatorische Umsetzungsoption, technische Umsetzungsoption, typische Nachweise, geeignete Vorlage (Verweis) und eine **Ressourcenindikation** (Tool/Personal/Budget/Zeit). Die Einblendung wird über Scope und Antworten gefiltert (z. B. nur AL3-relevante Hinweise). Hinweise sind Wissensinhalt, keine trackbare Aufgabe – führt ein Hinweis zu einer umzusetzenden Maßnahme, entsteht daraus eine Aufgabe (siehe 2.3).
**Ressourcen:** Fachredaktion/ISMS-SME (Content-Pflege) L · Backend (Hinweis-Datenmodell, Filter) S · Frontend (Inline-Panel) S.
### 2.3 Maßnahmen → Aufgaben-Modul
Ergibt sich bei der Bearbeitung eine umzusetzende Maßnahme (z. B. „Patchmanagement-Tool einführen", „Berechtigungs-Review etablieren", „Notfallübung durchführen"), wird daraus eine **Aufgabe im Aufgaben-Modul** erzeugt – automatisch vorgeschlagen und vom Bearbeiter bestätigt. Trigger-Punkte: Richtlinie „später erstellen" (Schritt 4), Risikobehandlung (Schritt 6), Control-Lücke/Reifegrad < Ziel (Schritt 7), Gap-Liste (Schritt 8), Zurückweisung durch Validierer.
**Aufgaben-Objekt (Felder):** Titel, Beschreibung, Typ (`Dokument erstellen` / `Nachweis liefern` / `technische Maßnahme` / `organisatorische Maßnahme`), Verantwortlicher, Fälligkeit, Priorität, Status, Verknüpfung (Control / Risiko / Dokument / Asset), **benötigte Ressourcen** (Tool, Budget, Personal, Zeit), Herkunft (auslösender Schritt). Die Ressourcenindikation stammt aus dem Umsetzungshinweis und ist editierbar.
*Beispiel:* Control 5.2.3/5.2.5 unzureichend → Hinweis „technische Umsetzung: zentrales Patchmanagement" → Aufgabe „Patchmanagement-Tool auswählen und einführen", Typ `technische Maßnahme`, verknüpft mit 5.2.3 und 5.2.5, Ressourcen: Tool-Lizenz + IT-Personal ca. X PT, Priorität Hoch.
**Ressourcen:** Backend (Task-Generierung, Verknüpfungen, API zum Aufgaben-Modul) M · Frontend (Vorschlag/Bestätigung, Ressourcenfelder) M. Voraussetzung: Aufgaben-Modul der App existiert bzw. bietet eine Schnittstelle.
### 2.4 Audit-Trail & Versionierung
Jede Eingabe, Statusänderung und Freigabe wird protokolliert (wer/wann/was). Templates, Katalog und Risikokatalog sind versioniert; Kundendokumente referenzieren die genutzte Template-Version.
**Ressourcen:** Backend (Event-Log, Versionierung) M.
---
## 3. Fachliche Bausteine / Datenobjekte (Fundament)
| Baustein | Inhalt | Ressourcen |
|---|---|---|
| Control-Katalog VDA ISA | Prüfziele, Assessment-Level, je Control die Muss-/Soll-/Zusatzanforderungen als einzeln adressierbare Teilanforderungen | SME (Datenpflege) M · Backend (Import/Modell) S |
| Template-Bibliothek (optional) | Richtlinien-/VA-Vorlagen mit Platzhaltern, optionalen Bausteinen und Control-Verknüpfung | SME/Content L · Backend S |
| Upload-Pfad eigene Dokumente | Upload + Zuordnung „welche Controls belegt dieses Dokument" | Backend S · Frontend S |
| Fakten-/Fragemodell | Antwortobjekte zu Organisation, IT, Dienstleistern, Prozessen; wiederverwendbar | SME (Fragebogen) M · Backend M |
| Risiko-Katalog | Standard- und VDA-geforderte Risiken, vordefiniert und erweiterbar; Verknüpfung zu Controls/Assets | SME L · Backend M |
| Regel-/Mapping-Layer | Antwort → Platzhalter, Klausel-Ein/Ausblendung, betroffene Controls/Assets/Risiken | Backend (Regel-Engine) L |
| Reifegrad-/Gap-Engine | Reifegrad-Ersteinschätzung + offene Punkte je Control | Backend M · SME (Logik) M |
---
## 4. Die Schritte im Detail
> Vorbedingung (außerhalb des Wizards): technisches Onboarding – Mandant, Nutzer, Rollen eingerichtet.
### Schritt 1 – Scoping
- **Zweck:** Prüfziele, Assessment-Level (AL2/AL3), Standorte und Geltungsbereich festlegen. Dies steuert, welche Controls, Templates und Risiken im weiteren Verlauf überhaupt relevant sind.
- **Eingaben:** Prüfziele (Info-Sicherheit, Prototypenschutz, Datenschutz), Assessment-Level, Standort(e), organisatorischer/technischer Geltungsbereich, Ausschlüsse.
- **Verarbeitung & Regeln:** Aktiviert/deaktiviert Control-Set und Template-Set; z. B. Prototypenschutz nur bei entsprechendem Prüfziel, AL3 schaltet Zusatzanforderungen „sehr hoher Schutzbedarf" frei.
- **Erzeugte Objekte:** Scope-Objekt; gefiltertes Control-Set als Arbeitsgrundlage.
- **Umsetzungshinweise:** Erläuterung der Prüfziele und Level (Entscheidungshilfe AL2 vs. AL3), typische Scope-Formulierungen.
- **Aufgaben-Trigger:** i. d. R. keine.
- **Validierung:** Scope wird durch ISB/Berater bestätigt (Fundament für alles Weitere).
- **Ressourcen (Entwicklung):** Frontend (Scoping-UI) S · Backend (Scope-Filterlogik) M · SME (Scope-Regeln) S.
- **Akzeptanzkriterien:** Geänderter Scope filtert nachgelagerte Schritte korrekt; nicht relevante Controls/Templates sind ausgeblendet.
### Schritt 2 – Kontextaufnahme (Gegebenheiten)
- **Zweck:** Die tatsächlichen Gegebenheiten der Organisation als Faktenbasis erheben, die Platzhalter befüllt und Regeln aktiviert.
- **Eingaben:** Geführter Fragebogen – Unternehmensdaten, Organisationsstruktur, IT-Landschaft (Systeme, Cloud, Netzwerke), eingesetzte Dienstleister, relevante Prozesse, grundlegender Schutzbedarf.
- **Verarbeitung & Regeln:** Antworten werden als wiederverwendbare Faktenobjekte gespeichert und mit Platzhaltern, Klauselregeln, Controls, Assets und Risiken verknüpft. Bedingte Fragen (nur anzeigen, wenn relevant).
- **Erzeugte Objekte:** Faktenprofil der Organisation.
- **Umsetzungshinweise:** Erläuterung, warum eine Angabe gebraucht wird und wie sie sich auf Dokumente/Bewertung auswirkt.
- **Aufgaben-Trigger:** Fehlende Grundvoraussetzung erkennbar (z. B. „kein Inventar vorhanden") → optionale Aufgabe.
- **Validierung:** Plausibilitätsprüfung durch ISB/Berater.
- **Ressourcen (Entwicklung):** Frontend (dynamischer Fragebogen) M · Backend (Faktenmodell, bedingte Logik) M · SME (Fragenkatalog) M.
- **Akzeptanzkriterien:** Antworten sind wiederverwendbar; eine Änderung propagiert sichtbar in abhängige Objekte.
### Schritt 3 – Rollen & Verantwortlichkeiten
- **Zweck:** ISMS-Rollen festlegen (Geschäftsführung, ISB/DSB, IT-Verantwortung), inkl. Funktionstrennung.
- **Eingaben:** Personen/Funktionen je Rolle; intern/extern; Weisungs-/Berichtswege.
- **Verarbeitung & Regeln:** Prüft auf Funktionstrennung (z. B. ISB ≠ IT-Verantwortung); befüllt Rollen-Platzhalter in Dokumenten (Bestellungen/Ernennungen).
- **Erzeugte Objekte:** Rollenmodell; Dokumente wie ISB-Bestellung (aus Vorlage, optional).
- **Umsetzungshinweise:** Anforderungen an ISB-Qualifikation, externe vs. interne Besetzung, typische Nachweise.
- **Aufgaben-Trigger:** Fehlende ISB-Bestellung / fehlende Funktionstrennung → Aufgabe (organisatorische Maßnahme).
- **Validierung:** Freigabe des Rollenmodells.
- **Ressourcen (Entwicklung):** Frontend S · Backend (Rollenmodell, Regelprüfung) M · SME S.
- **Akzeptanzkriterien:** Funktionstrennungs-Konflikt wird erkannt und als Hinweis/Aufgabe ausgewiesen.
### Schritt 4 – Richtlinien & Verfahrensanweisungen
- **Zweck:** Für jeden relevanten Themenbereich die belegenden Dokumente bereitstellen – flexibel in der Quelle.
- **Eingaben:** Je Themenbereich Auswahl einer von drei Optionen:
(a) mitgelieferte Vorlage tailoren (Platzhalter aus Fakten befüllt, nicht zutreffende Klauseln ausgeblendet),
(b) eigene Richtlinie hochladen und den abgedeckten Controls zuordnen,
(c) als offen markieren / später erstellen.
- **Verarbeitung & Regeln:** (a) Template-Engine erzeugt kundenspezifisches Dokument aus Faktenprofil + Regeln; (b) Upload wird gespeichert und über Control-Mapping in die Nachweislage aufgenommen; (c) erzeugt Aufgabe.
- **Erzeugte Objekte:** Dokumenten-Set (generiert oder hochgeladen) mit Control-Verknüpfung und Versionsbezug.
- **Umsetzungshinweise:** Was die jeweilige Richtlinie mindestens enthalten muss (VDA-ISA-Bezug); Formulierungsbausteine.
- **Aufgaben-Trigger:** Option (c) „später erstellen" → Aufgabe `Dokument erstellen`; hochgeladenes Dokument ohne vollständige Control-Abdeckung → Hinweis/Aufgabe.
- **Validierung:** Freigabe je Dokument (Objekt-Ebene).
- **Ressourcen (Entwicklung):** Backend (Template-Engine, Platzhalter/Regeln, Rendering) L · Frontend (Auswahl, Editor/Vorschau, Upload) L · SME/Content (Vorlagen mit Regeln auszeichnen) L.
- **Akzeptanzkriterien:** Generiertes Dokument enthält keine unaufgelösten Platzhalter und keine nicht zutreffenden Klauseln; hochgeladenes Dokument ist Controls zugeordnet.
### Schritt 5 – Werte-/Assetinventar (Grundstock)
- **Zweck:** Die für Scope und Schutzbedarf nötigen Informationswerte/Assets erfassen.
- **Eingaben:** Assets (Kategorie, Eigentümer, Schutzziele C/I/A, Schutzbedarf, Standort/Verknüpfung).
- **Verarbeitung & Regeln:** Assets werden Risiken (Schritt 6) und Controls zugeordnet; Schutzbedarf steuert die AL3-Relevanz von Zusatzanforderungen.
- **Erzeugte Objekte:** Assetinventar (Grundstock).
- **Umsetzungshinweise:** Wie man Werte identifiziert und Schutzbedarf herleitet; Beispielkategorien.
- **Aufgaben-Trigger:** Unvollständiges Inventar / fehlender Eigentümer → Aufgabe.
- **Validierung:** Freigabe des Inventars.
- **Ressourcen (Entwicklung):** Frontend (Inventar-CRUD, Import) M · Backend (Assetmodell, Verknüpfungen) M · SME S.
- **Akzeptanzkriterien:** Jedes Asset hat Eigentümer und Schutzbedarf; Verknüpfung zu Risiken/Controls möglich.
### Schritt 6 – Risikomanagement
- **Zweck:** Risiken systematisch erfassen, bewerten und behandeln – auf Basis eines Katalogs mit Standard- und VDA-geforderten Risiken.
- **Eingaben:** Auswahl aus Risiko-Katalog (Standard/VDA) + eigene Ergänzungen; Bewertung (Eintrittswahrscheinlichkeit/Schadensausmaß); Behandlungsentscheidung.
- **Verarbeitung & Regeln:** Risiken werden mit Assets und Controls verknüpft; Bewertungsmethodik (Skalen, Akzeptanzlinie) wird angewendet; Behandlungsoptionen (reduzieren/übertragen/vermeiden/akzeptieren) mit Maßnahmenableitung.
- **Erzeugte Objekte:** Risikoregister; Behandlungsmaßnahmen.
- **Umsetzungshinweise:** Vorschlag typischer Maßnahmen je Risiko; Erläuterung der Bewertungsmethodik.
- **Aufgaben-Trigger:** Jede Behandlungsmaßnahme, die Umsetzung erfordert → Aufgabe (technisch/organisatorisch), verknüpft mit Risiko und Control (z. B. Risiko „Schadsoftware" → Aufgabe „Endpoint-/Patchmanagement einführen").
- **Validierung:** Freigabe des Risikoregisters bzw. je Risiko.
- **Ressourcen (Entwicklung):** Backend (Risikomodell, Bewertungslogik, Katalog) L · Frontend (Register, Matrix, Bewertung) L · SME (Risiko-Katalog + Standardmaßnahmen) L.
- **Akzeptanzkriterien:** VDA-geforderte Risiken sind auswählbar; jedes inakzeptable Risiko hat eine Behandlung; Maßnahmen erzeugen Aufgaben.
### Schritt 7 – Control-Zuordnung & Reifegrad
- **Zweck:** Je relevantem VDA-ISA-Control die Umsetzung bewerten und Reifegrad einschätzen.
- **Eingaben:** Je Control: Verknüpfung der belegenden Dokumente/Risiken/Assets; geführte Reifegrad-Selbsteinschätzung; ggf. Kurzbeschreibung.
- **Verarbeitung & Regeln:** Reifegrad-/Gap-Engine schlägt auf Basis vorhandener Belege einen Reifegrad vor; offene Teilanforderungen werden markiert.
- **Erzeugte Objekte:** Control-Bewertung (Reifegrad, Belege, offene Punkte) – die Rohdaten des späteren VDA-ISA-Katalogs.
- **Umsetzungshinweise:** Pro Teilanforderung „So setzen Sie das um" (organisatorisch/technisch, Nachweise, passende Vorlage, Ressourcenbedarf).
- **Aufgaben-Trigger:** Reifegrad unter Ziel / offene Teilanforderung → Aufgabe (Dokument, Nachweis, technische oder organisatorische Maßnahme).
- **Validierung:** Freigabe der Control-Bewertung durch ISB/Berater.
- **Ressourcen (Entwicklung):** Backend (Scoring/Gap-Logik, Verknüpfungen) L · Frontend (Control-Ansicht, Belege, Reifegrad) L · SME (Reifegrad-Logik, Hinweis-Content) L.
- **Akzeptanzkriterien:** Jede relevante Teilanforderung ist adressiert; Reifegradvorschlag ist nachvollziehbar an Belege gekoppelt.
### Schritt 8 – Gap- & Maßnahmenableitung
- **Zweck:** Offene Punkte aus Controls und Risikobehandlung zu einer priorisierten Maßnahmenliste zusammenführen.
- **Eingaben:** Ergebnisse aus Schritt 6 und 7; bestehende Aufgaben.
- **Verarbeitung & Regeln:** Deduplizierung, Priorisierung (Muss-/AL3-kritisch = hoch), Konsolidierung zu einer Gesamtsicht; Abgleich mit dem Aufgaben-Modul.
- **Erzeugte Objekte:** Konsolidierte Maßnahmen-/Gap-Liste (verknüpft mit Aufgaben).
- **Umsetzungshinweise:** Priorisierungslogik erläutert; Quick-Wins hervorgehoben.
- **Aufgaben-Trigger:** Jeder offene Punkt ohne bestehende Aufgabe → Aufgabe.
- **Validierung:** Freigabe der Maßnahmenliste.
- **Ressourcen (Entwicklung):** Backend (Aggregation/Dedup/Priorisierung) M · Frontend (Listenansicht, Filter) M.
- **Akzeptanzkriterien:** Keine Dopplungen; jeder offene Punkt hat Priorität und – wo Umsetzung nötig – eine Aufgabe.
### Schritt 9 – Assessment-Readiness-Auswertung
- **Zweck:** Das Gesamtergebnis darstellen: vorausgefüllter VDA-ISA-Katalog, Reifegrad-Dashboard, Maßnahmenplan.
- **Eingaben:** Alle validierten Objekte.
- **Verarbeitung & Regeln:** Aggregation der Reifegrade je Kapitel/gesamt; „bestätigt vs. unbestätigt" nach Validierungsstatus; Export.
- **Erzeugte Objekte:** VDA-ISA-Katalog-Sicht/Export; Reifegrad-Dashboard; Maßnahmenplan.
- **Umsetzungshinweise:** Interpretation der Auswertung; empfohlene nächste Schritte vor dem Assessment.
- **Aufgaben-Trigger:** Aus offenen Hoch-Punkten ableitbar (bereits in Schritt 8 erzeugt).
- **Validierung:** Gesamtfreigabe/Abschluss durch ISB/Berater.
- **Ressourcen (Entwicklung):** Backend (Aggregation, Export) M · Frontend (Dashboard, Katalogansicht) L · SME (Auswertungslogik) M.
- **Akzeptanzkriterien:** Kennzahlen stimmen mit den Einzelbewertungen überein; Export ist vollständig und nachvollziehbar.
---
## 5. Entwicklungs-Fahrplan (Meilensteine mit Ressourcen)
| Meilenstein | Inhalt | Schritte | Kern-Ressourcen | Größe |
|---|---|---|---|---|
| M1 Fundament | Datenmodell + Import Control-Katalog VDA ISA | – | Backend, SME | M |
| M2 Frage-Engine | Scoping + Kontext + Rollen | 1–3 | Backend, Frontend, SME | L |
| M3 Richtlinien-Modul | Template-Engine + Upload/Control-Mapping | 4 | Backend, Frontend, Content-SME | L |
| M4 Asset- & Risiko-Modul | Assetinventar + Risiko-Katalog + Bewertung | 5–6 | Backend, Frontend, SME | L |
| M5 Bewertungs-Engine | Control-Mapping, Reifegrad/Gap, Maßnahmen-Konsolidierung | 7–8 | Backend, SME, Frontend | L |
| M6 Ergebnis/Output | Assessment-Readiness-Auswertung + Export | 9 | Backend, Frontend | M |
| M7 Governance (querlaufend) | Validierungs-Workflow, Aufgaben-Integration, Audit-Trail, Versionierung, Umsetzungshinweise | alle | Backend, Frontend, SME | L |
**Kürzester Pfad zur Assessment-Readiness:** M1 → M2 → M3 → M4 → M5 → M6. M7 wird parallel eingezogen (Validierung, Aufgaben und Hinweise sollten nicht nachträglich „angeflanscht" werden). Nachweis-Upload ist bewusst spätere Iteration.
---
## 6. Ressourcen-Gesamtübersicht (Rollen im Entwicklungsprojekt)
| Rolle | Beitrag | Auslastung (grob) |
|---|---|---|
| ISMS/TISAX-SME (Fachredaktion) | Control-Katalog, Templates mit Regeln, Risiko-Katalog, Umsetzungshinweise, Reifegrad-/Bewertungslogik | hoch, durchgehend – der inhaltliche Flaschenhals |
| Backend-Entwicklung | Datenmodell, Regel-/Mapping-Engine, Scoring, Task-Integration, API, Audit-Trail | hoch |
| Frontend-Entwicklung | Wizard-Flow, dynamische Formulare, Editor/Vorschau, Dashboards | hoch |
| Content-/Template-Engineering | Vorlagen mit Platzhaltern und Regel-Tags auszeichnen | mittel, in M3 konzentriert |
| UX/Design | geführter Flow, Statusführung, Hinweis-Panels | mittel |
| QA/Test | Regel-/Scoring-Korrektheit, Dokumentgenerierung, Freigabelogik | mittel–hoch |
**Wichtig:** Der größte, oft unterschätzte Aufwand liegt nicht in der Software, sondern im **Fachcontent** (Control-Daten, regelfähige Vorlagen, Risiko-Katalog, Umsetzungshinweise). Dieser sollte parallel und früh durch die Beratung erstellt werden, sonst wird er zum kritischen Pfad.
---
## 7. Offene Entscheidungspunkte (vor der technischen Spezifikation zu klären)
1. **Upload eigener Richtlinien:** nur Control-Zuordnung, oder zusätzlich ein automatischer Abgleich Dokumentinhalt ↔ Anforderungen (Gap-Check)?
2. **Risiko-Bewertungsmethodik:** fest vorgegeben oder mandantenspezifisch konfigurierbar (Skalen, Akzeptanzlinie)?
3. **Versionierung von Katalog/Templates/Risiken:** wie werden laufende Kundeninstanzen bei Updates nachgezogen (automatisch, mit Diff, manuell)?
4. **Validierungsgranularität:** pro Modul, zusätzlich pro Einzelobjekt (einzelne Richtlinie/Risiko/Control)?
5. **Aufgaben-Modul:** existiert bereits mit passender Schnittstelle, oder muss die Task-Erzeugung mitgeplant werden?
6. **Reifegrad-Vorschlag:** rein regelbasiert aus Belegen, oder als Empfehlung mit Pflicht zur Bestätigung durch den Bearbeiter?
---
## 8. Nächster Schritt
Nach Verifizierung dieses Fahrplans: technische Feinspezifikation je Modul (Datenschemata, Regel-Syntax, API-Verträge, UI-Wireframes, Akzeptanztests) – vorgeschlagene Reihenfolge entlang der Meilensteine M1–M7.
@@ -1,9 +0,0 @@
# Hinweis: STAND-dev-branch.md liegt zentral
Die im Original-Übergabepaket enthaltene Kopie von `STAND-dev-branch.md` (Stand 2026-07-24)
wurde **bewusst nicht** hier abgelegt, um keinen zweiten, veralteten Stand zu führen.
**Maßgeblich ist die zentrale Datei im Repo:** [`docs/STAND-dev-branch.md`](../../STAND-dev-branch.md)
(wird nach jeder abgeschlossenen Arbeit gepflegt — Single Source of Truth).
Weitere Basis-Doku: `docs/HANDOVER-DEV.md`, `docs/HANDOVER-PM.md`, `docs/SPEC.md`.
-20
View File
@@ -1,20 +0,0 @@
# Onboarding-Wizard — Übergabepaket (Entwicklung)
Alles, was die beiden Entwickler und der Berater für die parallele Umsetzung brauchen.
## Reihenfolge / Einstieg
1. **00_Entwicklerpakete/** — je ein fertiger Umsetzungs-Prompt:
- `Paket-DevA-Wizard-Scoping.md` — Wizard-Shell, Scoping, **AL2/AL3 zentral im Adminportal**.
- `Paket-DevB-Engines-Richtlinien.md` — Aufgaben/Regel-Engine, Fragebogen, **Richtlinien-Import bei Modul-Aktivierung + manueller Upload**.
- Beide enthalten einen **identischen Contracts-Anhang** (Naht) + Datei-Hoheit → konfliktarm parallel. Vorab: 15-Min-Kickoff.
2. **01_Planung/** — Kontext & Gesamtbild:
- `Wizard-Stories.md` (kompletter Story-Backlog F/A/B), `Wizard-Entwickler-Backlog.md` (2-Lane-Plan),
`Onboarding-Wizard-Machbarkeitsanalyse.md` (Ist-Abgleich), `Berater-Anweisung-Fachcontent.md`.
3. **02_Fachcontent_C1-C9/** — Zuarbeit des Fachberaters (ID-konsistent zu `isms-vorlagenpaket-v2`). Start: `C0_README-...`.
4. **03_Referenz/** — Berater-Fahrplan + Dev-Stand `dev`.
## Wichtig
- Basis-Branch **`dev`**; Feature-Branches `dev/a*-…` bzw. `dev/b*-…`; PR-Ziel `dev`.
- **Definition of Ready** je Story: zugehöriges C-Paket eingespielt, bei Seed-Änderung `python3 seed/isms-vorlagenpaket-v2/_verify.py` → **OK**.
- Kritischer Pfad Fachcontent: C2 → C1/C3 → C5/C6.
- Offene Fachpunkte (C0): neue `FLAG_*` in `variables.schema.json`, P01/D01+VA-20 anlegen, Baseline-`BL-*` normalisieren, ISB-Freigabe.
-1136
View File
File diff suppressed because it is too large Load Diff
+58
View File
@@ -0,0 +1,58 @@
{
"backToOverview": "Zurück zur Übersicht",
"crumb": "Mandant",
"sub": "Kürzel {slug}{sector}",
"statusActive": "Aktiv",
"statusSuspended": "Gesperrt",
"statusArchived": "Archiviert",
"masterDataTitle": "Stammdaten",
"masterDataHint": "Unternehmensdaten aus den Mandanten-Einstellungen.",
"orgName": "Unternehmensname",
"orgShort": "Kurzname",
"slug": "Kürzel",
"address": "Adresse",
"sector": "Sektor",
"status": "Status",
"notSet": "—",
"mainContactTitle": "Hauptkontakt",
"mainContactHint": "Abgeleitet aus dem/den Mandanten-Administrator(en).",
"mainContactNone": "Kein aktiver Mandanten-Administrator hinterlegt.",
"manageTitle": "Verwaltung",
"manageHint": "Module und Benutzer dieses Mandanten in einem Popup verwalten.",
"manageModules": "Module verwalten",
"manageUsers": "Benutzer verwalten",
"usersCount": "{count} Nutzer",
"modulesTitle": "Module",
"modulesHint": "Deaktivierte Module sind für den Kunden ausgeblendet und serverseitig gesperrt.",
"moduleActive": "Aktiv",
"moduleInactive": "Inaktiv",
"moduleActivate": "Aktivieren",
"moduleDeactivate": "Deaktivieren",
"modulesModalSub": "Module für {name} aktivieren/deaktivieren",
"usersModalSub": "Benutzer von {name} — anlegen, Rollen, deaktivieren, Passwort zurücksetzen",
"lifecycleTitle": "Lebenszyklus",
"lifecycleActivate": "Aktivieren",
"lifecycleSuspend": "Sperren (Login blockiert)",
"lifecycleArchive": "Archivieren",
"lifecycleNote": "Gesperrte Mandanten können sich nicht anmelden; Archivierung blendet sie aus.",
"mfaTitle": "MFA-Pflicht",
"mfaHint": "Bei Aktivierung müssen alle Nutzer dieses Mandanten beim nächsten Login eine Zwei-Faktor-Authentifizierung einrichten. Aktuell: {state}.",
"mfaStateOn": "aktiv",
"mfaStateOff": "aus",
"mfaActivate": "MFA-Pflicht aktivieren",
"mfaDeactivate": "MFA-Pflicht deaktivieren",
"auditTitle": "Audit-Trail",
"auditHint": "Nachvollziehbare Aktivitäten dieses Mandanten — wer hat wann was geändert.",
"auditView": "Audit-Trail einsehen",
"auditSub": "Aktivitäten von {name} (neueste zuerst, letzte 200)",
"userCreate": "Benutzer anlegen",
"userCreateSub": "Neuer Nutzer für {name}",
"userEdit": "Benutzer bearbeiten — {name}",
"close": "Schließen",
"phone": "Telefon",
"email": "E-Mail",
"localeTitle": "Mandantensprache",
"localeHint": "Standardsprache für Benachrichtigungen und Dokumente dieses Mandanten (Nutzer wählen ihre UI-Sprache selbst). Aktuell: {lang}.",
"localeDe": "Deutsch",
"localeEn": "English"
}
+23
View File
@@ -0,0 +1,23 @@
{
"appName": "Craftvia",
"appTagline": "Handwerk. Digital auf Kurs.",
"language": "Sprache",
"logout": "Abmelden",
"create": "Anlegen",
"save": "Speichern",
"cancel": "Abbrechen",
"edit": "Bearbeiten",
"delete": "Löschen",
"back": "Zurück",
"close": "Schließen",
"search": "Suchen…",
"actions": "Aktionen",
"add": "Hinzufügen",
"remove": "Entfernen",
"none": "—",
"comingSoon": "Bald verfügbar",
"confirmDelete": "Wirklich löschen?",
"yes": "Ja",
"no": "Nein",
"readOnly": "Nur-Lese-Zugriff."
}
+8
View File
@@ -0,0 +1,8 @@
{
"title": "Dashboard",
"crumb": "Übersicht",
"subtitle": "Willkommen, {name} — {tenant}",
"placeholderTitle": "Dashboard in Umsetzung",
"placeholder": "Hier erscheinen künftig offene Aufträge, heutige Einsätze, freizugebende Berichte und Notdienst-Meldungen.",
"moduleDisabled": "Das angeforderte Modul ist für Ihren Betrieb nicht freigeschaltet."
}
+11
View File
@@ -0,0 +1,11 @@
{
"title": "Anmelden",
"tagline": "Handwerk. Digital auf Kurs.",
"subtitle": "Melde dich mit deinem Firmenkonto an.",
"email": "E-Mail-Adresse",
"password": "Passwort",
"mfaOptional": "MFA-Code (falls eingerichtet)",
"submit": "Anmelden",
"forgotPassword": "Passwort vergessen?",
"error": "Anmeldung fehlgeschlagen. Bitte prüfe E-Mail und Passwort."
}
+18
View File
@@ -0,0 +1,18 @@
{
"customers": "Kunden",
"sites": "Objekte",
"teams": "Teams",
"workOrders": "Aufträge",
"imports": "Auftragsimport",
"field": "Einsätze",
"reports": "Berichte",
"emergency": "Notdienst",
"documents": "Dokumente",
"notifications": "Benachrichtigungen",
"lotse": "Lotse (KI-Assistent)",
"placeholder": {
"crumb": "Modul",
"status": "Modul in Umsetzung",
"hint": "Dieser Bereich wird gerade gebaut. Navigation, Modul-Freischaltung und Berechtigungen sind bereits aktiv."
}
}
+12
View File
@@ -0,0 +1,12 @@
{
"dashboard": "Dashboard",
"workOrders": "Aufträge",
"imports": "Import",
"customers": "Kunden",
"sites": "Objekte",
"teams": "Teams",
"reports": "Berichte",
"documents": "Dokumente",
"settings": "Einstellungen",
"admin": "Admin-Konsole"
}
-1136
View File
File diff suppressed because it is too large Load Diff
+58
View File
@@ -0,0 +1,58 @@
{
"backToOverview": "Back to overview",
"crumb": "Tenant",
"sub": "Slug {slug}{sector}",
"statusActive": "Active",
"statusSuspended": "Suspended",
"statusArchived": "Archived",
"masterDataTitle": "Master data",
"masterDataHint": "Company data from the tenant settings.",
"orgName": "Company name",
"orgShort": "Short name",
"slug": "Slug",
"address": "Address",
"sector": "Sector",
"status": "Status",
"notSet": "—",
"mainContactTitle": "Main contact",
"mainContactHint": "Derived from the tenant administrator(s).",
"mainContactNone": "No active tenant administrator on record.",
"manageTitle": "Management",
"manageHint": "Manage this tenant's modules and users in a popup.",
"manageModules": "Manage modules",
"manageUsers": "Manage users",
"usersCount": "{count} users",
"modulesTitle": "Modules",
"modulesHint": "Disabled modules are hidden from the customer and blocked server-side.",
"moduleActive": "Active",
"moduleInactive": "Inactive",
"moduleActivate": "Activate",
"moduleDeactivate": "Deactivate",
"modulesModalSub": "Enable/disable modules for {name}",
"usersModalSub": "Users of {name} — create, roles, deactivate, reset password",
"lifecycleTitle": "Lifecycle",
"lifecycleActivate": "Activate",
"lifecycleSuspend": "Suspend (login blocked)",
"lifecycleArchive": "Archive",
"lifecycleNote": "Suspended tenants cannot sign in; archiving hides them.",
"mfaTitle": "MFA requirement",
"mfaHint": "When enabled, every user of this tenant must set up two-factor authentication at their next login. Currently: {state}.",
"mfaStateOn": "on",
"mfaStateOff": "off",
"mfaActivate": "Enable MFA requirement",
"mfaDeactivate": "Disable MFA requirement",
"auditTitle": "Audit trail",
"auditHint": "Traceable activity of this tenant — who changed what and when.",
"auditView": "View audit trail",
"auditSub": "Activity of {name} (newest first, last 200)",
"userCreate": "Create user",
"userCreateSub": "New user for {name}",
"userEdit": "Edit user — {name}",
"close": "Close",
"phone": "Phone",
"email": "E-mail",
"localeTitle": "Tenant language",
"localeHint": "Default language for notifications and documents of this tenant (users choose their own UI language). Currently: {lang}.",
"localeDe": "German",
"localeEn": "English"
}
+23
View File
@@ -0,0 +1,23 @@
{
"appName": "Craftvia",
"appTagline": "Trades. Digitally on course.",
"language": "Language",
"logout": "Sign out",
"create": "Create",
"save": "Save",
"cancel": "Cancel",
"edit": "Edit",
"delete": "Delete",
"back": "Back",
"close": "Close",
"search": "Search…",
"actions": "Actions",
"add": "Add",
"remove": "Remove",
"none": "—",
"comingSoon": "Coming soon",
"confirmDelete": "Really delete?",
"yes": "Yes",
"no": "No",
"readOnly": "Read-only access."
}
+8
View File
@@ -0,0 +1,8 @@
{
"title": "Dashboard",
"crumb": "Overview",
"subtitle": "Welcome, {name} — {tenant}",
"placeholderTitle": "Dashboard under construction",
"placeholder": "Open work orders, today's jobs, reports awaiting approval and emergency calls will appear here.",
"moduleDisabled": "The requested module is not enabled for your company."
}
+11
View File
@@ -0,0 +1,11 @@
{
"title": "Sign in",
"tagline": "Trades. Digitally on course.",
"subtitle": "Sign in with your company account.",
"email": "E-mail address",
"password": "Password",
"mfaOptional": "MFA code (if enabled)",
"submit": "Sign in",
"forgotPassword": "Forgot password?",
"error": "Sign-in failed. Please check e-mail and password."
}
+18
View File
@@ -0,0 +1,18 @@
{
"customers": "Customers",
"sites": "Sites",
"teams": "Teams",
"workOrders": "Work orders",
"imports": "Order import",
"field": "Field jobs",
"reports": "Reports",
"emergency": "Emergency service",
"documents": "Documents",
"notifications": "Notifications",
"lotse": "Lotse (AI assistant)",
"placeholder": {
"crumb": "Module",
"status": "Module under construction",
"hint": "This area is currently being built. Navigation, module activation and permissions are already in place."
}
}
+12
View File
@@ -0,0 +1,12 @@
{
"dashboard": "Dashboard",
"workOrders": "Work orders",
"imports": "Import",
"customers": "Customers",
"sites": "Sites",
"teams": "Teams",
"reports": "Reports",
"documents": "Documents",
"settings": "Settings",
"admin": "Admin console"
}
+19 -14
View File
@@ -5,28 +5,26 @@ import createNextIntlPlugin from "next-intl/plugin";
// zum Dev-Server. Diese Lockerungen gelten NUR in der Entwicklung, nie im Build. // zum Dev-Server. Diese Lockerungen gelten NUR in der Entwicklung, nie im Build.
const isDev = process.env.NODE_ENV !== "production"; const isDev = process.env.NODE_ENV !== "production";
// Content-Security-Policy (F-07) — zweite Verteidigungslinie hinter dem // Content-Security-Policy (F-07).
// HTML-Sanitizing (F-03).
// //
// Hinweis Skripte: 'unsafe-inline' ist ein bewusster Zwischenstand. Next.js // Hinweis Skripte: 'unsafe-inline' ist ein bewusster Zwischenstand. Next.js liefert
// liefert seinen Hydration-Bootstrap als Inline-Skript aus; eine Nonce-basierte // seinen Hydration-Bootstrap als Inline-Skript aus; eine Nonce-basierte CSP (Nonce in
// CSP (Nonce in proxy.ts erzeugen und an <script>/Next durchreichen) ist ein // proxy.ts erzeugen und durchreichen) ist ein eigenes Folgepaket.
// eigenes Folgepaket. Bis dahin bleibt 'unsafe-inline' für Skripte bestehen.
const csp = [ const csp = [
"default-src 'self'", "default-src 'self'",
// Skripte: 'unsafe-inline' als Zwischenstand (s.o.); 'unsafe-eval' nur im Dev
// (Turbopack/HMR wertet zur Laufzeit aus, im Produktionsbuild nicht nötig).
`script-src 'self' 'unsafe-inline'${isDev ? " 'unsafe-eval'" : ""}`, `script-src 'self' 'unsafe-inline'${isDev ? " 'unsafe-eval'" : ""}`,
// Next.js sowie @xyflow/react und Tailwind v4 setzen Inline-Styles. // Next.js und Tailwind v4 setzen Inline-Styles.
"style-src 'self' 'unsafe-inline'", "style-src 'self' 'unsafe-inline'",
// data: für den MFA-QR-Code (qrcode → data:-URI); blob: für Graph-Bildexport // data: für den MFA-QR-Code; blob: für clientseitige Foto-Vorschauen (Einsatz-Fotos).
// (html-to-image) und ähnliche clientseitig erzeugte Bilder.
"img-src 'self' data: blob:", "img-src 'self' data: blob:",
// blob: für lokale Wiedergabe von Sprachnotizen vor dem Upload.
"media-src 'self' blob:",
"font-src 'self' data:", "font-src 'self' data:",
// Im Dev zusätzlich der HMR-WebSocket des Dev-Servers. // Im Dev zusätzlich der HMR-WebSocket des Dev-Servers.
`connect-src 'self'${isDev ? " ws: wss:" : ""}`, `connect-src 'self'${isDev ? " ws: wss:" : ""}`,
// html-to-image kann Worker aus Blobs erzeugen. // PWA: Service Worker nur vom eigenen Ursprung.
"worker-src 'self' blob:", "worker-src 'self'",
"manifest-src 'self'",
"frame-ancestors 'none'", "frame-ancestors 'none'",
"form-action 'self'", "form-action 'self'",
"base-uri 'self'", "base-uri 'self'",
@@ -40,12 +38,19 @@ const securityHeaders = [
{ key: "X-Content-Type-Options", value: "nosniff" }, { key: "X-Content-Type-Options", value: "nosniff" },
{ key: "X-Frame-Options", value: "DENY" }, { key: "X-Frame-Options", value: "DENY" },
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" }, { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
{ key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=(), payment=()" }, // Einsatz-App: Kamera (Fotos), Mikrofon (Sprachnotizen), Standort (Einsatzstart) —
// nur für den eigenen Ursprung. Zahlungs-API bleibt aus.
{ key: "Permissions-Policy", value: "camera=(self), microphone=(self), geolocation=(self), payment=()" },
]; ];
const nextConfig: NextConfig = { const nextConfig: NextConfig = {
// Standalone-Output für den Docker-Multi-Stage-Build (siehe Dockerfile) // Standalone-Output für den Docker-Multi-Stage-Build (siehe Dockerfile)
output: "standalone", output: "standalone",
// i18n-Kataloge werden zur Laufzeit per fs geladen (src/i18n/request.ts) — für den
// standalone-Output explizit mitkopieren.
outputFileTracingIncludes: {
"/*": ["./messages/**/*.json"],
},
async headers() { async headers() {
return [ return [
{ {
-82
View File
@@ -1,82 +0,0 @@
// Import der Umsetzungshinweise (Story B5-1) aus dem Fachcontent C6 in das globale
// Modell ImplementationHint. Ein Block je Teilanforderung:
//
// ### <id> — [<STUFE>]
// **Anforderung:** …
// - **Organisatorisch:** …
// - **Technisch:** …
// - **Typische Nachweise:** …
// - **Vorlage:** L00
// - **Ressourcen:** …
// - **AL-Filter:** AL2 + AL3
//
// Idempotent (Upsert über reqId). Kein tenantId (globaler Katalog).
import { readFileSync } from "node:fs";
import { join } from "node:path";
import type { PrismaClient } from "@prisma/client";
const C6_PATH = join(__dirname, "..", "docs", "wizard-uebergabe", "02_Fachcontent_C1-C9", "C6_Umsetzungshinweise.md");
const field = (block: string, label: string): string => {
// Feldzeile "- **Label:** Wert" oder "**Label:** Wert" bis Zeilenende.
const re = new RegExp(`\\*\\*${label}:\\*\\*\\s*(.+)`);
const m = block.match(re);
return m ? m[1].trim() : "";
};
const isProcurement = (resources: string): boolean => {
const r = resources.toLowerCase();
if (/kein\s+toolbudget|kein\s+budget|ohne\s+budget|keine\s+beschaffung/.test(r)) return false;
return /beschaffung|toolbudget|budget|lizenz|invest|anschaffung|€|eur\b/.test(r);
};
export interface ParsedHint {
reqId: string; control: string; stufe: string; requirement: string;
organisational: string; technical: string; evidence: string;
template: string | null; resources: string; alFilter: string; procurement: boolean;
}
/** Parst den C6-Text zu Hinweis-Objekten (ohne DB-Zugriff — testbar). */
export function parseC6Hints(text: string): ParsedHint[] {
const hints: ParsedHint[] = [];
// Blöcke ab "### <id> — [STUFE]"; nächster "### "/"## "/"# " beendet den Block.
const re = /^###\s+(\S+)\s+—\s+\[([^\]]+)\]\s*\n([\s\S]*?)(?=^#{1,3}\s|$(?![\s\S]))/gm;
let m: RegExpExecArray | null;
while ((m = re.exec(text)) !== null) {
const reqId = m[1].trim();
if (!/^\d+\.\d+\.\d+-/.test(reqId)) continue; // nur Anforderungs-IDs
const stufe = m[2].trim();
const block = m[3];
const resources = field(block, "Ressourcen");
const template = field(block, "Vorlage") || null;
hints.push({
reqId,
control: reqId.split("-")[0],
stufe,
requirement: field(block, "Anforderung"),
organisational: field(block, "Organisatorisch"),
technical: field(block, "Technisch"),
evidence: field(block, "Typische Nachweise"),
template,
resources,
alFilter: field(block, "AL-Filter"),
procurement: isProcurement(resources),
});
}
return hints;
}
/** Liest C6 und schreibt die Hinweise (Upsert je reqId). Liefert die Anzahl. */
export async function importImplementationHints(prisma: PrismaClient): Promise<number> {
const text = readFileSync(C6_PATH, "utf-8");
const hints = parseC6Hints(text);
for (const h of hints) {
await prisma.implementationHint.upsert({
where: { reqId: h.reqId },
update: { control: h.control, stufe: h.stufe, requirement: h.requirement, organisational: h.organisational, technical: h.technical, evidence: h.evidence, template: h.template, resources: h.resources, alFilter: h.alFilter, procurement: h.procurement },
create: h,
});
}
return hints.length;
}
-222
View File
@@ -1,222 +0,0 @@
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)." },
{ level: 5, label: "Nahezu sicher", definition: "Ereignis ist praktisch dauerhaft gegeben oder tritt ständig ein." },
];
const DAMAGE_DIMS: { name: string; levels: Record<string, string> }[] = [
{ name: "Gesetzes-/Vertragsverstöße", levels: { "1": "unerheblich", "2": "geringfügige Verstöße", "3": "erhebliche Verstöße/Bußgelder", "4": "gravierende Rechtsfolgen/Straftatbestand", "5": "existenzbedrohende Rechtsfolgen (Lizenzentzug/Haftung)" } },
{ name: "Datenschutz", levels: { "1": "kein Personenbezug", "2": "geringe Betroffenheit", "3": "sensible Daten betroffen", "4": "massenhaft sensible Daten / hohe Betroffenheit", "5": "besonders schützenswerte Daten in großem Umfang / existenzielle Betroffenheit" } },
{ name: "Persönliche Unversehrtheit", levels: { "1": "keine", "2": "leichte Beeinträchtigung", "3": "Gesundheitsgefahr", "4": "Gefahr für Leib und Leben", "5": "Lebensgefahr für viele / Todesfolge" } },
{ name: "Aufgabenerfüllung", levels: { "1": "keine Einschränkung", "2": "geringe Einschränkung", "3": "erhebliche Einschränkung", "4": "Kernaufgaben nicht erfüllbar", "5": "vollständiger, dauerhafter Ausfall der Organisation" } },
{ name: "Innen-/Außenwirkung (Reputation)", levels: { "1": "keine", "2": "intern begrenzt", "3": "öffentlich wahrnehmbar", "4": "nachhaltiger Reputationsschaden", "5": "existenzbedrohender, dauerhafter Reputationsverlust" } },
{ name: "Störungs-/Ausfallzeit", levels: { "1": "< 1 Std.", "2": "bis 1 Tag", "3": "bis 1 Woche", "4": "> 1 Woche", "5": "> 1 Monat / dauerhaft" } },
{ name: "Finanzielle Auswirkungen", levels: { "1": "bis 10 T€", "2": "bis 50 T€", "3": "bis 200 T€", "4": "über 500 T€", "5": "existenzbedrohend (> 1 Mio €)" } },
];
// 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"],
},
];
/** Generische verwaltete Register (WP3.0): Code, Titel, Pflichtspalten, Cross-Links. */
const GENERIC_REGISTERS: {
code: string; title: string; description: string; orderIdx: number;
columns: { key: string; label: string }[]; supplierLink?: boolean; assetLink?: boolean;
}[] = [
{ code: "REG-PROJECTS", title: "Register Informationssicherheit in Projekten", orderIdx: 905,
description: "Projekte mit IS-Klassifizierung, Risikobewertung, Maßnahmen und ISB-Einbindung (R01 / VA-19).",
columns: [{ key: "projekt", label: "Projekt" }, { key: "klassifizierung", label: "IS-Klassifizierung" }, { key: "risiko", label: "Risikobewertung" }, { key: "massnahmen", label: "Maßnahmen/Status" }, { key: "isb", label: "ISB-Einbindung" }] },
{ code: "REG-SENS-ROLES", title: "Register sensibler Tätigkeiten", orderIdx: 906,
description: "Sensible Tätigkeitsbereiche/Rollen mit Eignungsnachweisen und Prüftiefe (R05 / VA-14).",
columns: [{ key: "rolle", label: "Rolle/Bereich" }, { key: "nachweise", label: "Geforderte Eignungsnachweise" }, { key: "prueftiefe", label: "Prüftiefe" }, { key: "verantwortlich", label: "Verantwortlich" }, { key: "review", label: "Review" }] },
{ code: "REG-AUDIT-PLAN", title: "Audit-Programm-Register", orderIdx: 907,
description: "Auditprogramm mit Zeitplan, Umfang, geprüften Controls, Prüfern und Ergebnissen (R03 / VA-15).",
columns: [{ key: "zeitplan", label: "Zeitplan" }, { key: "umfang", label: "Umfang" }, { key: "controls", label: "Geprüfte Controls" }, { key: "pruefer", label: "Prüfer" }, { key: "ergebnisse", label: "Ergebnisse" }] },
// REG-NET bleibt ein leichtgewichtiges Register: der Netzplan ist ein extern gepflegtes
// Dokument, daher nur Referenz/Speicherort statt Tabelleninhalt (Datei-Upload folgt separat).
{ code: "REG-NET", title: "Netz-/Netzdienste-Register", orderIdx: 911,
description: "Verweis auf den (extern gepflegten) Netzplan sowie Netzdienste/Zonen und Review-Turnus (R09 5.1.2 / R10 5.2.7 / VA-07).",
columns: [{ key: "netzplan", label: "Netzplan (Speicherort/Referenz)" }, { key: "netzdienst", label: "Netzdienst" }, { key: "zone", label: "Zone" }, { key: "review", label: "Review" }] },
];
// GAP-Konsolidierung: Diese Register wurden in die Fachmodule überführt und werden
// daher stillgelegt (Dokument + ManagedRegister + Zeilen entfernt). Die zugehörigen
// {{LINK:…}} zeigen jetzt auf die Module (siehe resolveLink):
// REG-EXT-SERVICES → Lieferanten/IT-Services · REG-SW-WHITELIST → Lieferanten/Software
// REG-CRIT-SERVICES → Assets (kritische Dienste) · REG-PROJECTS → Assets (Projekte)
const RETIRED_REGISTERS = ["REG-EXT-SERVICES", "REG-SW-WHITELIST", "REG-CRIT-SERVICES", "REG-PROJECTS"];
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." },
];
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}` },
});
}
// Generische verwaltete Register (GAP-Report WP3.0 / §5.2): Dokument (Bibliothek/Link)
// + editierbare ManagedRegister-Definition mit Pflichtspalten + Cross-Links. Zeilen
// (RegisterRow) bleiben bei Re-Import erhalten (nicht-destruktiv).
for (const g of GENERIC_REGISTERS) {
const intro = `${g.description} Pflichtspalten: ${g.columns.map((c) => c.label).join(", ")}.`;
await prisma.policyDocument.upsert({
where: { tenantId_code: { tenantId, code: g.code } },
update: { title: g.title, type: "REGISTER", orderIdx: g.orderIdx, rawMarkdown: `# ${g.title}\n\n${intro}` },
create: { tenantId, code: g.code, type: "REGISTER", title: g.title, version: "1.0", status: "FREIGEGEBEN", orderIdx: g.orderIdx, rawMarkdown: `# ${g.title}\n\n${intro}` },
});
await prisma.managedRegister.upsert({
where: { tenantId_code: { tenantId, code: g.code } },
update: { title: g.title, description: g.description, columns: g.columns, supplierLink: g.supplierLink ?? false, assetLink: g.assetLink ?? false },
create: { tenantId, code: g.code, title: g.title, description: g.description, columns: g.columns, supplierLink: g.supplierLink ?? false, assetLink: g.assetLink ?? false },
});
}
// Stillgelegte Register entfernen (in Fachmodule überführt). RegisterRow hängt per
// Cascade an ManagedRegister; das Bibliotheks-Dokument wird ebenfalls entfernt.
await prisma.managedRegister.deleteMany({ where: { tenantId, code: { in: RETIRED_REGISTERS } } });
await prisma.policyDocument.deleteMany({ where: { tenantId, code: { in: RETIRED_REGISTERS } } });
// 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 };
}
-521
View File
@@ -1,521 +0,0 @@
import { readFileSync, readdirSync } from "node:fs";
import { join } from "node:path";
import type { PrismaClient, Framework } from "@prisma/client";
/**
* Mapping-Datei je Framework (AP1). Beide Frameworks teilen sich denselben
* Dokumentensatz (richtlinien/, verfahren/, Baseline, Nachweisregister); nur die
* Anforderungs-Zuordnung unterscheidet sich → nur der Mapping-Dateiname wechselt
* (Variante A). Default TISAX, damit Bestandsaufrufer unverändert bleiben.
*/
export function mappingFileFor(framework: Framework = "TISAX"): string {
return framework === "ISO_27001" ? "mapping-iso.json" : "mapping.json";
}
/** Kanonische Version des Vorlagenpakets (mapping meta.version) — Story B6. */
export function readPackageVersion(seedDir: string, mappingFile = "mapping.json"): string {
const mapping = JSON.parse(readFileSync(join(seedDir, mappingFile), "utf-8")) as { meta?: { version?: string } };
return mapping.meta?.version ?? "unknown";
}
/** Übernommene Paket-Version je Mandant/Framework stempeln (Story B6) — nach jedem Apply. */
export async function stampPackageState(
prisma: PrismaClient,
tenantId: string,
seedDir: string,
framework: Framework = "TISAX",
): Promise<void> {
await stampPackageVersion(prisma, tenantId, readPackageVersion(seedDir, mappingFileFor(framework)), framework);
}
/** Wie stampPackageState, aber mit direkt übergebener Version (DB-Vorlagen-Quelle). */
export async function stampPackageVersion(
prisma: PrismaClient,
tenantId: string,
version: string,
framework: Framework = "TISAX",
): Promise<void> {
await prisma.policyPackageState.upsert({
where: { tenantId_framework: { tenantId, framework } },
update: { importedVersion: version, importedAt: new Date() },
create: { tenantId, framework, importedVersion: version, importedAt: new Date() },
});
}
/**
* Importer für das VDA-ISA-2027-Vorlagenpaket (§6 der Spezifikation).
*
* Das Modul ist in zwei Schritte getrennt:
* 1. `parsePackageFiles(seedDir)` — liest die echten .md-Dateien, mapping.json,
* variables.schema.json, Technische-Sicherheits-Baseline.md und
* Nachweisregister_zentral.md in ein neutrales `ParsedPackage`.
* 2. `reconcilePackage(prisma, tenantId, pkg)` — schreibt ein `ParsedPackage`
* **nicht-destruktiv** in die Mandanten-Tabellen (Diff/Upsert).
* Dieselbe `ParsedPackage`-Struktur kommt auch aus der globalen DB-Vorlagenablage
* (template-store.ts), sodass der Reconcile-Import identisch für Datei- UND
* DB-Quelle funktioniert.
*
* **Nicht-destruktiver Re-Import (Phase-1-Härtung Paket 3):** Abgleich über stabile
* Schlüssel (Dokument-`code`, Anforderungs-`reqId`, Variablen-`key`, Baseline-`blId`,
* Nachweis-`nr`) per Diff/Upsert statt Löschen+Neuanlegen.
* - Neu im Paket ⇒ anlegen.
* - Weiterhin vorhanden ⇒ Inhalt aktualisieren, aber per-Dokument-Status,
* TISAX-Override, Freigabe-/Versionsstand und Verknüpfungen (Coverage/Nachweise)
* sowie **nutzergepflegte Variablenwerte** erhalten.
* - Nicht mehr im Paket ⇒ **deaktivieren** (`archivedAt` setzen), nicht löschen.
* Jeder Lauf liefert einen Änderungsreport (added/updated/archived/reactivated) und
* protokolliert ihn im Audit-Log. `dryRun` erzeugt den Report ohne Schreibzugriffe
* (Vorschau). Idempotent: erneuter Lauf ohne Änderungen ⇒ nur „unchanged".
*/
type DocType = "LEITLINIE" | "RICHTLINIE" | "VERFAHREN" | "REGISTER";
const arraysEqual = (a: string[], b: string[]) => a.length === b.length && a.every((v, i) => v === b[i]);
// ── Neutrale Paket-Struktur (Quelle: Dateien ODER globale DB-Vorlagenablage) ──────
export interface ParsedDoc {
code: string;
type: DocType;
title: string;
policyCode: string | null;
fulfills: string[];
rawMarkdown: string;
orderIdx: number;
}
export interface ParsedRequirement {
reqId: string;
policyCode: string;
control: string;
obligation: string;
condition: string | null;
requirement: string;
implementation: string;
vaCodes: string[];
nachweisLink: string | null;
}
export interface ParsedVariable {
key: string;
title: string;
kind: string;
groupName: string | null;
value: string;
required: boolean;
orderIdx: number;
}
export interface ParsedBaselineParam {
blId: string;
section: string;
name: string;
vorgabe: string;
orderIdx: number;
}
export interface ParsedEvidence {
nr: number;
policyCode: string;
nachweis: string;
quelle: string;
verantwortlich: string;
turnus: string;
}
export interface ParsedPackage {
version: string;
documents: ParsedDoc[];
requirements: ParsedRequirement[];
variables: ParsedVariable[];
baseline: ParsedBaselineParam[];
evidence: ParsedEvidence[];
}
/**
* Namensraum der Dokumente, die DIESER Importer verwaltet (Leitlinie, Richtlinien,
* Verfahren, Baseline-/Nachweisregister). Verwaltete Register aus import-managed.ts
* (CRYPTO, RISKMATRIX, CLASSIFICATION, HANDBUCH, …) gehören NICHT dazu und dürfen beim
* Re-Import nicht deaktiviert werden.
*/
const isPackageDoc = (code: string) =>
code === "L00" || code === "BASELINE" || code === "NACHWEIS" || /^R\d/.test(code) || /^VA-/.test(code);
function docCodeFromFile(file: string): string {
return file.split("_")[0]; // "R08_…​.md" → "R08", "VA-03_…​.md" → "VA-03"
}
function titleOf(md: string): string {
const m = md.match(/^#\s+(.+?)\s*$/m);
return m ? m[1].trim() : "(ohne Titel)";
}
/** Umsetzungstext je Anforderung aus den `<!-- IMPL id -->`-Ankern extrahieren. */
function extractImpls(raw: string, into: Map<string, string>) {
const lines = raw.split("\n");
for (let i = 0; i < lines.length; i++) {
const m = lines[i].match(/^<!--\s*IMPL\s+(\S+)\s*-->/);
if (!m) continue;
const buf: string[] = [];
for (let j = i + 1; j < lines.length; j++) {
const l = lines[j];
const tr = l.trim();
if (tr === "" || tr.startsWith("<!--") || tr.startsWith("{{#") || tr.startsWith("{{/") || /^#{1,6}\s/.test(tr) || tr.startsWith("**") || tr.startsWith("- ") || tr.startsWith("|")) break;
buf.push(l);
}
if (buf.length) into.set(m[1], buf.join(" ").trim());
}
}
function varGroup(key: string): string {
if (key.startsWith("FLAG_")) return "Feature-Flags";
if (key.startsWith("ORG_") || key.startsWith("ISMS_")) return "Organisation";
if (key.startsWith("ROLE_")) return "Rollen";
if (key.startsWith("DOC_")) return "Metadaten";
if (key.startsWith("TECH_") || key.startsWith("TOOL_")) return "Technik/Werkzeuge";
if (/^(PW_|MFA_|SESSION_|ACCOUNT_|RECERT_)/.test(key)) return "IAM-Baseline";
if (key.startsWith("TLS_") || key.startsWith("CRYPTO_")) return "Krypto-Baseline";
if (/^(PATCH_|VULN_|MALWARE_|LOG_|BACKUP_|PENTEST_)/.test(key)) return "Betriebs-Baseline";
return "Sonstige";
}
interface SchemaProp {
type?: string;
title?: string;
default?: string | boolean;
example?: string;
}
export interface ImportReport {
documents: { added: number; updated: number; archived: number; reactivated: number; unchanged: number };
requirements: { added: number; updated: number; archived: number; reactivated: number; unchanged: number };
variables: { added: number; updated: number; unchanged: number; obsolete: number };
baseline: { added: number; updated: number; unchanged: number; obsolete: number };
evidence: { added: number; updated: number; unchanged: number; obsolete: number };
}
export interface ImportOptions {
/** true = nur Report berechnen, keine Schreibzugriffe (Vorschau). */
dryRun?: boolean;
/** Akteur für den Audit-Eintrag (z. B. Plattform-/Mandanten-Admin). */
actorId?: string | null;
/**
* Namensraum der abzugleichenden Anforderungen (AP1). Der Archivierungslauf und die
* Anlage laufen NUR innerhalb dieses Frameworks — ein ISO-Import archiviert damit
* keine TISAX-Anforderungen (und umgekehrt, Falle 1.1). Default TISAX.
*/
framework?: Framework;
/**
* Geteilte Inhalte (Dokumente, Variablen, Baseline, Nachweisregister) mit abgleichen.
* Diese sind für beide Frameworks identisch und dürfen je Mandant **einmal**
* abgeglichen werden (Falle 1.1). Beim Import mehrerer Frameworks nur für das erste
* `true` setzen. Default true (Einzel-Framework-Fall unverändert).
*/
reconcileShared?: boolean;
}
/**
* Liest das Datei-Vorlagenpaket in eine neutrale `ParsedPackage`-Struktur (kein DB-Zugriff).
* Quelle für die einmalige Synchronisierung in die globale DB-Vorlagenablage und für den
* Datei-Fallback des Mandanten-Imports.
*/
export function parsePackageFiles(seedDir: string, mappingFile = "mapping.json"): ParsedPackage {
// ── 1. mapping-Datei → Anforderungen + FULFILLS-Rückwärtsmapping vorbereiten ──
const mapping = JSON.parse(readFileSync(join(seedDir, mappingFile), "utf-8")) as {
meta?: { version?: string };
anforderungen: Array<{
id: string; policy: string; control: string; type: string;
condition: string | null; requirement: string; implementation?: string; nachweis_link?: string;
/** Optional: „IMPL <key>" — Umsetzungsblock, aus dem der Text stammt (Control- statt Anforderungsebene). */
impl_anchor?: string;
}>;
};
const version = mapping.meta?.version ?? "unknown";
// ── 2. Dokumente einlesen (Richtlinien + Verfahren) ──────────────────────
const reqToVas = new Map<string, Set<string>>();
const implMap = new Map<string, string>(); // Umsetzungstext je Anforderung aus den IMPL-Ankern
const documents: ParsedDoc[] = [];
const richtDir = join(seedDir, "richtlinien");
for (const file of readdirSync(richtDir).filter((f) => f.endsWith(".md")).sort()) {
const raw = readFileSync(join(richtDir, file), "utf-8");
const code = docCodeFromFile(file);
const type: DocType = code === "L00" ? "LEITLINIE" : "RICHTLINIE";
const orderIdx = code === "L00" ? 0 : parseInt(code.replace(/\D/g, ""), 10);
extractImpls(raw, implMap);
documents.push({ code, type, title: titleOf(raw), policyCode: null, fulfills: [], rawMarkdown: raw, orderIdx });
}
const verfDir = join(seedDir, "verfahren");
for (const file of readdirSync(verfDir).filter((f) => f.endsWith(".md")).sort()) {
const raw = readFileSync(join(verfDir, file), "utf-8");
const code = docCodeFromFile(file);
const fh = raw.match(/<!--\s*FULFILLS\s+([^|]+?)\s*\|\s*POLICY\s+(\S+)\s*-->/);
const fulfills = fh ? fh[1].split(",").map((s) => s.trim()).filter(Boolean) : [];
const policyCode = fh ? fh[2].trim() : null;
for (const req of fulfills) {
if (!reqToVas.has(req)) reqToVas.set(req, new Set());
reqToVas.get(req)!.add(code);
}
const orderIdx = 100 + parseInt(code.replace(/\D/g, ""), 10);
documents.push({ code, type: "VERFAHREN", title: titleOf(raw), policyCode, fulfills, rawMarkdown: raw, orderIdx });
}
// Register-Dokumente (Baseline + Nachweisregister) mitführen
const baselineRaw = readFileSync(join(seedDir, "Technische-Sicherheits-Baseline.md"), "utf-8");
const nachweisRaw = readFileSync(join(seedDir, "Nachweisregister_zentral.md"), "utf-8");
documents.push({ code: "BASELINE", type: "REGISTER", title: titleOf(baselineRaw), policyCode: null, fulfills: [], rawMarkdown: baselineRaw, orderIdx: 900 });
documents.push({ code: "NACHWEIS", type: "REGISTER", title: titleOf(nachweisRaw), policyCode: null, fulfills: [], rawMarkdown: nachweisRaw, orderIdx: 901 });
// ── 3. Anforderungen ─────────────────────────────────────────────────────
// Umsetzungstext: bevorzugt über `impl_anchor` (Control-Ebene, z. B. „IMPL 4.1.2") auflösen —
// die Anforderungs-IDs (4.1.2-M1) haben keinen eigenen IMPL-Block. Fällt zurück auf die
// Anforderungs-ID und zuletzt auf ein im Mapping mitgeliefertes `implementation`-Feld.
const implOf = (a: { id: string; impl_anchor?: string; implementation?: string }) =>
implMap.get((a.impl_anchor ?? "").replace(/^IMPL\s+/, "")) ?? implMap.get(a.id) ?? a.implementation ?? "";
const requirements: ParsedRequirement[] = mapping.anforderungen.map((a) => ({
reqId: a.id, policyCode: a.policy, control: a.control, obligation: a.type,
condition: a.condition, requirement: a.requirement, implementation: implOf(a),
vaCodes: [...(reqToVas.get(a.id) ?? [])].sort(), nachweisLink: a.nachweis_link ?? null,
}));
// ── 4. Variablen + Feature-Flags ─────────────────────────────────────────
const schema = JSON.parse(readFileSync(join(seedDir, "variables.schema.json"), "utf-8")) as {
properties: Record<string, SchemaProp>; required?: string[];
};
const required = new Set(schema.required ?? []);
const variables: ParsedVariable[] = Object.entries(schema.properties).map(([key, prop], i) => {
const kind = prop.type === "boolean" ? "boolean" : "string";
const value = prop.default !== undefined ? String(prop.default) : prop.example !== undefined ? String(prop.example) : kind === "boolean" ? "false" : "";
return { key, title: prop.title ?? key, kind, groupName: varGroup(key), value, required: required.has(key), orderIdx: i };
});
// ── 5. Baseline-Parameter ────────────────────────────────────────────────
const baseline: ParsedBaselineParam[] = [];
{
let section = "";
let bi = 0;
for (const line of baselineRaw.split("\n")) {
const h = line.match(/^##\s+(.+?)\s*$/);
if (h) { section = h[1].trim(); continue; }
const row = line.match(/^\|\s*(BL-[A-Z]+-\d+)\s*\|\s*(.+?)\s*\|\s*(.+?)\s*\|\s*$/);
if (row) baseline.push({ blId: row[1], section, name: row[2].trim(), vorgabe: row[3].trim(), orderIdx: bi++ });
}
}
// ── 6. Nachweisregister ──────────────────────────────────────────────────
const evidence: ParsedEvidence[] = [];
for (const line of nachweisRaw.split("\n")) {
const row = line.match(/^\|\s*(\d+)\s*\|\s*(.+?)\s*\|\s*(.+?)\s*\|\s*(.+?)\s*\|\s*(.+?)\s*\|\s*(.+?)\s*\|\s*$/);
if (!row) continue;
const policyMatch = row[2].match(/\{\{LINK:([^}]+)\}\}/);
evidence.push({
nr: parseInt(row[1], 10), policyCode: policyMatch ? policyMatch[1] : row[2].trim(),
nachweis: row[3].trim(), quelle: row[4].trim(), verantwortlich: row[5].trim(), turnus: row[6].trim(),
});
}
return { version, documents, requirements, variables, baseline, evidence };
}
/**
* Schreibt ein `ParsedPackage` nicht-destruktiv in die Mandanten-Tabellen (Diff/Upsert).
* Quelle-neutral: das Paket stammt aus den Dateien (`parsePackageFiles`) oder der globalen
* DB-Vorlagenablage (`loadPublishedPackage`).
*/
export async function reconcilePackage(
prisma: PrismaClient,
tenantId: string,
pkg: ParsedPackage,
options: ImportOptions = {}
) {
const dryRun = options.dryRun ?? false;
const framework: Framework = options.framework ?? "TISAX";
// Geteilte Inhalte (Dokumente/Variablen/Baseline/Nachweise) sind frameworkübergreifend
// identisch → nur einmal je Mandant abgleichen (Falle 1.1). Anforderungen dagegen
// IMMER, aber framework-scoped.
const reconcileShared = options.reconcileShared ?? true;
const now = new Date();
const report: ImportReport = {
documents: { added: 0, updated: 0, archived: 0, reactivated: 0, unchanged: 0 },
requirements: { added: 0, updated: 0, archived: 0, reactivated: 0, unchanged: 0 },
variables: { added: 0, updated: 0, unchanged: 0, obsolete: 0 },
baseline: { added: 0, updated: 0, unchanged: 0, obsolete: 0 },
evidence: { added: 0, updated: 0, unchanged: 0, obsolete: 0 },
};
// ── Reconcile Dokumente (Upsert über code; Status/Override/Freigabe bleiben) ──
// Geteilt → nur beim ersten Framework je Mandant (Falle 1.1).
if (reconcileShared) {
const existingDocs = await prisma.policyDocument.findMany({ where: { tenantId } });
const docByCode = new Map(existingDocs.map((d) => [d.code, d]));
const desiredDocCodes = new Set(pkg.documents.map((d) => d.code));
for (const d of pkg.documents) {
const ex = docByCode.get(d.code);
if (!ex) {
report.documents.added++;
if (!dryRun)
await prisma.policyDocument.create({
data: {
tenantId, code: d.code, type: d.type, title: d.title, version: "1.0", status: "FREIGEGEBEN",
policyCode: d.policyCode, fulfills: d.fulfills, rawMarkdown: d.rawMarkdown, orderIdx: d.orderIdx,
},
});
continue;
}
const contentChanged =
ex.type !== d.type || ex.title !== d.title || (ex.policyCode ?? null) !== (d.policyCode ?? null) ||
!arraysEqual(ex.fulfills, d.fulfills) || ex.rawMarkdown !== d.rawMarkdown || ex.orderIdx !== d.orderIdx;
// Nur Inhaltsfelder + Lifecycle; Status/Version/Owner/Freigabe/Override NICHT anfassen.
const contentData = { type: d.type, title: d.title, policyCode: d.policyCode, fulfills: d.fulfills, rawMarkdown: d.rawMarkdown, orderIdx: d.orderIdx };
if (ex.archivedAt) {
report.documents.reactivated++;
if (!dryRun) await prisma.policyDocument.update({ where: { id: ex.id }, data: { ...contentData, archivedAt: null } });
} else if (contentChanged) {
report.documents.updated++;
if (!dryRun) await prisma.policyDocument.update({ where: { id: ex.id }, data: contentData });
} else {
report.documents.unchanged++;
}
}
for (const ex of existingDocs) {
// Nur Paket-Dokumente deaktivieren — verwaltete Register (import-managed) unberührt lassen.
if (desiredDocCodes.has(ex.code) || ex.archivedAt || !isPackageDoc(ex.code)) continue;
report.documents.archived++;
if (!dryRun) await prisma.policyDocument.update({ where: { id: ex.id }, data: { archivedAt: now } });
}
} // Ende reconcileShared (Dokumente)
// ── Reconcile Anforderungen (Upsert über reqId; entfernte deaktiviert) — FRAMEWORK-SCOPED ──
// Nur Anforderungen DIESES Frameworks lesen/anlegen/archivieren, damit ein ISO-Import
// die TISAX-Anforderungen (und umgekehrt) nicht stilllegt (Falle 1.1).
const existingReqs = await prisma.policyRequirement.findMany({ where: { tenantId, framework } });
const reqById = new Map(existingReqs.map((r) => [r.reqId, r]));
const desiredReqIds = new Set(pkg.requirements.map((r) => r.reqId));
for (const r of pkg.requirements) {
const ex = reqById.get(r.reqId);
if (!ex) {
report.requirements.added++;
if (!dryRun) await prisma.policyRequirement.create({ data: { tenantId, framework, ...r } });
continue;
}
const changed =
ex.policyCode !== r.policyCode || ex.control !== r.control || ex.obligation !== r.obligation ||
(ex.condition ?? null) !== (r.condition ?? null) || ex.requirement !== r.requirement ||
ex.implementation !== r.implementation || !arraysEqual(ex.vaCodes, r.vaCodes) ||
(ex.nachweisLink ?? null) !== (r.nachweisLink ?? null);
if (ex.archivedAt) {
report.requirements.reactivated++;
if (!dryRun) await prisma.policyRequirement.update({ where: { id: ex.id }, data: { ...r, archivedAt: null } });
} else if (changed) {
report.requirements.updated++;
if (!dryRun) await prisma.policyRequirement.update({ where: { id: ex.id }, data: r });
} else {
report.requirements.unchanged++;
}
}
for (const ex of existingReqs) {
if (desiredReqIds.has(ex.reqId) || ex.archivedAt) continue;
report.requirements.archived++;
if (!dryRun) await prisma.policyRequirement.update({ where: { id: ex.id }, data: { archivedAt: now } });
}
// Geteilte Inhalte (Variablen/Baseline/Nachweise) wieder nur beim ersten Framework.
if (reconcileShared) {
// ── Reconcile Variablen + Feature-Flags (Wert bleibt IMMER erhalten) ──
const existingVars = await prisma.policyVariable.findMany({ where: { tenantId } });
const varByKey = new Map(existingVars.map((v) => [v.key, v]));
const desiredVarKeys = new Set(pkg.variables.map((v) => v.key));
for (const v of pkg.variables) {
const ex = varByKey.get(v.key);
if (!ex) {
report.variables.added++;
if (!dryRun) await prisma.policyVariable.create({ data: { tenantId, ...v } });
continue;
}
// Nur Metadaten aktualisieren — den (ggf. vom Kunden gepflegten) value NIE überschreiben.
const metaChanged =
ex.title !== v.title || ex.kind !== v.kind || (ex.groupName ?? null) !== (v.groupName ?? null) ||
ex.required !== v.required || ex.orderIdx !== v.orderIdx;
if (metaChanged) {
report.variables.updated++;
if (!dryRun) await prisma.policyVariable.update({ where: { id: ex.id }, data: { title: v.title, kind: v.kind, groupName: v.groupName, required: v.required, orderIdx: v.orderIdx } });
} else {
report.variables.unchanged++;
}
}
// Nicht mehr im Schema enthaltene Variablen bleiben bestehen (können in eigenem
// Rohtext referenziert sein) — nur als „obsolet" im Report ausgewiesen.
report.variables.obsolete = existingVars.filter((v) => !desiredVarKeys.has(v.key)).length;
// ── Reconcile Baseline-Parameter (Upsert über blId) ───────────────────
const existingBaseline = await prisma.policyBaselineParam.findMany({ where: { tenantId } });
const blById = new Map(existingBaseline.map((b) => [b.blId, b]));
const desiredBlIds = new Set(pkg.baseline.map((b) => b.blId));
for (const b of pkg.baseline) {
const ex = blById.get(b.blId);
if (!ex) {
report.baseline.added++;
if (!dryRun) await prisma.policyBaselineParam.create({ data: { tenantId, ...b } });
continue;
}
const changed = ex.section !== b.section || ex.name !== b.name || ex.vorgabe !== b.vorgabe || ex.orderIdx !== b.orderIdx;
if (changed) {
report.baseline.updated++;
if (!dryRun) await prisma.policyBaselineParam.update({ where: { id: ex.id }, data: { section: b.section, name: b.name, vorgabe: b.vorgabe, orderIdx: b.orderIdx } });
} else {
report.baseline.unchanged++;
}
}
report.baseline.obsolete = existingBaseline.filter((b) => !desiredBlIds.has(b.blId)).length;
// ── Reconcile Nachweisregister (Upsert über nr) ───────────────────────
const existingEvidence = await prisma.policyEvidence.findMany({ where: { tenantId } });
const evByNr = new Map(existingEvidence.map((e) => [e.nr, e]));
const desiredNrs = new Set(pkg.evidence.map((e) => e.nr));
for (const e of pkg.evidence) {
const ex = evByNr.get(e.nr);
if (!ex) {
report.evidence.added++;
if (!dryRun) await prisma.policyEvidence.create({ data: { tenantId, ...e } });
continue;
}
const changed = ex.policyCode !== e.policyCode || ex.nachweis !== e.nachweis || ex.quelle !== e.quelle || ex.verantwortlich !== e.verantwortlich || ex.turnus !== e.turnus;
if (changed) {
report.evidence.updated++;
if (!dryRun) await prisma.policyEvidence.update({ where: { id: ex.id }, data: { policyCode: e.policyCode, nachweis: e.nachweis, quelle: e.quelle, verantwortlich: e.verantwortlich, turnus: e.turnus } });
} else {
report.evidence.unchanged++;
}
}
report.evidence.obsolete = existingEvidence.filter((e) => !desiredNrs.has(e.nr)).length;
} // Ende reconcileShared (Variablen/Baseline/Nachweise)
// Änderungsprotokoll ins Audit-Log (nur bei echtem Lauf).
if (!dryRun) {
await prisma.auditLog.create({
data: {
tenantId, scope: "tenant", actorId: options.actorId ?? null,
action: "import", entity: "policy_package",
after: { framework, ...report } as unknown as object,
},
});
}
return {
documents: pkg.documents.length,
requirements: pkg.requirements.length,
variables: pkg.variables.length,
baseline: pkg.baseline.length,
report,
};
}
/**
* Rückwärtskompatibler Datei-Import: parst das Datei-Paket und reconciled es in den
* Mandanten. Wird als Fallback genutzt, solange keine veröffentlichte DB-Vorlage vorliegt.
*/
export async function importPolicies(
prisma: PrismaClient,
tenantId: string,
seedDir: string,
options: ImportOptions = {}
) {
// Datei-Quelle für das Framework des Imports (Default TISAX = mapping.json).
const pkg = parsePackageFiles(seedDir, mappingFileFor(options.framework));
return reconcilePackage(prisma, tenantId, pkg, options);
}
-256
View File
@@ -1,256 +0,0 @@
// Import des Standard-Prozess-Katalogs (M2 Strukturanalyse, Ebene 2) in das globale
// Modell ProcessCatalogEntry. GLOBALER Content (kein tenantId), analog RiskCatalogEntry.
// Idempotent (Upsert je `code`). Kuratierte Kern-/Management-/Unterstützungsprozesse
// mit Vorschlägen für Träger-Asset-Typen, Standard-Risiken (→ RiskCatalogEntry.code)
// und Info-Labels — angeboten im prozessgeführten Wizard-Schritt.
import type { AssetType, InfoLabel, PrismaClient, ProcessCategory } from "@prisma/client";
interface CatalogSeed {
code: string;
name: string;
category: ProcessCategory;
/** Katalog-Prozess-Tiefe: Code des übergeordneten Katalog-Prozesses (Teilprozess). */
parentCode?: string;
suggestedAssetTypes: AssetType[];
suggestedRiskCodes: string[];
suggestedInfoLabels: InfoLabel[];
}
// „Neben"-Prozesse → SUPPORT (Vorgabe). Codes: P-<KAT><nr>, KAT = CORE|MGMT|SUP.
const CATALOG: CatalogSeed[] = [
// ── Kernprozesse ──────────────────────────────────────────────────────────
{
code: "P-CORE-01",
name: "Auftragsabwicklung",
category: "CORE",
suggestedAssetTypes: ["INFORMATION", "DATA", "APPLICATION", "SYSTEM", "PERSON"],
suggestedRiskCodes: ["R-OPS-01", "R-ORG-01"],
suggestedInfoLabels: ["INFO_HIGH"],
},
{
code: "P-CORE-02",
name: "Produktentwicklung / Engineering",
category: "CORE",
suggestedAssetTypes: ["INFORMATION", "DATA", "APPLICATION", "PERSON", "SUPPLIER"],
suggestedRiskCodes: ["R-DEV-01", "R-PROTO-01"],
suggestedInfoLabels: ["INFO_VERY_HIGH", "PROTOTYPE"],
},
{
code: "P-CORE-03",
name: "Produktion / Leistungserbringung",
category: "CORE",
suggestedAssetTypes: ["SYSTEM", "APPLICATION", "LOCATION", "PERSON"],
suggestedRiskCodes: ["R-OPS-01", "R-PHY-01"],
suggestedInfoLabels: ["INFO_HIGH"],
},
{
code: "P-CORE-04",
name: "Vertrieb & Kundenbetreuung",
category: "CORE",
suggestedAssetTypes: ["INFORMATION", "DATA", "APPLICATION", "PERSON"],
suggestedRiskCodes: ["R-DSGVO-01", "R-HR-01"],
suggestedInfoLabels: ["PERSONAL_DATA"],
},
// ── Managementprozesse ──────────────────────────────────────────────────────
{
code: "P-MGMT-01",
name: "Unternehmensführung & Strategie",
category: "MANAGEMENT",
suggestedAssetTypes: ["INFORMATION", "PERSON"],
suggestedRiskCodes: ["R-ORG-01"],
suggestedInfoLabels: ["INFO_HIGH"],
},
{
code: "P-MGMT-02",
name: "Informationssicherheits-Management (ISMS)",
category: "MANAGEMENT",
suggestedAssetTypes: ["INFORMATION", "APPLICATION", "PERSON"],
suggestedRiskCodes: ["R-ORG-01", "R-OPS-01"],
suggestedInfoLabels: ["INFO_HIGH"],
},
{
code: "P-MGMT-03",
name: "Risiko- & Compliance-Management",
category: "MANAGEMENT",
suggestedAssetTypes: ["INFORMATION", "PERSON"],
suggestedRiskCodes: ["R-ORG-01", "R-DSGVO-01"],
suggestedInfoLabels: ["INFO_HIGH", "PERSONAL_DATA"],
},
// ── Unterstützende Prozesse (inkl. „Neben"-Prozesse → SUPPORT) ────────────────
{
code: "P-SUP-01",
name: "IT-Betrieb & Infrastruktur",
category: "SUPPORT",
suggestedAssetTypes: ["SYSTEM", "APPLICATION", "IT_SERVICE", "SOFTWARE", "SUPPLIER", "LOCATION"],
suggestedRiskCodes: ["R-OPS-01", "R-NET-01", "R-IAM-01"],
suggestedInfoLabels: ["INFO_HIGH"],
},
// Teilprozesse von P-SUP-01 „IT-Betrieb & Infrastruktur" (parentCode).
{
code: "P-SUP-01-01",
name: "Datensicherung & Wiederherstellung",
category: "SUPPORT",
parentCode: "P-SUP-01",
suggestedAssetTypes: ["SYSTEM", "IT_SERVICE", "DATA", "SOFTWARE"],
suggestedRiskCodes: ["R-OPS-01"],
suggestedInfoLabels: ["INFO_HIGH"],
},
{
code: "P-SUP-01-02",
name: "Server-/Virtualisierungsbetrieb",
category: "SUPPORT",
parentCode: "P-SUP-01",
suggestedAssetTypes: ["SYSTEM", "APPLICATION", "IT_SERVICE"],
suggestedRiskCodes: ["R-OPS-01"],
suggestedInfoLabels: ["INFO_HIGH"],
},
{
code: "P-SUP-01-03",
name: "Netzwerk-/Firewallbetrieb",
category: "SUPPORT",
parentCode: "P-SUP-01",
suggestedAssetTypes: ["SYSTEM", "IT_SERVICE"],
suggestedRiskCodes: ["R-NET-01"],
suggestedInfoLabels: ["INFO_HIGH"],
},
{
code: "P-SUP-01-04",
name: "Patch-/Schwachstellenmanagement",
category: "SUPPORT",
parentCode: "P-SUP-01",
suggestedAssetTypes: ["SYSTEM", "APPLICATION", "SOFTWARE"],
suggestedRiskCodes: ["R-OPS-01"],
suggestedInfoLabels: ["INFO_HIGH"],
},
{
code: "P-SUP-01-05",
name: "Monitoring & Alerting",
category: "SUPPORT",
parentCode: "P-SUP-01",
suggestedAssetTypes: ["SYSTEM", "IT_SERVICE", "APPLICATION"],
suggestedRiskCodes: ["R-OPS-01"],
suggestedInfoLabels: ["INFO_HIGH"],
},
{
code: "P-SUP-01-06",
name: "Identity-/Access-Betrieb (IAM)",
category: "SUPPORT",
parentCode: "P-SUP-01",
suggestedAssetTypes: ["APPLICATION", "IT_SERVICE", "PERSON"],
suggestedRiskCodes: ["R-IAM-01"],
suggestedInfoLabels: ["INFO_HIGH"],
},
{
code: "P-SUP-01-07",
name: "Endpoint-Management",
category: "SUPPORT",
parentCode: "P-SUP-01",
suggestedAssetTypes: ["SYSTEM", "SOFTWARE", "IT_SERVICE"],
suggestedRiskCodes: ["R-OPS-01"],
suggestedInfoLabels: ["INFO_HIGH"],
},
{
code: "P-SUP-01-08",
name: "RZ-/Cloud-Betrieb",
category: "SUPPORT",
parentCode: "P-SUP-01",
suggestedAssetTypes: ["LOCATION", "IT_SERVICE", "SUPPLIER", "SYSTEM"],
suggestedRiskCodes: ["R-OPS-01", "R-PHY-01"],
suggestedInfoLabels: ["INFO_HIGH"],
},
{
code: "P-SUP-02",
name: "Personalwesen (HR)",
category: "SUPPORT",
suggestedAssetTypes: ["INFORMATION", "DATA", "PERSON", "APPLICATION"],
suggestedRiskCodes: ["R-HR-01", "R-DSGVO-01"],
suggestedInfoLabels: ["PERSONAL_DATA", "INFO_HIGH"],
},
// Teilprozesse von P-SUP-02 „Personalwesen (HR)" (parentCode).
{
code: "P-SUP-02-01",
name: "Recruiting & Onboarding",
category: "SUPPORT",
parentCode: "P-SUP-02",
suggestedAssetTypes: ["INFORMATION", "DATA", "PERSON", "APPLICATION"],
suggestedRiskCodes: ["R-HR-01", "R-DSGVO-01"],
suggestedInfoLabels: ["PERSONAL_DATA"],
},
{
code: "P-SUP-02-02",
name: "Offboarding",
category: "SUPPORT",
parentCode: "P-SUP-02",
suggestedAssetTypes: ["INFORMATION", "PERSON", "APPLICATION"],
suggestedRiskCodes: ["R-HR-01", "R-IAM-01"],
suggestedInfoLabels: ["PERSONAL_DATA"],
},
{
code: "P-SUP-02-03",
name: "Awareness & Schulung",
category: "SUPPORT",
parentCode: "P-SUP-02",
suggestedAssetTypes: ["INFORMATION", "PERSON"],
suggestedRiskCodes: ["R-HR-01"],
suggestedInfoLabels: ["INFO_HIGH"],
},
{
code: "P-SUP-03",
name: "Einkauf & Lieferantensteuerung",
category: "SUPPORT",
suggestedAssetTypes: ["INFORMATION", "SUPPLIER", "IT_SERVICE"],
suggestedRiskCodes: ["R-SUP-01", "R-ORG-01"],
suggestedInfoLabels: ["INFO_HIGH"],
},
{
code: "P-SUP-04",
name: "Finanzen & Buchhaltung",
category: "SUPPORT",
suggestedAssetTypes: ["INFORMATION", "DATA", "APPLICATION", "PERSON"],
suggestedRiskCodes: ["R-DSGVO-01", "R-ORG-01"],
suggestedInfoLabels: ["INFO_HIGH", "PERSONAL_DATA"],
},
{
code: "P-SUP-05",
name: "Facility Management & physische Sicherheit",
category: "SUPPORT",
suggestedAssetTypes: ["LOCATION", "SYSTEM", "PERSON"],
suggestedRiskCodes: ["R-PHY-01"],
suggestedInfoLabels: ["NONE"],
},
// Teilprozesse von P-SUP-05 „Facility Management & physische Sicherheit" (parentCode).
{
code: "P-SUP-05-01",
name: "Zutritts-/Zonenkontrolle",
category: "SUPPORT",
parentCode: "P-SUP-05",
suggestedAssetTypes: ["LOCATION", "SYSTEM", "PERSON"],
suggestedRiskCodes: ["R-PHY-01"],
suggestedInfoLabels: ["NONE"],
},
{
code: "P-SUP-05-02",
name: "Gebäude-/Versorgungstechnik",
category: "SUPPORT",
parentCode: "P-SUP-05",
suggestedAssetTypes: ["LOCATION", "SYSTEM"],
suggestedRiskCodes: ["R-PHY-01"],
suggestedInfoLabels: ["NONE"],
},
];
/** Schreibt den Katalog (Upsert je code). Liefert die Anzahl. */
export async function importProcessCatalog(prisma: PrismaClient): Promise<number> {
let orderIdx = 0;
for (const c of CATALOG) {
// parentCode explizit setzen (null bei Hauptprozessen) → idempotente Updates.
const payload = { ...c, parentCode: c.parentCode ?? null, orderIdx: orderIdx++ };
await prisma.processCatalogEntry.upsert({
where: { code: c.code },
update: payload,
create: payload,
});
}
return CATALOG.length;
}
-63
View File
@@ -1,63 +0,0 @@
// Import des Standard-Risikokatalogs (Story A6-1) aus dem Fachcontent C4 in das
// globale Modell RiskCatalogEntry. Tabellenzeile:
//
// | R-ORG-01 | Titel | Beschreibung | 1.1.1, 1.2.1 | Richtlinien, Organisation | Standardmaßnahme | E3/S4 — Begründung |
//
// Idempotent (Upsert über code). Kein tenantId (globaler Katalog).
import { readFileSync } from "node:fs";
import { join } from "node:path";
import type { PrismaClient } from "@prisma/client";
const C4_PATH = join(__dirname, "..", "docs", "wizard-uebergabe", "02_Fachcontent_C1-C9", "C4_Risikokatalog.md");
export interface ParsedRisk {
code: string; category: string; title: string; description: string;
controls: string[]; assetTypes: string[]; standardMeasure: string;
defaultLikelihood: number; defaultImpact: number; rationale: string; orderIdx: number;
}
const splitList = (s: string): string[] => s.split(",").map((x) => x.trim()).filter(Boolean);
/** Parst die C4-Tabellen zu Katalog-Objekten (ohne DB-Zugriff — testbar). */
export function parseRiskCatalog(text: string): ParsedRisk[] {
const out: ParsedRisk[] = [];
let i = 0;
for (const line of text.split("\n")) {
const m = line.match(/^\|\s*(R-[A-Z]+-\d+)\s*\|(.*)\|\s*$/);
if (!m) continue;
const cols = m[2].split("|").map((c) => c.trim());
if (cols.length < 6) continue; // title|desc|controls|assetTypes|measure|ES-rationale
const code = m[1];
const es = cols[5].match(/E\s*(\d)\s*\/\s*S\s*(\d)/i);
const [, esL, esI] = es ?? [];
out.push({
code,
category: code.split("-")[1],
title: cols[0],
description: cols[1],
controls: splitList(cols[2]),
assetTypes: splitList(cols[3]),
standardMeasure: cols[4],
defaultLikelihood: esL ? Number(esL) : 3,
defaultImpact: esI ? Number(esI) : 3,
rationale: cols[5].replace(/^E\s*\d\s*\/\s*S\s*\d\s*[—-]\s*/i, "").trim(),
orderIdx: i++,
});
}
return out;
}
/** Liest C4 und schreibt den Katalog (Upsert je code). Liefert die Anzahl. */
export async function importRiskCatalog(prisma: PrismaClient): Promise<number> {
const text = readFileSync(C4_PATH, "utf-8");
const risks = parseRiskCatalog(text);
for (const r of risks) {
await prisma.riskCatalogEntry.upsert({
where: { code: r.code },
update: { category: r.category, title: r.title, description: r.description, controls: r.controls, assetTypes: r.assetTypes, standardMeasure: r.standardMeasure, defaultLikelihood: r.defaultLikelihood, defaultImpact: r.defaultImpact, rationale: r.rationale, orderIdx: r.orderIdx },
create: r,
});
}
return risks.length;
}
+54 -2152
View File
File diff suppressed because it is too large Load Diff
-60
View File
@@ -1,60 +0,0 @@
import "dotenv/config";
import { PrismaClient } from "@prisma/client";
import { PrismaPg } from "@prisma/adapter-pg";
import { pathToFileURL } from "node:url";
import { CONTROL_DOMAIN_DEFAULTS } from "../src/lib/control-domain";
import { importProcessCatalog } from "./import-process-catalog";
/**
* Prod-tauglicher Content-Seed: die GLOBALEN, mandantenunabhängigen Inhalte der
* TISAX-Neustruktur — Control→Domain/RACI-Defaults (M3) und Prozess-Katalog (M2).
* Enthält KEINE Demo-/Mandantendaten und ist idempotent (upsert bzw.
* global-neu-setzen), also gefahrlos in jeder Umgebung mehrfach ausführbar.
*
* Wird vom Demo-Seed (`seed.ts`) wiederverwendet und ist eigenständig ausführbar:
* npm run seed:content
*/
export async function seedGlobalContent(
prisma: PrismaClient
): Promise<{ controlDomains: number; processCatalog: number }> {
// Control→Domain/RACI-Defaults (global, tenantId = null). Idempotent: die globalen
// Zeilen werden neu gesetzt; mandantenspezifische Overrides (tenantId != null) bleiben.
await prisma.controlDomainMap.deleteMany({ where: { tenantId: null } });
await prisma.controlDomainMap.createMany({
data: CONTROL_DOMAIN_DEFAULTS.map((r) => ({
tenantId: null,
control: r.control,
domain: r.domain,
raci: r.raci,
functionKey: r.functionKey ?? null,
})),
});
// Prozess-Katalog (global, upsert je code — idempotent).
const processCatalog = await importProcessCatalog(prisma);
return { controlDomains: CONTROL_DOMAIN_DEFAULTS.length, processCatalog };
}
async function main() {
const prisma = new PrismaClient({
adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }),
});
try {
console.log("→ Globaler Content-Seed (Control→Domain-Defaults + Prozess-Katalog)…");
const { controlDomains, processCatalog } = await seedGlobalContent(prisma);
console.log(`✔ ${controlDomains} Control→Domain-Defaults`);
console.log(`✔ ${processCatalog} Prozess-Katalog-Einträge`);
console.log("✓ Globaler Content-Seed abgeschlossen.");
} finally {
await prisma.$disconnect();
}
}
// Nur als eigenständiger Runner ausführen, nicht beim Import aus seed.ts.
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
main().catch((e) => {
console.error(e);
process.exit(1);
});
}
+60 -695
View File
@@ -2,148 +2,36 @@ import "dotenv/config";
import { PrismaClient } from "@prisma/client"; import { PrismaClient } from "@prisma/client";
import { PrismaPg } from "@prisma/adapter-pg"; import { PrismaPg } from "@prisma/adapter-pg";
import { hashPassword } from "../src/server/password"; import { hashPassword } from "../src/server/password";
import { join } from "node:path"; import { PERMISSIONS, type RoleKey } from "../src/server/rbac";
import { PERMISSIONS, ROLE_DEFS } from "../src/server/rbac";
import { importPolicies, stampPackageState } from "./import-policies";
import { importImplementationHints } from "./import-hints";
import { importRiskCatalog } from "./import-risks";
import { seedGlobalContent } from "./seed-content";
import { importManaged } from "./import-managed";
import { MODULE_KEYS } from "../src/lib/modules";
import { normalizeAssetName } from "../src/lib/normalize-asset";
import { provisionTenant } from "../src/server/provision"; import { provisionTenant } from "../src/server/provision";
/** /**
* Seed: global permission catalog + demo tenant with roles and users. * Craftvia-Demo-Seed (Fundament): Plattform-Admin + Mandant `demo` mit je einem Nutzer
* Idempotent (upserts) — safe to run repeatedly. * pro Standardrolle + zweiter Mandant `demo2` (Isolations-/Tenant-Switch-Tests) +
* Multi-Membership-Fixture. KEINE Fachdaten (Kunden/Aufträge liefern die Module).
*
* Idempotent (upserts) — beliebig oft ausführbar. Passwort: SEED_PASSWORD oder Default.
*/ */
const prisma = new PrismaClient({ const prisma = new PrismaClient({
adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }), adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }),
}); });
const DEMO_PASSWORD = "Demo1234!"; const DEMO_PASSWORD = process.env.SEED_PASSWORD || "Demo1234!";
const THREATS = [ async function ensureMember(tenantId: string, u: { email: string; name: string; roles: RoleKey[] }, passwordHash: string) {
"Schadsoftware / Ransomware", // Globale Identity (Anmeldung) + Mitgliedschaft (User) im Mandanten.
"Phishing / Social Engineering", const identity = await prisma.identity.upsert({
"Innentäter / Missbrauch von Berechtigungen",
"Diebstahl oder Verlust von Geräten",
"Ausfall von IT-Systemen oder Diensten",
"Ausfall eines Dienstleisters / Lieferanten",
"Stromausfall / Infrastrukturausfall",
"Feuer / Wasser / Elementarschäden",
"Unbefugter physischer Zutritt",
"Denial-of-Service-Angriff",
"Datenabfluss / Industriespionage",
"Fehlbedienung durch Mitarbeitende",
"Softwarefehler / fehlerhafte Updates",
"Kompromittierte Zugangsdaten",
"Rechtliche / regulatorische Verstöße",
];
const VULNERABILITIES = [
"Fehlende oder veraltete Backups",
"Fehlende Netzwerksegmentierung",
"Unzureichendes Patch-Management",
"Schwache oder wiederverwendete Passwörter",
"Fehlende Multi-Faktor-Authentifizierung",
"Übermäßige Berechtigungen / fehlendes Least-Privilege",
"Fehlende Awareness / Schulungen",
"Unverschlüsselte Datenträger oder Übertragungen",
"Kein Notfallkonzept / ungetesteter Wiederanlauf",
"Single Point of Failure (Technik oder Person)",
"Unzureichende Protokollierung / Überwachung",
"Veraltete oder nicht mehr unterstützte Software",
"Fehlende Vertrags-/AV-Regelungen mit Dienstleistern",
"Offene, ungenutzte Dienste und Ports",
"Unklare Verantwortlichkeiten",
];
async function main() {
// 0. Globale Kataloge Bedrohungen/Schwachstellen (Vorschläge, Freitext bleibt möglich)
for (const name of THREATS) {
await prisma.threat.upsert({ where: { name }, update: {}, create: { name } });
}
for (const name of VULNERABILITIES) {
await prisma.vulnerability.upsert({ where: { name }, update: {}, create: { name } });
}
console.log(`✔ Kataloge: ${THREATS.length} Bedrohungen, ${VULNERABILITIES.length} Schwachstellen`);
// 1. Global permission catalog
for (const key of PERMISSIONS) {
await prisma.permission.upsert({ where: { key }, update: {}, create: { key } });
}
console.log(`✔ ${PERMISSIONS.length} Permissions`);
// 1b. Globaler Content (M2/M3): Control→Domain/RACI-Defaults + Prozess-Katalog.
// Ausgelagert in den prod-tauglichen, idempotenten Content-Seed (seed-content.ts),
// damit DevOps ihn auch ohne Demo-Seed in jeder Umgebung fahren kann (npm run seed:content).
const globalContent = await seedGlobalContent(prisma);
console.log(`✔ ${globalContent.controlDomains} Control→Domain-Defaults (M3)`);
// 2. Demo tenant
const tenant = await prisma.tenant.upsert({
where: { slug: "demo" },
update: {},
create: { name: "Demo GmbH", slug: "demo" },
});
// 3. Roles from blueprints, with permission bundles
for (const [key, def] of Object.entries(ROLE_DEFS)) {
const role = await prisma.role.upsert({
where: { tenantId_key: { tenantId: tenant.id, key } },
update: { name: def.name },
create: { tenantId: tenant.id, key, name: def.name },
});
const perms = await prisma.permission.findMany({
where: { key: { in: [...def.permissions] } },
});
for (const p of perms) {
await prisma.rolePermission.upsert({
where: { roleId_permissionId: { roleId: role.id, permissionId: p.id } },
update: {},
create: { roleId: role.id, permissionId: p.id },
});
}
}
console.log(`✔ Mandant "demo" mit ${Object.keys(ROLE_DEFS).length} Rollen`);
// 4. Demo users
const passwordHash = await hashPassword(DEMO_PASSWORD);
const demoUsers: { email: string; name: string; roles: string[] }[] = [
{ email: "admin@demo.example", name: "Anna Admin", roles: ["tenant-admin", "isb"] },
// Zweiter ISB als Freigeber — ermöglicht den Vier-Augen-Freigabe-Workflow (≠ Einreicher).
{ email: "bea.approver@demo.example", name: "Bea Approver", roles: ["isb"] },
{ email: "auditor@demo.example", name: "Axel Auditor", roles: ["auditor"] },
{ email: "owner@demo.example", name: "Oskar Owner", roles: ["owner"] },
{ email: "user@demo.example", name: "Ulla User", roles: ["user"] },
];
for (const u of demoUsers) {
// Option C: globale Identity (Anmeldung) + Mitgliedschaft (User) im Mandanten.
// Diese 5 Standard-Logins bleiben bewusst SINGLE-Membership, damit der
// (bis WS1) einstufige Login-Lookup (auth.ts: findMany by email) eindeutig
// bleibt und dev lauffähig ist. Der Multi-Membership-Fall wird über einen
// separaten Fixture-Nutzer (multi@demo.example) abgebildet (siehe unten).
const idn = await prisma.identity.upsert({
where: { email: u.email }, where: { email: u.email },
update: {}, update: {},
create: { email: u.email, passwordHash }, create: { email: u.email, passwordHash },
}); });
const user = await prisma.user.upsert({ const user = await prisma.user.upsert({
where: { tenantId_email: { tenantId: tenant.id, email: u.email } }, where: { tenantId_email: { tenantId, email: u.email } },
update: { identityId: idn.id }, update: { identityId: identity.id, name: u.name },
create: { create: { tenantId, identityId: identity.id, email: u.email, name: u.name },
tenantId: tenant.id,
identityId: idn.id,
email: u.email,
name: u.name,
},
});
const roles = await prisma.role.findMany({
where: { tenantId: tenant.id, key: { in: u.roles } },
}); });
const roles = await prisma.role.findMany({ where: { tenantId, key: { in: u.roles } } });
for (const r of roles) { for (const r of roles) {
await prisma.userRole.upsert({ await prisma.userRole.upsert({
where: { userId_roleId: { userId: user.id, roleId: r.id } }, where: { userId_roleId: { userId: user.id, roleId: r.id } },
@@ -151,583 +39,60 @@ async function main() {
create: { userId: user.id, roleId: r.id }, create: { userId: user.id, roleId: r.id },
}); });
} }
} return user;
console.log(`✔ ${demoUsers.length} Demo-Nutzer (Passwort: ${DEMO_PASSWORD})`); }
// 4b. Zweiter Mandant + Multi-Membership-Fixture (Option C, WS0/WS2/WS7). async function main() {
// Eine Identity (multi@demo.example) ist Mitglied in ZWEI Mandanten mit je const passwordHash = await hashPassword(DEMO_PASSWORD);
// unterschiedlichen Rollen — Grundlage für Tenant-Switch- und Isolationstests.
// `demo2` wird schlank provisioniert (Rollen/Module/Einstellungen, ohne // 1. Mandant „demo" (provisioniert Permissions, Rollen, Einstellungen, Module, Admin)
// Richtlinienpaket). Berührt die 5 Standard-Logins nicht. const demo = await provisionTenant(prisma, {
const tenant2 = await provisionTenant(prisma, { name: "Musterbau Haustechnik GmbH",
name: "Demo Zwei GmbH", slug: "demo",
short: "Musterbau",
sector: "Sanitär, Heizung, Klima",
admin: { email: "admin@demo.example", name: "Anna Admin", password: DEMO_PASSWORD },
});
await prisma.tenantSettings.update({
where: { tenantId: demo.id },
data: {
address: "Hafenstraße 12, 20457 Hamburg",
phone: "+49 40 123456-0",
email: "info@musterbau.example",
},
});
const demoUsers: { email: string; name: string; roles: RoleKey[] }[] = [
{ email: "admin@demo.example", name: "Anna Admin", roles: ["tenant-admin"] },
{ email: "backoffice@demo.example", name: "Bernd Backoffice", roles: ["backoffice"] },
{ email: "teamleiter@demo.example", name: "Tina Teamleiter", roles: ["team-lead"] },
{ email: "monteur@demo.example", name: "Max Monteur", roles: ["technician"] },
];
for (const u of demoUsers) await ensureMember(demo.id, u, passwordHash);
console.log(`✔ Mandant "demo" (${PERMISSIONS.length} Permissions) mit ${demoUsers.length} Nutzern (Passwort: ${DEMO_PASSWORD})`);
// 2. Zweiter Mandant „demo2" — Grundlage für Isolations- und Tenant-Switch-Tests.
const demo2 = await provisionTenant(prisma, {
name: "Nordlicht Elektro GmbH",
slug: "demo2", slug: "demo2",
short: "Demo2", short: "Nordlicht",
sector: "Automotive", sector: "Elektro",
admin: { email: "admin2@demo.example", name: "Zoe Zweitadmin", password: DEMO_PASSWORD }, admin: { email: "admin2@demo.example", name: "Zoe Zweitadmin", password: DEMO_PASSWORD },
}); });
const multiIdentity = await prisma.identity.upsert({ console.log(`✔ Mandant "demo2" mit Admin admin2@demo.example`);
where: { email: "multi@demo.example" },
update: {},
create: { email: "multi@demo.example", passwordHash },
});
// Mitgliedschaft je Mandant mit je eigener Rolle (Rechte je aktivem Mandant verschieden).
const multiMemberships: { tenantId: string; roleKey: string }[] = [
{ tenantId: tenant.id, roleKey: "user" }, // in "demo": nur Standardnutzer
{ tenantId: tenant2.id, roleKey: "tenant-admin" }, // in "demo2": Mandanten-Admin
];
for (const m of multiMemberships) {
const membership = await prisma.user.upsert({
where: { tenantId_email: { tenantId: m.tenantId, email: "multi@demo.example" } },
update: { identityId: multiIdentity.id },
create: {
tenantId: m.tenantId,
identityId: multiIdentity.id,
email: "multi@demo.example",
name: "Mika Multi",
},
});
const role = await prisma.role.findFirst({ where: { tenantId: m.tenantId, key: m.roleKey } });
if (role) {
await prisma.userRole.upsert({
where: { userId_roleId: { userId: membership.id, roleId: role.id } },
update: {},
create: { userId: membership.id, roleId: role.id },
});
}
}
console.log(`✔ Multi-Membership-Fixture multi@demo.example (Mandanten: demo, demo2)`);
// 5. Beispiel-Assets, -Prozesse und BIA (nur wenn noch keine Assets existieren) // 3. Multi-Membership-Fixture: eine Identity in ZWEI Mandanten mit je anderer Rolle.
const assetCount = await prisma.asset.count({ where: { tenantId: tenant.id } }); const multi = { email: "multi@demo.example", name: "Mika Multi" };
if (assetCount === 0) { await ensureMember(demo.id, { ...multi, roles: ["technician"] }, passwordHash);
const isb = await prisma.user.findUniqueOrThrow({ await ensureMember(demo2.id, { ...multi, roles: ["tenant-admin"] }, passwordHash);
where: { tenantId_email: { tenantId: tenant.id, email: "admin@demo.example" } }, console.log(`✔ Multi-Membership-Fixture multi@demo.example (demo: Monteur, demo2: Mandantenadministrator)`);
});
const owner = await prisma.user.findUniqueOrThrow({
where: { tenantId_email: { tenantId: tenant.id, email: "owner@demo.example" } },
});
const mk = (data: { // 4. Plattform-Admin (getrennter Store, Login unter /platform/login).
name: string;
type: "INFORMATION" | "SYSTEM" | "APPLICATION" | "LOCATION" | "SUPPLIER" | "PERSON" | "DATA";
c: number;
i: number;
a: number;
ownerId?: string;
location?: string;
tags?: string[];
description?: string;
}) =>
prisma.asset.create({
data: {
tenantId: tenant.id,
name: data.name,
type: data.type,
// M2: Dedup-Schlüssel setzen (nur INFORMATION/DATA sind primäre Werte;
// für die übrigen Typen dient er nur der Exakt-Duplikat-Vermeidung).
normalizedName: normalizeAssetName(data.name),
confidentiality: data.c,
integrity: data.i,
availability: data.a,
ownerId: data.ownerId,
location: data.location,
tags: data.tags ?? [],
description: data.description,
},
});
const erp = await mk({ name: "ERP-System", type: "SYSTEM", c: 3, i: 4, a: 3, ownerId: owner.id, location: "RZ Frankfurt", tags: ["kritisch", "kern"] });
const crm = await mk({ name: "Kundendatenbank", type: "DATA", c: 4, i: 3, a: 2, ownerId: isb.id, tags: ["dsgvo"] });
const auftraege = await mk({ name: "Auftragsdaten", type: "INFORMATION", c: 3, i: 4, a: 3, ownerId: owner.id });
const hoster = await mk({ name: "Cloud-Hoster (IaaS)", type: "SUPPLIER", c: 2, i: 3, a: 4, description: "Betreibt das Rechenzentrum für ERP und CRM." });
const buero = await mk({ name: "Bürostandort München", type: "LOCATION", c: 2, i: 2, a: 2 });
const itTeam = await mk({ name: "IT-Administration", type: "PERSON", c: 3, i: 3, a: 3 });
await prisma.assetRelation.createMany({
data: [
{ tenantId: tenant.id, assetId: erp.id, relatedAssetId: hoster.id, type: "depends_on" },
{ tenantId: tenant.id, assetId: crm.id, relatedAssetId: hoster.id, type: "depends_on" },
{ tenantId: tenant.id, assetId: erp.id, relatedAssetId: itTeam.id, type: "depends_on" },
],
});
const auftrag = await prisma.process.create({
data: {
tenantId: tenant.id,
name: "Auftragsabwicklung",
description: "Vom Kundenauftrag bis zur Auslieferung.",
category: "CORE",
ownerId: owner.id,
},
});
await prisma.processAsset.createMany({
data: [
{ tenantId: tenant.id, processId: auftrag.id, assetId: auftraege.id, role: "PRIMARY" },
{ tenantId: tenant.id, processId: auftrag.id, assetId: erp.id, role: "SECONDARY" },
{ tenantId: tenant.id, processId: auftrag.id, assetId: crm.id, role: "SECONDARY" },
{ tenantId: tenant.id, processId: auftrag.id, assetId: itTeam.id, role: "SECONDARY" },
],
});
await prisma.biaEntry.create({
data: {
tenantId: tenant.id,
processId: auftrag.id,
rtoHours: 8,
rpoHours: 4,
mtdHours: 24,
impactC: 2,
impactI: 4,
impactA: 3,
criticality: 4,
notes: "Ausfall > 1 Tag führt zu Lieferverzug und Vertragsstrafen.",
},
});
const vertrieb = await prisma.process.create({
data: {
tenantId: tenant.id,
name: "Kundenbetreuung",
description: "Anfragen, Angebote, Support.",
category: "SUPPORT",
ownerId: isb.id,
},
});
await prisma.processAsset.createMany({
data: [
{ tenantId: tenant.id, processId: vertrieb.id, assetId: crm.id, role: "PRIMARY" },
{ tenantId: tenant.id, processId: vertrieb.id, assetId: buero.id, role: "SECONDARY" },
],
});
await prisma.biaEntry.create({
data: {
tenantId: tenant.id,
processId: vertrieb.id,
rtoHours: 24,
rpoHours: 24,
mtdHours: 72,
impactC: 3,
impactI: 2,
impactA: 2,
criticality: 3,
notes: "Kundenkommunikation kann kurzfristig über Ausweichkanäle laufen.",
},
});
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`);
}
// 7. Beispiel-Maßnahmen (nur wenn noch keine existieren) — bestimmen das Rest-Risiko
const measureCount = await prisma.measure.count({ where: { tenantId: tenant.id } });
if (measureCount === 0) {
const owner = await prisma.user.findUniqueOrThrow({
where: { tenantId_email: { tenantId: tenant.id, email: "owner@demo.example" } },
});
const riskByTitle = (title: string) =>
prisma.risk.findFirst({ where: { tenantId: tenant.id, title } });
const measures: {
title: string;
status: "OPEN" | "IN_PROGRESS" | "DONE";
priority: "LOW" | "MEDIUM" | "HIGH";
dueInDays?: number;
riskTitle?: string;
reduction?: [number, number]; // [Wahrscheinlichkeit, Schaden]
}[] = [
{
title: "Offline-Backups einführen (3-2-1-Regel)",
status: "IN_PROGRESS",
priority: "HIGH",
dueInDays: 14,
riskTitle: "Ransomware auf ERP",
reduction: [1, 1],
},
{
title: "Makro-Ausführung per GPO einschränken",
status: "OPEN",
priority: "HIGH",
dueInDays: 30,
riskTitle: "Ransomware auf ERP",
reduction: [1, 0],
},
{
title: "Awareness-Schulung Phishing (alle Mitarbeitenden)",
status: "IN_PROGRESS",
priority: "MEDIUM",
dueInDays: 45,
riskTitle: "Phishing / Social Engineering",
reduction: [1, 0],
},
{
title: "Notfallhandbuch aktualisieren",
status: "DONE",
priority: "MEDIUM",
},
];
let mRef = 1;
for (const m of measures) {
const measure = await prisma.measure.create({
data: {
tenantId: tenant.id,
refNo: mRef++,
title: m.title,
status: m.status,
priority: m.priority,
ownerId: owner.id,
dueDate: m.dueInDays
? new Date(Date.now() + m.dueInDays * 24 * 3600 * 1000)
: undefined,
},
});
if (m.riskTitle && m.reduction) {
const risk = await riskByTitle(m.riskTitle);
if (risk) {
await prisma.riskMeasure.create({
data: {
tenantId: tenant.id,
riskId: risk.id,
measureId: measure.id,
reductionLikelihood: m.reduction[0],
reductionImpact: m.reduction[1],
},
});
}
}
}
// Rest-Risiken aus den Maßnahmen ableiten (manuelle Alt-Werte ersetzen)
const allRisks = await prisma.risk.findMany({
where: { tenantId: tenant.id },
include: { riskMeasures: true },
});
for (const r of allRisks) {
if (r.riskMeasures.length === 0) {
await prisma.risk.update({
where: { id: r.id },
data: { residualLikelihood: null, residualImpact: null, residualScore: null },
});
} else {
const redL = r.riskMeasures.reduce((s, x) => s + x.reductionLikelihood, 0);
const redI = r.riskMeasures.reduce((s, x) => s + x.reductionImpact, 0);
const rl = Math.max(1, r.likelihood - redL);
const ri = Math.max(1, r.impact - redI);
await prisma.risk.update({
where: { id: r.id },
data: { residualLikelihood: rl, residualImpact: ri, residualScore: rl * ri },
});
}
}
console.log(`✔ ${measures.length} Beispiel-Maßnahmen angelegt, Rest-Risiken berechnet`);
}
// 8. Demo-Lieferanten als Assets (type SUPPLIER) mit Profil, Nachweisen, Verträgen
const supCount = await prisma.supplierProfile.count({ where: { tenantId: tenant.id } });
if (supCount === 0) {
const hoster = await prisma.asset.findFirst({ where: { tenantId: tenant.id, name: "Cloud-Hoster (IaaS)" } });
const a1 = await prisma.asset.create({
data: { tenantId: tenant.id, name: "Cloud-Hoster GmbH", type: "SUPPLIER", confidentiality: 3, integrity: 3, availability: 4 },
});
await prisma.supplierProfile.create({
data: { tenantId: tenant.id, assetId: a1.id, refNo: 1, sector: "IT-Dienstleistung / IaaS", serviceDesc: "Rechenzentrum, Virtualisierung, Backup", criticality: 4, dataCategories: ["Kundendaten", "Auftragsdaten"], nis2Relevant: true, lifecycle: "ACTIVE", contact: "security@cloud-hoster.example", nextReview: new Date(Date.now() + 90 * 24 * 3600 * 1000) },
});
if (hoster) await prisma.assetRelation.create({ data: { tenantId: tenant.id, assetId: a1.id, relatedAssetId: hoster.id, type: "provides" } }).catch(() => {});
await prisma.supplierEvidence.create({ data: { tenantId: tenant.id, assetId: a1.id, kind: "TISAX_LABEL", name: "TISAX AL3 (info high)", protectsCia: "C,I,A", validTo: new Date(Date.now() + 200 * 24 * 3600 * 1000), adequacyChecked: true } });
await prisma.contract.create({ data: { tenantId: tenant.id, assetId: a1.id, type: "av_dpa", avDpa: true, securityClauses: true, flowdown: true, customerRequirementsPassed: true, validTo: new Date(Date.now() + 400 * 24 * 3600 * 1000), reference: "AV-2025-014" } });
await prisma.nda.create({ data: { tenantId: tenant.id, assetId: a1.id, subject: "Betrieb der ERP-/CRM-Infrastruktur", parties: "Demo GmbH / Cloud-Hoster GmbH", validTo: new Date(Date.now() + 60 * 24 * 3600 * 1000), extensionStatus: "offen" } });
await prisma.supplierAssessment.create({ data: { tenantId: tenant.id, assetId: a1.id, type: "SELF_ASSESSMENT", score: 82, date: new Date(Date.now() - 120 * 24 * 3600 * 1000), nextReview: new Date(Date.now() + 245 * 24 * 3600 * 1000), result: "angemessen" } });
const a2 = await prisma.asset.create({
data: { tenantId: tenant.id, name: "WebAgentur X", type: "SUPPLIER", confidentiality: 2, integrity: 2, availability: 2 },
});
await prisma.supplierProfile.create({
data: { tenantId: tenant.id, assetId: a2.id, refNo: 2, sector: "Webentwicklung", serviceDesc: "Website-Betrieb", criticality: 2, dataCategories: ["Marketingdaten"], nis2Relevant: false, lifecycle: "UNDER_REVIEW" },
});
console.log("✔ 2 Demo-Lieferanten als Assets angelegt");
}
// 9. Demo-IT-Service (type IT_SERVICE) mit RACI-Matrix über den ISA-Katalog
const svcCount = await prisma.iTServiceProfile.count({ where: { tenantId: tenant.id } });
if (svcCount === 0) {
const provider = await prisma.asset.findFirst({ where: { tenantId: tenant.id, name: "Cloud-Hoster GmbH", type: "SUPPLIER" } });
const svc = await prisma.asset.create({
data: { tenantId: tenant.id, name: "Managed ERP-Hosting", type: "IT_SERVICE", confidentiality: 3, integrity: 3, availability: 4 },
});
await prisma.iTServiceProfile.create({
data: { tenantId: tenant.id, assetId: svc.id, refNo: 1, criticality: 4, internal: false, providerAssetId: provider?.id ?? null, notes: "Betrieb & Wartung der ERP-Plattform beim Cloud-Hoster (Shared Responsibility)." },
});
const raci: { controlRef: string; title: string; responsibility: "PROVIDER" | "US" | "SHARED"; evidenceRef?: string }[] = [
{ controlRef: "4.1.2", title: "Authentisierung / MFA", responsibility: "SHARED", evidenceRef: "IAM-Konzept v2" },
{ controlRef: "5.2.4", title: "Schwachstellen-/Patch-Management", responsibility: "PROVIDER", evidenceRef: "Patch-SLA" },
{ controlRef: "5.2.5", title: "Datensicherung / Backup", responsibility: "PROVIDER", evidenceRef: "Backup-Report Q2" },
{ controlRef: "4.1.3", title: "Zugriffsrechte-Verwaltung", responsibility: "US" },
{ controlRef: "1.6.1", title: "Informationssicherheits-Vorfälle", responsibility: "SHARED", evidenceRef: "IR-Runbook" },
];
for (const r of raci) {
await prisma.serviceControlResponsibility.create({
data: { tenantId: tenant.id, assetId: svc.id, controlRef: r.controlRef, title: r.title, applicable: true, responsibility: r.responsibility, evidenceRef: r.evidenceRef ?? null },
});
}
console.log("✔ 1 Demo-IT-Service mit RACI-Matrix angelegt");
}
// 9b. Demo-Vorfälle (Modul „Vorfälle", IM-A) — 1–2 Beispiele im Demo-Mandanten
const incidentCount = await prisma.incident.count({ where: { tenantId: tenant.id } });
if (incidentCount === 0) {
const isb = await prisma.user.findUnique({
where: { tenantId_email: { tenantId: tenant.id, email: "admin@demo.example" } },
});
const owner = await prisma.user.findUnique({
where: { tenantId_email: { tenantId: tenant.id, email: "owner@demo.example" } },
});
const crm = await prisma.asset.findFirst({ where: { tenantId: tenant.id, name: "Kundendatenbank" } });
const erp = await prisma.asset.findFirst({ where: { tenantId: tenant.id, name: "ERP-System" } });
const vertrieb = await prisma.process.findFirst({ where: { tenantId: tenant.id, name: "Kundenbetreuung" } });
const year = new Date().getFullYear();
// Vorfall 1: Phishing (mittlerer Schweregrad), in Bearbeitung, mit betroffenem Asset/Prozess.
const inc1 = await prisma.incident.create({
data: {
tenantId: tenant.id,
refNo: `INC-${year}-0001`,
title: "Phishing-Welle gegen Vertriebsmitarbeitende",
description: "Mehrere Mitarbeitende meldeten gefälschte E-Mails mit Login-Aufforderung. Ein Konto könnte kompromittiert sein.",
source: "manual",
reporterName: "Helpdesk",
reporterContact: "helpdesk@demo.example",
detectedAt: new Date(Date.now() - 2 * 24 * 3600 * 1000),
occurredAt: new Date(Date.now() - 3 * 24 * 3600 * 1000),
reportedAt: new Date(Date.now() - 2 * 24 * 3600 * 1000),
category: "phishing",
impactC: 3,
impactI: 1,
impactA: 0,
urgency: 3,
severity: "hoch",
priority: "hoch",
personalData: true,
dsgvoRelevant: true,
nis2Relevant: false,
status: "in_bearbeitung",
ownerId: isb?.id ?? null,
assigneeId: owner?.id ?? null,
createdBy: isb?.id ?? null,
},
});
if (crm) await prisma.incidentAsset.create({ data: { tenantId: tenant.id, incidentId: inc1.id, assetId: crm.id } });
if (vertrieb) await prisma.incidentProcess.create({ data: { tenantId: tenant.id, incidentId: inc1.id, processId: vertrieb.id } });
await prisma.incidentComment.create({
data: { tenantId: tenant.id, incidentId: inc1.id, authorId: isb?.id ?? null, body: "Betroffene Konten gesperrt, Passwort-Reset veranlasst.", internal: false },
});
// Vorfall 2: Kurzer ERP-Ausfall, bereits abgeschlossen (mit Ursache/Lösung/Lessons Learned).
const inc2 = await prisma.incident.create({
data: {
tenantId: tenant.id,
refNo: `INC-${year}-0002`,
title: "Kurzzeitiger Ausfall des ERP-Systems",
description: "Das ERP-System war rund 40 Minuten nicht erreichbar (Speicherengpass auf dem Applikationsserver).",
source: "manual",
reporterName: "IT-Betrieb",
category: "outage",
impactC: 0,
impactI: 0,
impactA: 3,
urgency: 2,
severity: "mittel",
priority: "mittel",
status: "abgeschlossen",
rootCause: "Fehlende Überwachung des Speicherverbrauchs führte zu einem OOM-Neustart.",
resolution: "Dienst neu gestartet, Speicherlimits angehoben.",
closingNote: "Betrieb nach 40 Minuten wiederhergestellt, kein Datenverlust.",
lessonsLearned: "Monitoring der Ressourcen-Auslastung mit Schwellwert-Alarm einrichten.",
ownerId: isb?.id ?? null,
createdBy: isb?.id ?? null,
},
});
if (erp) await prisma.incidentAsset.create({ data: { tenantId: tenant.id, incidentId: inc2.id, assetId: erp.id } });
console.log("✔ 2 Demo-Vorfälle angelegt");
}
// 10. Richtlinien & Verfahren aus dem VDA-ISA-2027-Vorlagenpaket importieren
// AP1: Framework-Zuordnung des Demo-Mandanten (er wird direkt per upsert angelegt,
// nicht über provisionTenant) — TISAX als Primär-Framework.
await prisma.tenantFramework.upsert({
where: { tenantId_framework: { tenantId: tenant.id, framework: "TISAX" } },
update: {},
create: { tenantId: tenant.id, framework: "TISAX", isPrimary: true },
});
const seedDir = join(__dirname, "..", "seed", "isms-vorlagenpaket-v2");
const pc = await importPolicies(prisma, tenant.id, seedDir);
await stampPackageState(prisma, tenant.id, seedDir); // Story B6: Paket-Version stempeln
// Umsetzungshinweise (C6, Story B5) — globaler Katalog, einmalig/idempotent.
const hintCount = await importImplementationHints(prisma);
// Standard-Risikokatalog (C4, Story A6) — globaler Katalog, einmalig/idempotent.
const riskCatCount = await importRiskCatalog(prisma);
// Standard-Prozess-Katalog (M2) wird bereits oben über seedGlobalContent geseedet.
const processCatCount = globalContent.processCatalog;
// Demo-Werte für Variablen ohne Schema-Default (damit die Doku vollständig wirkt)
const demoVars: Record<string, string> = {
ORG_NAME: "GEFIM Demo GmbH",
ORG_SHORT: "GEFIM",
ISMS_SCOPE: "IT-Betrieb & Softwareentwicklung",
ISMS_SCOPE_DESCRIPTION: "IT-Betrieb, Softwareentwicklung und zugehörige Unterstützungsprozesse am Standort Zentrale",
DOC_DATE: "2026-07-07",
};
for (const [key, value] of Object.entries(demoVars)) {
await prisma.policyVariable.updateMany({ where: { tenantId: tenant.id, key }, data: { value } });
}
console.log(`✔ Richtlinienpaket importiert: ${pc.documents} Dokumente, ${pc.requirements} Anforderungen, ${pc.variables} Variablen, ${pc.baseline} Baseline-Parameter`);
console.log(`✔ Umsetzungshinweise (C6) importiert: ${hintCount} Hinweise`);
console.log(`✔ Risikokatalog (C4) importiert: ${riskCatCount} Katalog-Risiken`);
console.log(`✔ Prozess-Katalog (M2) importiert: ${processCatCount} Standard-Prozesse`);
const mc = await importManaged(prisma, tenant.id);
console.log(`✔ Verwaltete Register: Krypto ${mc.crypto}, Klassifizierung ${mc.classes}×${mc.aspects}, Risikomatrix (${mc.damage} Schadensdim.), Handbuch ${mc.handbook} Themen`);
// 11. Admin-Konsole: Mandanten-Einstellungen, Module, Plattform-Admin (getrennter Store)
// Demo-Plattform-Admin: gleiche Adresse wie der Mandanten-Admin, damit /login (Mandant)
// und /platform/login (Betrieb) mit denselben Demo-Zugangsdaten funktionieren. MFA wird
// beim ersten Plattform-Login eingerichtet.
await prisma.platformAdmin.upsert({ await prisma.platformAdmin.upsert({
where: { email: "admin@demo.example" }, where: { email: "platform@demo.example" },
update: { name: "Anna Admin", status: "ACTIVE" }, update: { name: "Paula Plattform", status: "ACTIVE" },
create: { email: "admin@demo.example", name: "Anna Admin", passwordHash: await hashPassword("Demo1234!"), status: "ACTIVE" }, create: { email: "platform@demo.example", name: "Paula Plattform", passwordHash, status: "ACTIVE" },
}); });
await prisma.tenantSettings.upsert({ console.log(`✔ Plattform-Admin platform@demo.example (/platform/login)`);
where: { tenantId: tenant.id },
update: {},
create: {
tenantId: tenant.id,
orgName: "GEFIM Demo GmbH",
orgShort: "GEFIM",
sector: "IT-Dienstleistung",
ismsScope: "IT-Betrieb & Softwareentwicklung",
ismsScopeDescription: "IT-Betrieb, Softwareentwicklung und zugehörige Unterstützungsprozesse am Standort Zentrale",
roleManagement: "Geschäftsführung",
roleIsb: "Informationssicherheitsbeauftragte(r) (ISB)",
roleItLead: "IT-Leitung",
roleDpo: "Datenschutzbeauftragte(r) (DSB)",
tisaxLevel: "AL2",
},
});
for (const key of MODULE_KEYS) {
await prisma.tenantModule.upsert({
where: { tenantId_moduleKey: { tenantId: tenant.id, moduleKey: key } },
update: {},
create: { tenantId: tenant.id, moduleKey: key, enabled: true },
});
}
console.log(`✔ Admin-Konsole: Einstellungen, ${MODULE_KEYS.length} Module aktiv; Plattform-Admin admin@demo.example (getrennter Login /platform/login, MFA beim ersten Login)`);
} }
main() main()
-205
View File
@@ -1,205 +0,0 @@
import type { PrismaClient, Framework } from "@prisma/client";
import type { ParsedPackage, ParsedDoc } from "./import-policies";
import { parsePackageFiles, readPackageVersion, mappingFileFor } from "./import-policies";
/**
* Globale DB-Vorlagenablage (Plattform-Ebene). Master-Quelle für den Mandanten-Import:
* eine `PolicyTemplateVersion` je Paketversion mit Inhalten je Sprache (`locale`).
*
* - `syncTemplatesFromParsed`: schreibt ein `ParsedPackage` (aus den Dateien geparst)
* für eine Sprache in eine Version. Für eine bestehende Version werden die Inhalte
* dieser Sprache vollständig ersetzt (idempotent). Sicher, weil die Vorlagen-Tabellen
* keine Mandanten-/Nutzerdaten enthalten.
* - `loadPublishedPackage`: liest die aktuell veröffentlichte Version in der gewünschten
* Sprache als `ParsedPackage` — dieselbe Struktur wie der Datei-Parser, sodass der
* non-destruktive Mandanten-Reconcile identisch damit arbeitet. Liefert null, wenn es
* keine veröffentlichte Version bzw. keine Inhalte in der Sprache gibt (→ Datei-Fallback).
*/
type DocType = ParsedDoc["type"];
/** Ein Datei-Paket (eine Sprache) in eine Vorlagen-Version schreiben; Inhalte der Sprache ersetzen. */
export async function syncTemplatesFromParsed(
prisma: PrismaClient,
pkg: ParsedPackage,
locale: string,
framework: Framework = "TISAX",
options: { publish?: boolean; publishedBy?: string | null } = {},
): Promise<{ versionId: string; version: string }> {
const publish = options.publish ?? true;
// Eindeutigkeit je (Framework, Version): ISO 2.1 und TISAX 2.1 sind verschiedene Zeilen.
const existing = await prisma.policyTemplateVersion.findUnique({
where: { framework_version: { framework, version: pkg.version } },
});
const version =
existing ??
(await prisma.policyTemplateVersion.create({
data: {
framework,
version: pkg.version,
status: publish ? "PUBLISHED" : "DRAFT",
publishedBy: publish ? (options.publishedBy ?? null) : null,
publishedAt: publish ? new Date() : null,
},
}));
if (existing && publish && existing.status !== "PUBLISHED") {
await prisma.policyTemplateVersion.update({
where: { id: existing.id },
data: { status: "PUBLISHED", publishedAt: new Date(), publishedBy: options.publishedBy ?? null },
});
}
const versionId = version.id;
// Inhalte dieser Sprache vollständig ersetzen (idempotenter Sync).
await prisma.$transaction([
prisma.policyTemplateDoc.deleteMany({ where: { versionId, locale } }),
prisma.policyTemplateRequirement.deleteMany({ where: { versionId, locale } }),
prisma.policyTemplateVariable.deleteMany({ where: { versionId, locale } }),
prisma.policyTemplateBaselineParam.deleteMany({ where: { versionId, locale } }),
prisma.policyTemplateEvidence.deleteMany({ where: { versionId, locale } }),
prisma.policyTemplateDoc.createMany({
data: pkg.documents.map((d) => ({
versionId, locale, code: d.code, type: d.type, title: d.title,
policyCode: d.policyCode, fulfills: d.fulfills, rawMarkdown: d.rawMarkdown, orderIdx: d.orderIdx,
})),
}),
prisma.policyTemplateRequirement.createMany({
data: pkg.requirements.map((r) => ({
versionId, locale, reqId: r.reqId, policyCode: r.policyCode, control: r.control,
obligation: r.obligation, condition: r.condition, requirement: r.requirement,
implementation: r.implementation, vaCodes: r.vaCodes, nachweisLink: r.nachweisLink,
})),
}),
prisma.policyTemplateVariable.createMany({
data: pkg.variables.map((v) => ({
versionId, locale, key: v.key, title: v.title, kind: v.kind,
groupName: v.groupName, value: v.value, required: v.required, orderIdx: v.orderIdx,
})),
}),
prisma.policyTemplateBaselineParam.createMany({
data: pkg.baseline.map((b) => ({
versionId, locale, blId: b.blId, section: b.section, name: b.name, vorgabe: b.vorgabe, orderIdx: b.orderIdx,
})),
}),
prisma.policyTemplateEvidence.createMany({
data: pkg.evidence.map((e) => ({
versionId, locale, nr: e.nr, policyCode: e.policyCode, nachweis: e.nachweis,
quelle: e.quelle, verantwortlich: e.verantwortlich, turnus: e.turnus,
})),
}),
]);
return { versionId, version: version.version };
}
/**
* Aktuell veröffentlichte Vorlagen-Version in der gewünschten Sprache als `ParsedPackage`.
* Liefert null, wenn keine veröffentlichte Version existiert oder die Sprache keine
* Dokumente enthält (→ Aufrufer nutzt den Datei-Fallback).
*/
export async function loadPublishedPackage(
prisma: PrismaClient,
locale: string,
framework: Framework = "TISAX",
): Promise<ParsedPackage | null> {
const version = await prisma.policyTemplateVersion.findFirst({
where: { status: "PUBLISHED", framework },
orderBy: [{ publishedAt: "desc" }, { createdAt: "desc" }],
});
if (!version) return null;
const [docs, reqs, vars, baseline, evidence] = await Promise.all([
prisma.policyTemplateDoc.findMany({ where: { versionId: version.id, locale }, orderBy: { orderIdx: "asc" } }),
prisma.policyTemplateRequirement.findMany({ where: { versionId: version.id, locale }, orderBy: { orderIdx: "asc" } }),
prisma.policyTemplateVariable.findMany({ where: { versionId: version.id, locale }, orderBy: { orderIdx: "asc" } }),
prisma.policyTemplateBaselineParam.findMany({ where: { versionId: version.id, locale }, orderBy: { orderIdx: "asc" } }),
prisma.policyTemplateEvidence.findMany({ where: { versionId: version.id, locale }, orderBy: { nr: "asc" } }),
]);
if (docs.length === 0) return null; // Sprache (noch) nicht befüllt → Fallback
return {
version: version.version,
documents: docs.map((d) => ({
code: d.code, type: d.type as DocType, title: d.title, policyCode: d.policyCode,
fulfills: d.fulfills, rawMarkdown: d.rawMarkdown, orderIdx: d.orderIdx,
})),
requirements: reqs.map((r) => ({
reqId: r.reqId, policyCode: r.policyCode, control: r.control, obligation: r.obligation,
condition: r.condition, requirement: r.requirement, implementation: r.implementation,
vaCodes: r.vaCodes, nachweisLink: r.nachweisLink,
})),
variables: vars.map((v) => ({
key: v.key, title: v.title, kind: v.kind, groupName: v.groupName,
value: v.value, required: v.required, orderIdx: v.orderIdx,
})),
baseline: baseline.map((b) => ({
blId: b.blId, section: b.section, name: b.name, vorgabe: b.vorgabe, orderIdx: b.orderIdx,
})),
evidence: evidence.map((e) => ({
nr: e.nr, policyCode: e.policyCode, nachweis: e.nachweis, quelle: e.quelle,
verantwortlich: e.verantwortlich, turnus: e.turnus,
})),
};
}
/** Sprachpräferenz eines Mandanten (Default "de"). Owner-Client (RLS-Bypass) — für Plattform- UND Mandanten-Aufrufer nutzbar. */
export async function getTenantLocale(prisma: PrismaClient, tenantId: string): Promise<string> {
const s = await prisma.tenantSettings.findUnique({ where: { tenantId }, select: { locale: true } });
return s?.locale ?? "de";
}
/**
* Auflösen des Vorlagenpakets für einen Mandanten: veröffentlichte DB-Version in der
* Sprache des Mandanten → DE-Fallback → Datei-Fallback. So funktioniert der Import
* identisch, egal ob die DB-Ablage schon befüllt ist.
*/
export async function resolvePackageForTenant(
prisma: PrismaClient,
tenantId: string,
seedDir: string,
framework: Framework = "TISAX",
): Promise<{ pkg: ParsedPackage; source: "db" | "file"; locale: string }> {
const locale = await getTenantLocale(prisma, tenantId);
const inLocale = await loadPublishedPackage(prisma, locale, framework);
if (inLocale) return { pkg: inLocale, source: "db", locale };
if (locale !== "de") {
const de = await loadPublishedPackage(prisma, "de", framework);
if (de) return { pkg: de, source: "db", locale: "de" };
}
// Datei-Fallback: das framework-passende Mapping (mapping.json / mapping-iso.json).
return { pkg: parsePackageFiles(seedDir, mappingFileFor(framework)), source: "file", locale };
}
/** Verfügbare Paketversion für den Update-Vergleich: veröffentlichte DB-Version, sonst Datei-Version. */
export async function getAvailableVersion(
prisma: PrismaClient,
seedDir: string,
framework: Framework = "TISAX",
): Promise<string> {
const v = await prisma.policyTemplateVersion.findFirst({
where: { status: "PUBLISHED", framework },
orderBy: [{ publishedAt: "desc" }, { createdAt: "desc" }],
select: { version: true },
});
return v?.version ?? readPackageVersion(seedDir, mappingFileFor(framework));
}
/**
* Frameworks eines Mandanten (AP1), Primär-Framework zuerst. Fällt auf `["TISAX"]`
* zurück, falls (noch) keine `TenantFramework`-Zeile existiert — so bleibt jeder
* Bestands-/Alt-Mandant ohne Zeile als TISAX lauffähig (Fail-safe zu Invariante 3).
* Owner-Client (RLS-Bypass) — für Plattform- UND Mandanten-Aufrufer nutzbar.
*/
export async function getTenantFrameworks(prisma: PrismaClient, tenantId: string): Promise<Framework[]> {
const rows = await prisma.tenantFramework.findMany({
where: { tenantId },
orderBy: [{ isPrimary: "desc" }, { createdAt: "asc" }],
select: { framework: true },
});
return rows.length ? rows.map((r) => r.framework) : ["TISAX"];
}
/** Primär-Framework eines Mandanten (erstes aus {@link getTenantFrameworks}). */
export async function getTenantPrimaryFramework(prisma: PrismaClient, tenantId: string): Promise<Framework> {
return (await getTenantFrameworks(prisma, tenantId))[0];
}
-61
View File
@@ -1,61 +0,0 @@
import "dotenv/config";
import { prisma } from "../src/server/db";
/**
* Backfill 5×5-Risikokriterien für Bestandsmandanten: ergänzt die 5. Eintritts-
* wahrscheinlichkeits-Stufe und die 5. Schadensstufe je Dimension, falls noch nicht
* vorhanden. Die Bewertung (Formular/Matrix/Score) war schon 5×5 — nur die Kriterien-
* daten waren 4-stufig geseedet. Idempotent (mehrfach ausführbar).
*
* npx tsx scripts/backfill-risk-5x5.ts # alle Mandanten
* TENANT_SLUG=gefim npx tsx scripts/backfill-risk-5x5.ts # nur einer
*/
// Sinnvolle Default-Texte für Stufe 5 der Standard-Schadensdimensionen (Abgleich per Name).
const LEVEL5: Record<string, string> = {
"Gesetzes-/Vertragsverstöße": "existenzbedrohende Rechtsfolgen (Lizenzentzug/Haftung)",
"Datenschutz": "besonders schützenswerte Daten in großem Umfang / existenzielle Betroffenheit",
"Persönliche Unversehrtheit": "Lebensgefahr für viele / Todesfolge",
"Aufgabenerfüllung": "vollständiger, dauerhafter Ausfall der Organisation",
"Innen-/Außenwirkung (Reputation)": "existenzbedrohender, dauerhafter Reputationsverlust",
"Störungs-/Ausfallzeit": "> 1 Monat / dauerhaft",
"Finanzielle Auswirkungen": "existenzbedrohend (> 1 Mio €)",
};
const FALLBACK5 = "(bitte definieren)";
async function main() {
const slug = process.env.TENANT_SLUG?.trim().toLowerCase();
const tenants = await prisma.tenant.findMany({ where: slug ? { slug } : {}, select: { id: true, slug: true } });
if (!tenants.length) { console.log("Keine Mandanten gefunden."); return; }
for (const t of tenants) {
let ew5 = 0, dim5 = 0;
// EW-Stufe 5 ergänzen, falls nicht vorhanden
const hasEw5 = await prisma.riskEwLevel.findFirst({ where: { tenantId: t.id, level: 5 } });
if (!hasEw5) {
// Nur ergänzen, wenn überhaupt EW-Stufen existieren (sonst provisioniert der Mandant sie neu).
const anyEw = await prisma.riskEwLevel.count({ where: { tenantId: t.id } });
if (anyEw > 0) {
await prisma.riskEwLevel.create({ data: { tenantId: t.id, level: 5, label: "Nahezu sicher", definition: "Ereignis ist praktisch dauerhaft gegeben oder tritt ständig ein." } });
ew5 = 1;
}
}
// Schadensdimensionen: je Dimension "5" ergänzen, falls fehlt
const dims = await prisma.riskDamageDimension.findMany({ where: { tenantId: t.id } });
for (const d of dims) {
const levels = (d.levels ?? {}) as Record<string, string>;
if (levels["5"] == null || levels["5"] === "") {
levels["5"] = LEVEL5[d.name] ?? FALLBACK5;
await prisma.riskDamageDimension.update({ where: { id: d.id }, data: { levels } });
dim5++;
}
}
console.log(`Mandant ${t.slug}: EW-Stufe 5 ${ew5 ? "ergänzt" : "bereits vorhanden/übersprungen"}, ${dim5} Schadensdimension(en) um Stufe 5 ergänzt.`);
}
console.log("Fertig.");
}
main().then(() => process.exit(0)).catch((e) => { console.error(e); process.exit(1); });
-63
View File
@@ -1,63 +0,0 @@
// 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)")));
-67
View File
@@ -1,67 +0,0 @@
// Generator (Story A7-2): parst die C5-Control-Belegtabelle (Fachcontent §5, IS-Controls)
// in die maschinenlesbare Reifegrad-Datengrundlage `seed/scoping/c5-controls.json`.
// Lauf: npx tsx scripts/build-c5-controls.ts
//
// Quelle §5-Zeilen: | **Control** Titel | P / V / N (mit (**A**)/(**R**)) | R2-Bed. | R3-Bed. | Ziel |
// - policy (P): L00 | R01..R14 (zuständige Richtlinie[n], „—" = keine)
// - verfahren (V): VA-01..VA-19 (geforderte Verfahren, „—" = in Richtlinie verankert)
// - needsAsset/needsRisk: Belegzelle enthält (**A**) bzw. (**R**)
// - target: Standard-Zielreifegrad der Tabelle (informativ; effektiv wird er zur Laufzeit
// aus dem Scope nach C5 §3 abgeleitet).
// §6 (Proto 8.x / Datenschutz 9.x) ist gruppenbasiert und wird NICHT je Control abgebildet;
// dafür greift zur Laufzeit ein generischer Fallback-Spec.
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
import { dirname, resolve } from "node:path";
const SRC = resolve("docs/wizard-uebergabe/02_Fachcontent_C1-C9/C5_Reifegradlogik.md");
const OUT = resolve("seed/scoping/c5-controls.json");
export interface C5ControlSpec {
control: string;
title: string;
policy: string[];
verfahren: string[];
needsAsset: boolean;
needsRisk: boolean;
target: number;
}
const CONTROL_RE = /^\|\s*\*\*([0-9]+\.[0-9]+\.[0-9]+(?:-[A-Z]+)?)\*\*\s*([^|]*?)\s*\|(.+)$/;
function codes(cell: string, re: RegExp): string[] {
const out = new Set<string>();
for (const m of cell.matchAll(re)) out.add(m[0]);
return [...out];
}
function parse(md: string): C5ControlSpec[] {
const rows: C5ControlSpec[] = [];
for (const line of md.split("\n")) {
const m = CONTROL_RE.exec(line.trim());
if (!m) continue;
const [, control, title, rest] = m;
// rest = "Belege | R2 | R3 | Ziel |"
const cells = rest.split("|").map((c) => c.trim());
const belege = cells[0] ?? "";
const zielCell = cells[3] ?? cells[cells.length - 2] ?? "";
const [pPart = "", vPart = ""] = belege.split(" / ");
const target = Number.parseInt(zielCell.replace(/[^0-9]/g, "").charAt(0) || "3", 10);
rows.push({
control,
title: title.trim(),
policy: codes(pPart, /L00|R[0-9]{2}/g),
verfahren: codes(vPart, /VA-[0-9]{2}/g),
needsAsset: /\*\*A\*\*/.test(belege),
needsRisk: /\*\*R\*\*/.test(belege),
target: Number.isFinite(target) ? target : 3,
});
}
return rows;
}
const specs = parse(readFileSync(SRC, "utf8"));
if (specs.length < 40) throw new Error(`C5 §5 unerwartet wenige Controls geparst: ${specs.length}`);
mkdirSync(dirname(OUT), { recursive: true });
writeFileSync(OUT, JSON.stringify(specs, null, 2) + "\n");
console.log(`c5-controls.json geschrieben: ${specs.length} Controls`);
+86 -95
View File
@@ -1,138 +1,129 @@
/** /**
* Vollständigkeitscheck der serverseitigen Modul-Durchsetzung (§3.4, Phase-1-Härtung). * Vollständigkeitscheck der serverseitigen Modul-Durchsetzung.
* *
* Jede mutierende Server-Action eines gegateten Moduls MUSS über einen * Jede mutierende Server-Action eines gegateten Moduls MUSS über einen
* `moduleGuard("<key>")`-Guard laufen (siehe src/server/action-guard.ts), damit ein * `moduleGuard("<key>")`-Guard laufen (src/server/action-guard.ts), damit ein
* für den Mandanten deaktiviertes Modul auch Writes serverseitig abweist. * für den Mandanten deaktiviertes Modul auch Writes serverseitig abweist.
* *
* Dieses Script erzwingt das statisch: Es kennt die Zuordnung Action-Datei → Modul * ── Konvention (Craftvia) ────────────────────────────────────────────────────
* und schlägt fehl (Exit 1 → Build/Test rot), sobald *
* - eine neue Action-Datei nicht zugeordnet ist ("vergessener Endpoint"), * 1) Modul-Actions liegen in einem Unterordner je Modul:
* src/server/actions/<moduleKey>/*.ts (auch tiefer verschachtelt)
* Der Modul-Key wird aus dem ERSTEN Ordnernamen abgeleitet und muss in MODULE_KEYS
* (src/lib/modules.ts) stehen, z. B. src/server/actions/work_orders/assign.ts →
* Modul „work_orders". Keine Eintragung in diesem Skript nötig.
* Jede solche Datei muss
* - `moduleGuard("<moduleKey>")` verwenden und
* - jede `export async function` über `await guard(` laufen lassen.
* Reine Hilfsdateien ohne exportierte async functions (z. B. schemas.ts) sind erlaubt;
* Dateien, die mit `_` beginnen, werden als intern übersprungen.
*
* 2) Top-Level-Dateien src/server/actions/*.ts sind Fundament (Auth, Plattform,
* Einstellungen …) und stehen in der expliziten Map ACTION_MODULE — entweder mit
* Modul-Key (dann gelten die Regeln aus 1) oder "EXEMPT" (eigene Auth-Prüfung
* erforderlich: require…-Guard oder auth()).
*
* Schlägt fehl (Exit 1 → prebuild/Gate rot), sobald
* - eine Top-Level-Datei nicht zugeordnet ist ("vergessener Endpoint"),
* - ein Modulordner keinen gültigen Modul-Key trägt,
* - eine gegatete Datei den erwarteten moduleGuard nicht verwendet, oder * - eine gegatete Datei den erwarteten moduleGuard nicht verwendet, oder
* - eine exportierte Action nicht über `await guard(...)` läuft. * - eine exportierte Action nicht über `await guard(...)` läuft.
*
* Neue Action-Datei anlegen ⇒ hier eintragen (Modul-Key oder "EXEMPT").
*/ */
import { readdirSync, readFileSync } from "node:fs"; import { readdirSync, readFileSync, statSync } from "node:fs";
import { join, dirname } from "node:path"; import { join, dirname, relative, sep } from "node:path";
import { fileURLToPath } from "node:url"; import { fileURLToPath } from "node:url";
import { MODULE_KEYS } from "../src/lib/modules"; import { MODULE_KEYS } from "../src/lib/modules";
const ACTIONS_DIR = join(dirname(fileURLToPath(import.meta.url)), "..", "src", "server", "actions"); const ACTIONS_DIR = join(dirname(fileURLToPath(import.meta.url)), "..", "src", "server", "actions");
/** Zuordnung Action-Datei → Modul-Key. "EXEMPT" = kein gegatetes Fachmodul (eigene Auth). */ /** Top-Level-Action-Datei → Modul-Key oder "EXEMPT" (Fundament mit eigener Auth). */
const ACTION_MODULE: Record<string, string> = { const ACTION_MODULE: Record<string, string> = {
"assets.ts": "assets", // Plattform-Betrieb (Auth über die Plattform-Session)
// M2 Strukturanalyse: primäre Informations-Assets (Dedup/Autocomplete) → Asset-Modul.
"structure.ts": "assets",
"processes.ts": "bia",
"risks.ts": "risk",
"risk-catalog.ts": "risk",
"measures.ts": "measures",
"tasks.ts": "tasks",
"incidents.ts": "incidents",
// IM-D: mandantenseitige Pflege der E-Mail-Intake-Konfiguration (moduleGuard("incidents") + tenant:manage).
"incident-intake.ts": "incidents",
"suppliers.ts": "suppliers",
"services.ts": "suppliers",
"software.ts": "suppliers",
"projects.ts": "assets",
"onboarding.ts": "onboarding",
"onboarding-facts.ts": "onboarding",
"onboarding-steps.ts": "onboarding",
"onboarding-team.ts": "onboarding",
"soa.ts": "onboarding",
// AP3: ISO-Anwendbarkeitserklärung (eigenes Modul „soa").
"soa-entries.ts": "soa",
// AP4: Managementklauseln (Kennzahlen/Managementbewertung/CAPA) im Modul „review".
"review.ts": "review",
"gap.ts": "onboarding",
// Audit-Vorbereitung — Modul `audit`.
"audits.ts": "audit",
"audit-evidence.ts": "audit",
"control-descriptions.ts": "audit",
"policies.ts": "policies",
"policy-package.ts": "policies",
// AP5: Dokumentenlenkung (Prüfzyklus, Neuversion/Historie, Lesebestätigung).
"policy-control.ts": "policies",
"policy-upload.ts": "policies",
"hints.ts": "policies",
"register.ts": "policies",
// Plattform-Betrieb (eigene Auth) und Kunden-Einstellungen (tenant:manage) sind
// keine per TenantModule gegateten Fachmodule — eigene Autorisierung, kein moduleGuard.
"admin.ts": "EXEMPT", "admin.ts": "EXEMPT",
// SEC1: Mail-Betriebsfunktionen der Plattform-Administration (Auth über die
// Plattform-Session), kein per TenantModule gegatetes Fachmodul.
"mail.ts": "EXEMPT", "mail.ts": "EXEMPT",
// Backup-Portal: Enqueue-Actions für Portal-Restore/Export/DSGVO-Zustellung.
// Betreiber-/Plattform-Fähigkeit (Auth über requirePlatformFullAdmin + MFA-Step-up),
// kein per TenantModule gegatetes Fachmodul.
"backup-admin.ts": "EXEMPT", "backup-admin.ts": "EXEMPT",
"backup-settings.ts": "EXEMPT", "backup-settings.ts": "EXEMPT",
// IM-D: Betreiber-Provisionierung/Verifizierung der Intake-Konfiguration + Inbound-Review.
// Plattform-Fähigkeit (requirePlatformFullAdmin), kein per TenantModule gegatetes Fachmodul.
"incident-intake-admin.ts": "EXEMPT",
// SEC2: Passwort-Self-Service. Die Reset-Abläufe laufen bewusst OHNE Session
// (der Nutzer ist ausgesperrt); abgesichert über Rate-Limit, Enumeration-
// Neutralität und single-use-Tokens. Die angemeldeten Abläufe nutzen
// requireSession bzw. requirePlatformSession.
"auth-recovery.ts": "EXEMPT",
"platform.ts": "EXEMPT", "platform.ts": "EXEMPT",
"platform-users.ts": "EXEMPT", "platform-users.ts": "EXEMPT",
"platform-admins.ts": "EXEMPT",
// SEC2: Passwort-Self-Service. Reset-Abläufe laufen bewusst OHNE Session; abgesichert
// über Rate-Limit, Enumeration-Neutralität und single-use-Tokens.
"auth-recovery.ts": "EXEMPT",
// Mandanten-Fundament (requireSession/requirePermission)
"tenant-users.ts": "EXEMPT", "tenant-users.ts": "EXEMPT",
"tenant-settings.ts": "EXEMPT",
"account.ts": "EXEMPT", "account.ts": "EXEMPT",
"tenant-switch.ts": "EXEMPT", "tenant-switch.ts": "EXEMPT",
"webauthn.ts": "EXEMPT", "webauthn.ts": "EXEMPT",
"platform-admins.ts": "EXEMPT",
"policy-templates.ts": "EXEMPT",
"tenant-settings.ts": "EXEMPT",
}; };
const errors: string[] = []; const errors: string[] = [];
const files = readdirSync(ACTIONS_DIR).filter((f) => f.endsWith(".ts")); let checked = 0;
for (const file of files) { function checkGatedFile(label: string, src: string, moduleKey: string) {
const mapped = ACTION_MODULE[file]; if (!(MODULE_KEYS as readonly string[]).includes(moduleKey)) {
if (!mapped) { errors.push(`${label}: unbekannter Modul-Key "${moduleKey}" (nicht in src/lib/modules.ts).`);
errors.push(
`Nicht zugeordnete Action-Datei: ${file} — in scripts/check-module-guards.ts eintragen (Modul-Key oder "EXEMPT").`
);
continue;
} }
const src = readFileSync(join(ACTIONS_DIR, file), "utf8");
if (mapped === "EXEMPT") {
// Auth-Nachweis: entweder ein require*-Guard ODER ein direkter auth()-Aufruf
// (z. B. tenant-switch.ts, das im No-Tenant-Zustand kein requireSession nutzen
// kann, aber die Identity + Mitgliedschaftszugehörigkeit selbst prüft).
if (!/require(Session|Platform\w*|Permission)|\bauth\(\)/.test(src)) {
errors.push(`${file}: als EXEMPT markiert, aber keine erkennbare Auth-Prüfung.`);
}
continue;
}
if (!MODULE_KEYS.includes(mapped)) {
errors.push(`${file}: unbekannter Modul-Key "${mapped}" (nicht in src/lib/modules.ts).`);
}
if (!src.includes(`moduleGuard("${mapped}")`)) {
errors.push(`${file}: erwartet moduleGuard("${mapped}") — Modul-Gating fehlt oder falscher Key.`);
}
// Jede exportierte Server-Action muss über await guard(...) laufen.
const exportRe = /export async function (\w+)\s*\(/g; const exportRe = /export async function (\w+)\s*\(/g;
const positions: { name: string; index: number }[] = []; const positions: { name: string; index: number }[] = [];
let m: RegExpExecArray | null; let m: RegExpExecArray | null;
while ((m = exportRe.exec(src))) positions.push({ name: m[1], index: m.index }); while ((m = exportRe.exec(src))) positions.push({ name: m[1], index: m.index });
if (positions.length === 0) return; // Hilfsdatei ohne Actions
if (!src.includes(`moduleGuard("${moduleKey}")`)) {
errors.push(`${label}: erwartet moduleGuard("${moduleKey}") — Modul-Gating fehlt oder falscher Key.`);
}
for (let i = 0; i < positions.length; i++) { for (let i = 0; i < positions.length; i++) {
const start = positions[i].index; const start = positions[i].index;
const end = i + 1 < positions.length ? positions[i + 1].index : src.length; const end = i + 1 < positions.length ? positions[i + 1].index : src.length;
if (!/await guard\(/.test(src.slice(start, end))) { if (!/await guard\(/.test(src.slice(start, end))) {
errors.push(`${label}: Action "${positions[i].name}" läuft nicht über await guard(...) — Modul-/Rechte-Guard fehlt.`);
}
}
}
function walk(dir: string): string[] {
return readdirSync(dir).flatMap((name) => {
const full = join(dir, name);
return statSync(full).isDirectory() ? walk(full) : [full];
});
}
for (const entry of readdirSync(ACTIONS_DIR)) {
const full = join(ACTIONS_DIR, entry);
if (statSync(full).isDirectory()) {
// (1) Modulordner: Key = Ordnername.
const moduleKey = entry;
for (const file of walk(full).filter((f) => f.endsWith(".ts"))) {
const rel = relative(ACTIONS_DIR, file).split(sep).join("/");
if (rel.split("/").some((seg) => seg.startsWith("_"))) continue;
checked++;
checkGatedFile(rel, readFileSync(file, "utf8"), moduleKey);
}
continue;
}
if (!entry.endsWith(".ts")) continue;
// (2) Top-Level-Fundament-Datei.
checked++;
const mapped = ACTION_MODULE[entry];
if (!mapped) {
errors.push( errors.push(
`${file}: Action "${positions[i].name}" läuft nicht über await guard(...) — Modul-/Rechte-Guard fehlt.` `Nicht zugeordnete Action-Datei: ${entry} — Modul-Actions gehören nach src/server/actions/<moduleKey>/; ` +
`Fundament-Dateien in scripts/check-module-guards.ts eintragen (Modul-Key oder "EXEMPT").`,
); );
continue;
} }
const src = readFileSync(full, "utf8");
if (mapped === "EXEMPT") {
// Auth-Nachweis: ein require*-Guard ODER ein direkter auth()-Aufruf.
if (!/require(Session|Platform\w*|Permission)|\bauth\(\)/.test(src)) {
errors.push(`${entry}: als EXEMPT markiert, aber keine erkennbare Auth-Prüfung.`);
} }
continue;
}
checkGatedFile(entry, src, mapped);
} }
if (errors.length) { if (errors.length) {
@@ -141,5 +132,5 @@ if (errors.length) {
process.exit(1); process.exit(1);
} }
console.log( console.log(
`✓ Modul-Guard-Vollständigkeitscheck: ${files.length} Action-Dateien geprüft — alle mutierenden Actions sind modul- und rechtegegated.` `✓ Modul-Guard-Vollständigkeitscheck: ${checked} Action-Dateien geprüft — alle mutierenden Actions sind modul- und rechtegegated.`,
); );
-15
View File
@@ -1,15 +0,0 @@
import "dotenv/config";
import { PrismaClient } from "@prisma/client";
import { PrismaPg } from "@prisma/adapter-pg";
import { importImplementationHints } from "../prisma/import-hints";
// Standalone-Import der C6-Umsetzungshinweise (Story B5-1). Idempotent.
const prisma = new PrismaClient({ adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }) });
async function main() {
const n = await importImplementationHints(prisma);
const total = await prisma.implementationHint.count();
const proc = await prisma.implementationHint.count({ where: { procurement: true } });
console.log(JSON.stringify({ parsed: n, inDb: total, procurement: proc }));
}
main().finally(() => prisma.$disconnect());
-15
View File
@@ -1,15 +0,0 @@
import "dotenv/config";
import { PrismaClient } from "@prisma/client";
import { PrismaPg } from "@prisma/adapter-pg";
import { importRiskCatalog } from "../prisma/import-risks";
// Standalone-Import des C4-Risikokatalogs (Story A6-1). Idempotent.
const prisma = new PrismaClient({ adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }) });
async function main() {
const n = await importRiskCatalog(prisma);
const total = await prisma.riskCatalogEntry.count();
const cats = await prisma.riskCatalogEntry.groupBy({ by: ["category"], _count: true });
console.log(JSON.stringify({ parsed: n, inDb: total, categories: cats.length }));
}
main().finally(() => prisma.$disconnect());
-205
View File
@@ -1,205 +0,0 @@
import "dotenv/config";
import { parseInbound, type RawHeaders } from "../src/server/incident-inbound/parse";
import { processInbound } from "../src/server/incident-inbound/process";
/**
* IM-D — Einstiegspunkt des Inbound-Mail-Workers (`npm run worker:incident-inbound`).
*
* Eigener Prozess/Container neben der App (analog Mail-/Backup-Worker). Holt Mails
* vom Catch-all-Postfach (`vorfall-<token>@in.certvia.de`, KONZEPT §2/§14.3) per
* IMAP ab, parst sie (rein, parse.ts) und verarbeitet sie (process.ts): Vorfall im
* richtigen Mandanten anlegen ODER in die Betreiber-Review geben.
*
* IMAP-Bibliotheken (imapflow/mailparser) werden BEWUSST dynamisch importiert:
* - der Web-/Build-Pfad zieht sie nie mit ein (reiner Ops-Prozess),
* - `tsc --noEmit` bleibt unabhängig von den Paket-Typdefinitionen grün.
*
* Ohne IMAP-Env (INCIDENT_IMAP_HOST/USER/PASSWORD) gibt es keinen Betrieb — der
* Worker meldet „nicht konfiguriert" und geht in den LEERLAUF (Prozess bleibt am
* Leben), statt sich zu beenden. Grund: mit `restart: unless-stopped` würde ein
* Exit einen Crash-Loop erzeugen, den Coolify als unhealthy wertet und deshalb den
* GESAMTEN Stack (inkl. gesunder App) wieder abreißt. Idle → Container „running",
* Deploy bleibt grün; bei nachträglicher IMAP-Konfig aktiviert ein Neustart den Betrieb.
*/
interface ImapEnv {
host: string;
port: number;
user: string;
password: string;
tls: boolean;
mailbox: string;
pollMs: number;
}
function readEnv(): ImapEnv | null {
const host = process.env.INCIDENT_IMAP_HOST?.trim();
const user = process.env.INCIDENT_IMAP_USER?.trim();
const password = process.env.INCIDENT_IMAP_PASSWORD;
if (!host || !user || !password) return null;
return {
host,
port: Number(process.env.INCIDENT_IMAP_PORT ?? "993"),
user,
password,
// Default TLS an; nur bei explizit "false"/"0" abschalten (STARTTLS/Plain).
tls: !["false", "0", "no"].includes((process.env.INCIDENT_IMAP_TLS ?? "true").toLowerCase()),
mailbox: process.env.INCIDENT_IMAP_MAILBOX?.trim() || "INBOX",
pollMs: Math.max(15_000, Number(process.env.INCIDENT_IMAP_POLL_MS ?? "60000")),
};
}
/** mailparser-Header (Map) → flache RawHeaders für parse.ts. */
function toRawHeaders(headerLines: Array<{ key: string; line: string }> | undefined): RawHeaders {
const out: RawHeaders = {};
for (const h of headerLines ?? []) {
// `line` ist die vollständige "Key: Value"-Zeile; Wert hinter dem ersten ":".
const idx = h.line.indexOf(":");
const value = idx >= 0 ? h.line.slice(idx + 1).trim() : h.line.trim();
const key = h.key;
const existing = out[key];
if (existing === undefined) out[key] = value;
else if (Array.isArray(existing)) existing.push(value);
else out[key] = [existing, value];
}
return out;
}
/**
* Hält den Prozess am Leben, bis SIGTERM/SIGINT kommt — statt Crash-Loop bei fehlender
* IMAP-Konfig. Der Heartbeat-Timer hält den Event-Loop offen (Signal-Listener allein
* genügen dafür nicht); bei Signal wird er gestoppt und der Prozess endet sauber (Exit 0).
*/
function idleUntilSignal(): Promise<void> {
return new Promise<void>((resolve) => {
const beat = setInterval(() => {}, 60_000);
const done = (signal: string) => {
clearInterval(beat);
console.info(`[incident-inbound] ${signal} — Leerlauf beendet.`);
resolve();
};
process.on("SIGTERM", () => done("SIGTERM"));
process.on("SIGINT", () => done("SIGINT"));
});
}
async function main() {
const env = readEnv();
if (!env) {
console.warn(
"[incident-inbound] IMAP ist nicht konfiguriert (INCIDENT_IMAP_HOST/USER/PASSWORD fehlen). " +
"Ohne Postfach kein E-Mail-to-Ticket-Betrieb — Worker läuft im Leerlauf (kein Crash-Loop). " +
"Setze die INCIDENT_IMAP_*-Variablen und starte den Container neu, um den Betrieb zu aktivieren.",
);
await idleUntilSignal();
return;
}
// Dynamischer Import: hält Web-/Build-Pfad + tsc frei von IMAP-Typen.
let ImapFlow: unknown;
let simpleParser: unknown;
try {
({ ImapFlow } = (await import("imapflow")) as { ImapFlow: unknown });
({ simpleParser } = (await import("mailparser")) as { simpleParser: unknown });
} catch (err) {
console.error(
"[incident-inbound] Pakete 'imapflow'/'mailparser' nicht installiert. " +
"`npm install imapflow mailparser` im Worker-Image sicherstellen.",
err,
);
process.exit(1);
}
/* eslint-disable @typescript-eslint/no-explicit-any */
const ClientCtor = ImapFlow as any;
const parse = simpleParser as any;
const client = new ClientCtor({
host: env.host,
port: env.port,
secure: env.tls,
auth: { user: env.user, pass: env.password },
logger: false,
});
let shuttingDown = false;
const stop = async (signal: string) => {
if (shuttingDown) return;
shuttingDown = true;
console.info(`[incident-inbound] ${signal} — fahre herunter…`);
try {
await client.logout();
} catch {
/* egal beim Herunterfahren */
}
process.exit(0);
};
process.on("SIGTERM", () => void stop("SIGTERM"));
process.on("SIGINT", () => void stop("SIGINT"));
await client.connect();
console.info(
`[incident-inbound] verbunden mit ${env.host} (${env.mailbox}) — Intake-Domain ` +
`${process.env.INCIDENT_INTAKE_DOMAIN ?? "in.certvia.de"}, Poll ${env.pollMs}ms.`,
);
async function drainOnce(): Promise<void> {
const lock = await client.getMailboxLock(env!.mailbox);
try {
// Nur ungelesene Mails; nach erfolgreicher Verarbeitung als \Seen markieren
// (Idempotenz zusätzlich über Message-ID in process.ts).
for await (const message of client.fetch({ seen: false }, { source: true, uid: true })) {
let handled = false;
try {
const mail = await parse(message.source);
const parsed = parseInbound({
headers: toRawHeaders(mail.headerLines),
from: mail.from?.text,
subject: mail.subject,
text: mail.text ?? "",
messageId: mail.messageId,
});
const result = await processInbound(parsed);
const decision = result.decision;
const detail =
decision.action === "incident"
? `Vorfall ${result.refNo}`
: decision.action === "review"
? `Review (${decision.review.reason})`
: `ignoriert (${decision.reason})`;
console.info(`[incident-inbound] UID ${message.uid}: ${detail}`);
handled = true;
} catch (err) {
// Verarbeitungsfehler: NICHT als gelesen markieren → nächster Lauf erneut.
console.error(`[incident-inbound] Fehler bei UID ${message.uid}:`, err);
}
if (handled) {
try {
await client.messageFlagsAdd({ uid: message.uid }, ["\\Seen"], { uid: true });
} catch (err) {
console.error(`[incident-inbound] Konnte UID ${message.uid} nicht als gelesen markieren:`, err);
}
}
}
} finally {
lock.release();
}
}
/* eslint-enable @typescript-eslint/no-explicit-any */
// Poll-Schleife (robust gegen einzelne Fehlläufe). IMAP-IDLE wäre latenzärmer, das
// Polling ist aber einfacher, ausreichend und übersteht Verbindungsabbrüche.
while (!shuttingDown) {
try {
await drainOnce();
} catch (err) {
console.error("[incident-inbound] Abholung fehlgeschlagen (nächster Versuch folgt):", err);
}
await new Promise((r) => setTimeout(r, env.pollMs));
}
}
main().catch((err) => {
console.error("[incident-inbound] Start fehlgeschlagen:", err);
process.exit(1);
});
File diff suppressed because it is too large Load Diff
-58
View File
@@ -1,58 +0,0 @@
// Einmalige Synchronisierung des Datei-Vorlagenpakets in die globale DB-Vorlagenablage.
// Überführt seed/isms-vorlagenpaket-v2/ (deutsch) in eine veröffentlichte
// PolicyTemplateVersion. Danach ist die DB die Master-Quelle; der Plattform-Admin
// bearbeitet Vorlagen/Versionen dort, der Mandanten-Import liest die veröffentlichte
// Version. Idempotent: erneuter Lauf ersetzt die deutschen Inhalte der Version.
//
// Lauf (lokal): npx tsx scripts/sync-policy-templates.ts
// Lauf (Deploy): im Container/Coolify mit gesetzter DATABASE_URL ausführen (einmalig
// bzw. nach Paket-Updates, solange die Pflege noch datei-basiert erfolgt).
import "dotenv/config";
import { join } from "node:path";
import { existsSync } from "node:fs";
import { PrismaClient, type Framework } from "@prisma/client";
import { PrismaPg } from "@prisma/adapter-pg";
import { parsePackageFiles, mappingFileFor } from "../prisma/import-policies";
import { syncTemplatesFromParsed } from "../prisma/template-store";
const prisma = new PrismaClient({ adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }) });
const ROOT = join(process.cwd(), "seed");
// Sprache → Seed-Verzeichnis. EN nur, wenn vorhanden (Übersetzungspaket).
const SOURCES: Array<{ locale: string; dir: string }> = [
{ locale: "de", dir: join(ROOT, "isms-vorlagenpaket-v2") },
{ locale: "en", dir: join(ROOT, "isms-vorlagenpaket-v2-en") },
];
// AP1: beide Frameworks teilen den Dokumentensatz, nur das Mapping wechselt. Ein Sync
// je (Framework × Sprache) — das ISO-Mapping nur, wenn die Datei existiert.
const FRAMEWORKS: Framework[] = ["TISAX", "ISO_27001"];
async function main() {
for (const { locale, dir } of SOURCES) {
if (!existsSync(dir)) {
console.log(`Sprache ${locale}: kein Verzeichnis (${dir}) — übersprungen.`);
continue;
}
for (const framework of FRAMEWORKS) {
const mappingFile = mappingFileFor(framework);
if (!existsSync(join(dir, mappingFile))) {
console.log(` ${framework}/${locale}: kein ${mappingFile} — übersprungen.`);
continue;
}
const pkg = parsePackageFiles(dir, mappingFile);
const { version } = await syncTemplatesFromParsed(prisma, pkg, locale, framework, { publish: true });
console.log(
`Vorlagen-Sync (${framework}/${locale}) → Version ${version}: ` +
`${pkg.documents.length} Dokumente, ${pkg.requirements.length} Anforderungen, ` +
`${pkg.variables.length} Variablen, ${pkg.baseline.length} Baseline, ${pkg.evidence.length} Nachweise.`,
);
}
}
}
main()
.catch((e) => {
console.error("Vorlagen-Sync fehlgeschlagen:", e.message);
process.exit(1);
})
.finally(() => prisma.$disconnect());
+2 -2
View File
@@ -25,7 +25,7 @@ const ok = (cond: boolean, msg: string) => {
}; };
const SLUG = "zz-actionerr-test"; const SLUG = "zz-actionerr-test";
const ENTITY = "risk_ae_test"; const ENTITY = "customer_ae_test";
async function cleanup() { async function cleanup() {
const tenant = await prisma.tenant.findFirst({ const tenant = await prisma.tenant.findFirst({
@@ -49,7 +49,7 @@ async function main() {
let outward = ""; let outward = "";
try { try {
await withActionErrors(async () => { await withActionErrors(async () => {
throw new ForbiddenError("risk:write" as Permission); throw new ForbiddenError("customer:write" as Permission);
}, ctx); }, ctx);
} catch (e) { } catch (e) {
outward = e instanceof Error ? e.message : String(e); outward = e instanceof Error ? e.message : String(e);
+2 -2
View File
@@ -21,7 +21,7 @@ const ok = (cond: boolean, msg: string) => {
const SLUG = "zz-f06-authz-test"; const SLUG = "zz-f06-authz-test";
const EMAIL = "f06-user@zz-authz.test"; const EMAIL = "f06-user@zz-authz.test";
const PERM = "asset:write"; // existiert im globalen Permissionskatalog const PERM = "customer:write"; // existiert im globalen Permissionskatalog
/** /**
* Repliziert die autoritative Prüfung aus moduleGuard (Option C): Membership-Status + * Repliziert die autoritative Prüfung aus moduleGuard (Option C): Membership-Status +
@@ -94,7 +94,7 @@ async function main() {
const a1 = await authorize(user.id, identity.id); const a1 = await authorize(user.id, identity.id);
ok(a1 !== null, "(1) aktives Konto wird gefunden"); ok(a1 !== null, "(1) aktives Konto wird gefunden");
ok(a1?.perms.has(PERM) === true, `(1) effektive Rechte enthalten ${PERM}`); ok(a1?.perms.has(PERM) === true, `(1) effektive Rechte enthalten ${PERM}`);
ok(a1?.perms.has("risk:accept") === false, "(1) nicht vergebenes Recht fehlt korrekt"); ok(a1?.perms.has("customer:merge") === false, "(1) nicht vergebenes Recht fehlt korrekt");
// (2) Membership deaktiviert → Query liefert null → Guard wirft „Konto ist nicht aktiv". // (2) Membership deaktiviert → Query liefert null → Guard wirft „Konto ist nicht aktiv".
await prisma.user.update({ where: { id: user.id }, data: { status: "DEACTIVATED" } }); await prisma.user.update({ where: { id: user.id }, data: { status: "DEACTIVATED" } });
-60
View File
@@ -1,60 +0,0 @@
// B1 Schritt 5/6 — ISO-Bewertung über buildAssessment("ISO_27001"). Prüft Scope (SoA-
// Anwendbarkeit + Klauseln 4–10), Umsetzungsgrad-Kennzahl, leere-SoA-Hinweis und dass
// KEIN TISAX-Vokabular (Reifegrad/AL) in der ISO-Zusammenfassung auftaucht.
//
// Lauf: npx tsx scripts/test-assessment-iso.ts
import "dotenv/config";
import { prisma, dbForTenant } from "../src/server/db";
import { buildAssessment } from "../src/server/assessment";
import { ensureSoaEntries } from "../src/server/soa-statement";
let failures = 0;
const ok = (c: boolean, m: string) => { console.log(`${c ? "✓" : "✗ FEHLER"} ${m}`); if (!c) failures++; };
const SLUG = "b1-test-iso";
async function cleanup() {
const t = await prisma.tenant.findUnique({ where: { slug: SLUG }, select: { id: true } });
if (!t) return;
await prisma.soaEntry.deleteMany({ where: { tenantId: t.id } });
await prisma.tenant.delete({ where: { id: t.id } });
}
async function main() {
await cleanup();
const tenant = await prisma.tenant.create({ data: { name: "B1 ISO", slug: SLUG } });
const t = tenant.id;
const db = dbForTenant(t);
// ── A) Leere SoA → „Anwendbarkeit offen", nicht 0 % ─────────────────────────
const empty = await buildAssessment(db, t, "ISO_27001");
ok(empty.summary.band === "Anwendbarkeit offen" && empty.summary.metricValue === "—", "leere SoA meldet Anwendbarkeit-noch-nicht-erklaert (nicht 0 Prozent)");
// ── B) SoA vorbefüllen → Bewertung rechnet ──────────────────────────────────
await ensureSoaEntries(db, t);
const asm = await buildAssessment(db, t, "ISO_27001");
const clauses = asm.rows.filter((r) => !r.control.startsWith("A."));
const annex = asm.rows.filter((r) => r.control.startsWith("A."));
ok(clauses.length >= 20, `Klauseln 4–10 immer im Scope (${clauses.length})`);
ok(annex.length > 0, `anwendbare Annex-A-Controls im Scope (${annex.length})`);
ok(asm.rows.every((r) => r.verdict.kind === "status"), "jede ISO-Bewertung ist ein Status-Verdict (kein Reifegrad)");
ok(asm.summary.metricLabel === "Umsetzungsgrad" && asm.summary.metricValue.endsWith("%"), `Kennzahl ist der Umsetzungsgrad (${asm.summary.metricValue})`);
// ── C) Einige anwendbare Controls „umgesetzt" → fulfilled steigt ────────────
const applicableAnnex = await prisma.soaEntry.findMany({ where: { tenantId: t, framework: "ISO_27001", applicable: true }, select: { id: true }, take: 5 });
await prisma.soaEntry.updateMany({ where: { id: { in: applicableAnnex.map((s) => s.id) } }, data: { implementationStatus: "umgesetzt" } });
const asm2 = await buildAssessment(db, t, "ISO_27001");
ok(asm2.summary.fulfilled >= 5, `bestätigte Umsetzungen zählen in den Umsetzungsgrad (${asm2.summary.fulfilled} umgesetzt)`);
// ── Kein TISAX-Vokabular in der ISO-Sicht ───────────────────────────────────
const vocab = `${asm2.summary.band} ${asm2.summary.metricLabel} ${asm2.summary.text}`;
ok(!/Reifegrad|AL2|AL3|Prüfziel|Assessment-reif/.test(vocab), "keine Reifegrad-/AL-/Prüfziel-Begriffe in der ISO-Zusammenfassung");
await cleanup();
console.log("\n✓ aufgeräumt (Wegwerf-Mandant entfernt)");
}
main()
.then(() => { console.log(failures === 0 ? "\nISO-Bewertung 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); })
.finally(() => prisma.$disconnect());
+6 -6
View File
@@ -30,7 +30,7 @@ async function cleanup() {
await prisma.tombstoneEntry.deleteMany({ where: { tenantId: t.id } }); await prisma.tombstoneEntry.deleteMany({ where: { tenantId: t.id } });
await prisma.deletionCertificate.deleteMany({ where: { tenantId: t.id } }); await prisma.deletionCertificate.deleteMany({ where: { tenantId: t.id } });
await prisma.auditLog.deleteMany({ where: { tenantId: t.id } }); await prisma.auditLog.deleteMany({ where: { tenantId: t.id } });
await prisma.asset.deleteMany({ where: { tenantId: t.id } }); await prisma.notificationPreference.deleteMany({ where: { tenantId: t.id } });
await prisma.userRole.deleteMany({ where: { user: { tenantId: t.id } } }); await prisma.userRole.deleteMany({ where: { user: { tenantId: t.id } } });
await prisma.user.deleteMany({ where: { tenantId: t.id } }); await prisma.user.deleteMany({ where: { tenantId: t.id } });
await prisma.role.deleteMany({ where: { tenantId: t.id } }); await prisma.role.deleteMany({ where: { tenantId: t.id } });
@@ -49,7 +49,7 @@ async function cleanup() {
async function main() { async function main() {
await cleanup(); await cleanup();
// Fixture: Mandant + Identity + Mitgliedschaft + Rolle + Asset + AuditLog. // Fixture: Mandant + Identity + Mitgliedschaft + Rolle + NotificationPreference + AuditLog.
const tenant = await prisma.tenant.create({ data: { name: "DSGVO Test", slug: SLUG } }); const tenant = await prisma.tenant.create({ data: { name: "DSGVO Test", slug: SLUG } });
const identity = await prisma.identity.create({ const identity = await prisma.identity.create({
data: { email: EMAIL, passwordHash: "x", status: "ACTIVE" }, data: { email: EMAIL, passwordHash: "x", status: "ACTIVE" },
@@ -59,11 +59,11 @@ async function main() {
}); });
const role = await prisma.role.create({ data: { tenantId: tenant.id, key: "member", name: "Member" } }); const role = await prisma.role.create({ data: { tenantId: tenant.id, key: "member", name: "Member" } });
await prisma.userRole.create({ data: { userId: user.id, roleId: role.id } }); await prisma.userRole.create({ data: { userId: user.id, roleId: role.id } });
await prisma.asset.create({ await prisma.notificationPreference.create({
data: { tenantId: tenant.id, name: "Server der Person", type: "SYSTEM", ownerId: user.id, createdBy: user.id }, data: { tenantId: tenant.id, userId: user.id, eventType: "work_order_assigned", email: false },
}); });
await prisma.auditLog.create({ await prisma.auditLog.create({
data: { tenantId: tenant.id, actorId: user.id, action: "create", entity: "asset" }, data: { tenantId: tenant.id, actorId: user.id, action: "create", entity: "user" },
}); });
// 1. Auskunft (Art. 15/20). // 1. Auskunft (Art. 15/20).
@@ -71,7 +71,7 @@ async function main() {
ok(subj.identity?.email === EMAIL, "Auskunft enthält Identity-Metadaten (E-Mail)"); ok(subj.identity?.email === EMAIL, "Auskunft enthält Identity-Metadaten (E-Mail)");
ok(!("passwordHash" in (subj.identity ?? {})), "Auskunft enthält KEINE Secrets (passwordHash)"); ok(!("passwordHash" in (subj.identity ?? {})), "Auskunft enthält KEINE Secrets (passwordHash)");
ok(subj.memberships.length === 1, "Auskunft enthält die Mitgliedschaft"); ok(subj.memberships.length === 1, "Auskunft enthält die Mitgliedschaft");
ok(!!subj.references["Asset.ownerId"], "Auskunft findet Asset über ownerId"); ok(!!subj.references["NotificationPreference.userId"], "Auskunft findet NotificationPreference über userId");
ok(!!subj.references["AuditLog.actorId"], "Auskunft findet AuditLog über actorId"); ok(!!subj.references["AuditLog.actorId"], "Auskunft findet AuditLog über actorId");
// Snapshot MIT Original-PII (für den Tombstone-on-Restore-Beweis). // Snapshot MIT Original-PII (für den Tombstone-on-Restore-Beweis).
+2 -2
View File
@@ -43,7 +43,7 @@ async function cleanup() {
await prisma.tombstoneEntry.deleteMany({ where: { tenantId: t.id } }); await prisma.tombstoneEntry.deleteMany({ where: { tenantId: t.id } });
await prisma.deletionCertificate.deleteMany({ where: { tenantId: t.id } }); await prisma.deletionCertificate.deleteMany({ where: { tenantId: t.id } });
await prisma.auditLog.deleteMany({ where: { tenantId: t.id } }); await prisma.auditLog.deleteMany({ where: { tenantId: t.id } });
await prisma.asset.deleteMany({ where: { tenantId: t.id } }); await prisma.notificationPreference.deleteMany({ where: { tenantId: t.id } });
await prisma.userRole.deleteMany({ where: { user: { tenantId: t.id } } }); await prisma.userRole.deleteMany({ where: { user: { tenantId: t.id } } });
await prisma.user.deleteMany({ where: { tenantId: t.id } }); await prisma.user.deleteMany({ where: { tenantId: t.id } });
await prisma.role.deleteMany({ where: { tenantId: t.id } }); await prisma.role.deleteMany({ where: { tenantId: t.id } });
@@ -109,7 +109,7 @@ async function main() {
const tenant = await prisma.tenant.create({ data: { name: "Portal Test", slug: SLUG } }); const tenant = await prisma.tenant.create({ data: { name: "Portal Test", slug: SLUG } });
const identity = await prisma.identity.create({ data: { email: EMAIL, passwordHash: "x", status: "ACTIVE" } }); const identity = await prisma.identity.create({ data: { email: EMAIL, passwordHash: "x", status: "ACTIVE" } });
const user = await prisma.user.create({ data: { tenantId: tenant.id, identityId: identity.id, email: EMAIL, name: "Portal Person" } }); const user = await prisma.user.create({ data: { tenantId: tenant.id, identityId: identity.id, email: EMAIL, name: "Portal Person" } });
await prisma.asset.create({ data: { tenantId: tenant.id, name: "Asset der Person", type: "SYSTEM", ownerId: user.id, createdBy: user.id } }); await prisma.notificationPreference.create({ data: { tenantId: tenant.id, userId: user.id, eventType: "work_order_assigned", email: false } });
// ── (4a) DSGVO-Paket (Per-Person) direkt ──────────────────────────────────── // ── (4a) DSGVO-Paket (Per-Person) direkt ────────────────────────────────────
console.log("\n(4) DSGVO-Zustellpaket:"); console.log("\n(4) DSGVO-Zustellpaket:");
-73
View File
@@ -1,73 +0,0 @@
// AP5 — Dokumentenlenkung: Prüfzyklus/-termin, Neuversion + Historie, Lesebestätigung
// je Version. Prüft die reine Zyklus-Logik und die Daten-Invarianten (Wegwerf-Mandant).
//
// Lauf: npx tsx scripts/test-doc-control.ts
import "dotenv/config";
import { prisma } from "../src/server/db";
import { computeNextReview, isReviewDue, bumpMinorVersion } from "../src/lib/review-cycle";
let failures = 0;
const ok = (c: boolean, m: string) => { console.log(`${c ? "✓" : "✗ FEHLER"} ${m}`); if (!c) failures++; };
const SLUG = "ap5-test-doc";
async function cleanup() {
const t = await prisma.tenant.findUnique({ where: { slug: SLUG }, select: { id: true } });
if (!t) return;
await prisma.policyAcknowledgement.deleteMany({ where: { tenantId: t.id } });
await prisma.policyDocumentVersion.deleteMany({ where: { tenantId: t.id } });
await prisma.policyDocument.deleteMany({ where: { tenantId: t.id } });
await prisma.tenant.delete({ where: { id: t.id } });
}
async function main() {
await cleanup();
// ── Reine Zyklus-Logik ──────────────────────────────────────────────────────
const from = new Date("2026-01-15T00:00:00Z");
const next = computeNextReview(from, "jährlich");
ok(next?.getUTCFullYear() === 2027 && next?.getUTCMonth() === 0, "computeNextReview: jährlich → +12 Monate");
ok(computeNextReview(from, null) === null, "kein Zyklus → kein Termin");
ok(isReviewDue(new Date("2020-01-01")) === true && isReviewDue(new Date("2999-01-01")) === false, "isReviewDue erkennt Überfälligkeit");
ok(bumpMinorVersion("1.0") === "1.1" && bumpMinorVersion("2.9") === "2.10" && bumpMinorVersion("3") === "3.1", "bumpMinorVersion korrekt");
// ── Daten-Invarianten ───────────────────────────────────────────────────────
const tenant = await prisma.tenant.create({ data: { name: "AP5", slug: SLUG } });
const t = tenant.id;
const doc = await prisma.policyDocument.create({
data: { tenantId: t, code: "R08", type: "RICHTLINIE", title: "Zugriffskontrolle", version: "1.0", rawMarkdown: "# Zugriffskontrolle" },
select: { id: true, version: true },
});
// Lesebestätigung v1.0 durch zwei Nutzer; Duplikat (gleicher Nutzer/Version) wird verhindert.
await prisma.policyAcknowledgement.create({ data: { tenantId: t, policyDocumentId: doc.id, userId: "user-1", version: "1.0" } });
await prisma.policyAcknowledgement.create({ data: { tenantId: t, policyDocumentId: doc.id, userId: "user-2", version: "1.0" } });
let dupBlocked = false;
try {
await prisma.policyAcknowledgement.create({ data: { tenantId: t, policyDocumentId: doc.id, userId: "user-1", version: "1.0" } });
} catch { dupBlocked = true; }
ok(dupBlocked, "Lesebestätigung je (Dokument, Nutzer, Version) nur einmal (Unique)");
ok((await prisma.policyAcknowledgement.count({ where: { policyDocumentId: doc.id, version: "1.0" } })) === 2, "2 Lesebestätigungen für v1.0");
// „Als geprüft markieren": Momentaufnahme + Minor-Bump.
await prisma.policyDocumentVersion.create({ data: { tenantId: t, policyDocumentId: doc.id, version: "1.0", title: "Zugriffskontrolle", changeNote: "Turnusmäßige Überprüfung", createdBy: "user-1" } });
const newVersion = bumpMinorVersion(doc.version);
await prisma.policyDocument.update({ where: { id: doc.id }, data: { version: newVersion, reviewCycle: "jährlich", nextReviewAt: computeNextReview(new Date(), "jährlich") } });
const after = await prisma.policyDocument.findUnique({ where: { id: doc.id }, include: { versionHistory: true } });
ok(after?.version === "1.1", "Neuversion nach Prüfung (v1.0 → v1.1)");
ok((after?.versionHistory.length ?? 0) >= 1, "Versionshistorie enthält die frühere Version (≥2 Versionen sichtbar: Historie + aktuell)");
// Version-Scoping: alte Bestätigungen (v1.0) zählen NICHT für v1.1.
ok((await prisma.policyAcknowledgement.count({ where: { policyDocumentId: doc.id, version: "1.1" } })) === 0, "Lesebestätigungen sind versions-scoped (v1.1 startet bei 0)");
// Prüftermin gesetzt.
ok(after?.reviewCycle === "jährlich" && !!after?.nextReviewAt, "Prüfzyklus + nächster Prüftermin gesetzt");
await cleanup();
console.log("\n✓ aufgeräumt (Wegwerf-Mandant entfernt)");
}
main()
.then(() => { console.log(failures === 0 ? "\nAP5-Dokumentenlenkung 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); });
-103
View File
@@ -1,103 +0,0 @@
// B1 Schritt 1 — Snapshot-Regressionsnetz der heutigen TISAX-Bewertung.
//
// Hält die Ausgabe von buildControlRows (Reifegradvorschlag, Zielwert, Belegstatus und
// offene Punkte je Control) für den Demo-Mandanten als JSON im Repo fest. JEDE spätere
// Abweichung (ControlSpec-Umbau, Strategie-Extraktion) ist eine Regression, kein
// „ist besser geworden" (Feindesign §7, §7a: Schritt 1 ist die wichtigste Maßnahme).
//
// Baseline erzeugen/aktualisieren: UPDATE_SNAPSHOT=1 npx tsx scripts/test-framework-assessment.ts
// Prüfen (Merge-Gate): npx tsx scripts/test-framework-assessment.ts
import "dotenv/config";
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { prisma, dbForTenant } from "../src/server/db";
import { buildControlRows } from "../src/server/soa-context";
import { buildAssessment } from "../src/server/assessment";
const SNAPSHOT = join(dirname(fileURLToPath(import.meta.url)), "snapshots", "framework-assessment-tisax.json");
/** Kanonisches JSON (rekursiv sortierte Objekt-Schlüssel) für stabilen Vergleich. */
function canonical(v: unknown): unknown {
if (Array.isArray(v)) return v.map(canonical);
if (v && typeof v === "object") {
const out: Record<string, unknown> = {};
for (const k of Object.keys(v as Record<string, unknown>).sort()) out[k] = canonical((v as Record<string, unknown>)[k]);
return out;
}
return v;
}
const stable = (v: unknown) => JSON.stringify(canonical(v), null, 2);
async function build() {
const tenant = await prisma.tenant.findFirst({ where: { slug: "demo" }, select: { id: true } });
if (!tenant) throw new Error("Demo-Mandant fehlt (prisma/seed.ts ausführen).");
const { rows, level } = await buildControlRows(dbForTenant(tenant.id), tenant.id);
// Nur die bewertungsrelevanten, deterministischen Felder je Control festhalten.
const controls = rows.map((r) => ({
control: r.control,
target: r.target,
confirmed: r.confirmed,
suggestion: r.suggestion,
evidence: r.evidence,
gaps: r.gaps,
}));
return { level, count: controls.length, controls };
}
/** §7.2: buildAssessment("TISAX") auf dieselbe Snapshot-Form abbilden. */
async function buildViaStrategy() {
const tenant = await prisma.tenant.findFirst({ where: { slug: "demo" }, select: { id: true } });
const { rows } = await buildAssessment(dbForTenant(tenant!.id), tenant!.id, "TISAX");
const controls = rows.map((r) => {
if (r.verdict.kind !== "maturity") throw new Error("TISAX-Verdict ist nicht 'maturity'.");
return { control: r.control, target: r.verdict.target, confirmed: r.verdict.confirmed, suggestion: r.verdict.suggestion, evidence: r.evidence, gaps: r.gaps };
});
return { level: "AL2", count: controls.length, controls };
}
async function main() {
const current = await build();
// Strategie-Äquivalenz (Feindesign §7.2): buildAssessment("TISAX") == buildControlRows.
const viaStrategy = await buildViaStrategy();
if (stable(viaStrategy.controls) !== stable(current.controls)) {
console.error("✗ buildAssessment(\"TISAX\") weicht von buildControlRows ab — die Strategie-Schicht verändert TISAX.");
process.exit(1);
}
if (process.env.UPDATE_SNAPSHOT === "1" || !existsSync(SNAPSHOT)) {
mkdirSync(dirname(SNAPSHOT), { recursive: true });
writeFileSync(SNAPSHOT, stable(current) + "\n", "utf-8");
console.log(`✓ Snapshot ${existsSync(SNAPSHOT) ? "aktualisiert" : "erstellt"}: ${current.count} Controls (Level ${current.level}).`);
console.log(" → Baseline committen; künftige Läufe prüfen gegen diesen Stand.");
return;
}
const expected = readFileSync(SNAPSHOT, "utf-8").trim();
const actual = stable(current).trim();
if (expected === actual) {
console.log(`✓ TISAX-Snapshot unverändert: ${current.count} Controls, Level ${current.level} — 0 Abweichungen.`);
process.exit(0);
}
// Erste abweichende Control-Zeile für die Diagnose zeigen.
const exp = JSON.parse(expected) as { controls: { control: string }[] };
const expByControl = new Map(exp.controls.map((c) => [c.control, stable(c)]));
const diffs: string[] = [];
for (const c of current.controls) {
const e = expByControl.get(c.control);
const a = stable(c);
if (e !== a) diffs.push(c.control);
}
const onlyExpected = exp.controls.map((c) => c.control).filter((id) => !current.controls.some((c) => c.control === id));
console.error(`✗ TISAX-Snapshot weicht ab. Betroffene Controls (${diffs.length}): ${diffs.slice(0, 20).join(", ")}`);
if (onlyExpected.length) console.error(` Fehlend gegenüber Baseline: ${onlyExpected.join(", ")}`);
console.error(" → Wenn die Änderung beabsichtigt ist: UPDATE_SNAPSHOT=1 erneut ausführen. Sonst Regression.");
process.exit(1);
}
main()
.catch((e) => { console.error(e); process.exit(1); })
.finally(() => prisma.$disconnect());
-93
View File
@@ -1,93 +0,0 @@
// AP1 — Framework-Dimension: Kern-Invarianten (docs/UEBERGABE-framework-iso27001.md §1).
//
// Prüft gegen die lokale DB (Demo-Mandant):
// 1. Backfill: jeder Mandant hat ≥1 TenantFramework; Bestands-Anforderungen sind TISAX;
// getTenantFrameworks(demo) beginnt mit TISAX; Fallback ["TISAX"] ohne Zeile.
// 2. Beide Mappings parsen: mapping.json (VDA ISA) und mapping-iso.json (ISO) — gemeinsamer
// Dokumentensatz, unterschiedliche Anforderungen.
// 3. Doppel-Framework-Reconcile: ISO-Import in einen TISAX-Mandanten legt die ISO-
// Anforderungen an und archiviert KEINE TISAX-Anforderung (Falle 1.1); Dokumente
// werden nicht dupliziert (reconcileShared).
// 4. Rück-Reconcile TISAX archiviert die ISO-Anforderungen NICHT.
// Räumt die ISO-Testdaten am Ende wieder ab.
//
// Lauf: npx tsx scripts/test-framework-core.ts
import "dotenv/config";
import { join } from "node:path";
import { prisma } from "../src/server/db";
import { parsePackageFiles, reconcilePackage } from "../prisma/import-policies";
import { getTenantFrameworks, getTenantPrimaryFramework } from "../prisma/template-store";
const SEED_DIR = join(process.cwd(), "seed", "isms-vorlagenpaket-v2");
let failures = 0;
const ok = (c: boolean, m: string) => { console.log(`${c ? "✓" : "✗ FEHLER"} ${m}`); if (!c) failures++; };
const activeReqs = (tenantId: string, framework: "TISAX" | "ISO_27001") =>
prisma.policyRequirement.count({ where: { tenantId, framework, archivedAt: null } });
const archivedReqs = (tenantId: string, framework: "TISAX" | "ISO_27001") =>
prisma.policyRequirement.count({ where: { tenantId, framework, archivedAt: { not: null } } });
async function main() {
const tenant = await prisma.tenant.findFirst({ where: { slug: "demo" } });
if (!tenant) throw new Error("Demo-Mandant fehlt (prisma/seed.ts ausführen).");
const t = tenant.id;
// ── 1. Backfill / Framework-Zuordnung ───────────────────────────────────────
const allTenants = await prisma.tenant.count();
const tenantsWithFw = await prisma.tenantFramework.groupBy({ by: ["tenantId"] });
ok(tenantsWithFw.length === allTenants, `jeder Mandant hat ≥1 TenantFramework (${tenantsWithFw.length}/${allTenants})`);
const demoFw = await getTenantFrameworks(prisma, t);
ok(demoFw[0] === "TISAX", "getTenantFrameworks(demo) beginnt mit TISAX");
ok((await getTenantPrimaryFramework(prisma, t)) === "TISAX", "Primär-Framework = TISAX");
ok((await getTenantFrameworks(prisma, "does-not-exist"))[0] === "TISAX", "Fallback ohne Zeile → [\"TISAX\"]");
const isoBaseline = await prisma.policyRequirement.count({ where: { tenantId: t, framework: "ISO_27001" } });
ok(isoBaseline === 0, "Demo-Mandant hat vor dem Test keine ISO-Anforderungen");
// ── 2. Beide Mappings parsen ────────────────────────────────────────────────
const tisaxPkg = parsePackageFiles(SEED_DIR, "mapping.json");
const isoPkg = parsePackageFiles(SEED_DIR, "mapping-iso.json");
ok(tisaxPkg.requirements.length > 300, `mapping.json: ${tisaxPkg.requirements.length} Anforderungen (VDA ISA)`);
ok(isoPkg.requirements.length === 120, `mapping-iso.json: ${isoPkg.requirements.length} Anforderungen (erwartet 120)`);
ok(tisaxPkg.documents.length === isoPkg.documents.length, "gemeinsamer Dokumentensatz (gleiche Dokumentanzahl)");
// ── 3. Doppel-Framework: ISO in TISAX-Mandant ───────────────────────────────
const tisaxActiveBefore = await activeReqs(t, "TISAX");
const tisaxArchivedBefore = await archivedReqs(t, "TISAX");
const docsBefore = await prisma.policyDocument.count({ where: { tenantId: t } });
const isoRun = await reconcilePackage(prisma, t, isoPkg, { framework: "ISO_27001", reconcileShared: false });
ok(isoRun.report.requirements.added === isoPkg.requirements.length,
`ISO-Import legt ${isoRun.report.requirements.added} Anforderungen an (= ${isoPkg.requirements.length})`);
ok(isoRun.report.documents.added === 0 && isoRun.report.documents.updated === 0,
"ISO-Import fasst die geteilten Dokumente NICHT an (reconcileShared=false)");
const isoActive = await activeReqs(t, "ISO_27001");
ok(isoActive === isoPkg.requirements.length, `ISO-Anforderungen aktiv: ${isoActive}`);
const tisaxActiveAfter = await activeReqs(t, "TISAX");
const tisaxArchivedAfter = await archivedReqs(t, "TISAX");
ok(tisaxActiveAfter === tisaxActiveBefore, `KEINE TISAX-Anforderung archiviert (aktiv ${tisaxActiveBefore} → ${tisaxActiveAfter})`);
ok(tisaxArchivedAfter === tisaxArchivedBefore, "ISO-Import erzeugt keine neu archivierten TISAX-Anforderungen (Falle 1.1)");
const docsAfter = await prisma.policyDocument.count({ where: { tenantId: t } });
ok(docsAfter === docsBefore, `Dokumente nicht dupliziert (${docsBefore} → ${docsAfter})`);
const coexist = tisaxActiveAfter + isoActive;
ok(tisaxActiveAfter > 0 && isoActive === 120, `Doppel-Framework: ${tisaxActiveAfter} TISAX + ${isoActive} ISO koexistieren (${coexist})`);
// ── 4. Rück-Reconcile TISAX archiviert ISO NICHT ────────────────────────────
await reconcilePackage(prisma, t, tisaxPkg, { framework: "TISAX", reconcileShared: true });
const isoStillActive = await activeReqs(t, "ISO_27001");
ok(isoStillActive === isoPkg.requirements.length, "TISAX-Re-Import archiviert die ISO-Anforderungen NICHT");
// ── Aufräumen: ISO-Testdaten entfernen (Demo-Mandant zurücksetzen) ──────────
const removed = await prisma.policyRequirement.deleteMany({ where: { tenantId: t, framework: "ISO_27001" } });
console.log(`\n✓ aufgeräumt (${removed.count} ISO-Test-Anforderungen entfernt)`);
}
main()
.then(() => { console.log(failures === 0 ? "\nAP1-Framework-Kern grün." : `\n${failures} Prüfung(en) fehlgeschlagen.`); process.exit(failures === 0 ? 0 : 1); })
.catch((e) => { console.error(e); process.exit(1); });
-118
View File
@@ -1,118 +0,0 @@
// Abnahmetest der Mehr-Framework-Fähigkeit (ISO 27001 neben TISAX) — reiner Trockenlauf.
//
// Prüft die Zusagen aus docs/UEBERGABE-framework-iso27001.md, ohne etwas zu schreiben:
// 1. Paketebene: beide Mappings lesen denselben Dokumentensatz; kein ISO-Umsetzungstext
// ist leer; die Anforderungs-IDs beider Frameworks überschneiden sich nicht.
// 2. Falle 1.1: Ein ISO-Import in einen Mandanten mit TISAX archiviert KEINE
// TISAX-Anforderung (`report.requirements.archived === 0`).
// 3. Geteilte Inhalte (Dokumente, Variablen, Baseline, Nachweisregister) werden bei einem
// Mandanten, der das Paket bereits hat, nicht doppelt angelegt.
// 4. Der Trockenlauf hinterlässt keine Spuren: Zählstände vor und nach dem Lauf identisch.
//
// Lauf: npx tsx scripts/test-framework-dryrun.ts
// Nutzt die lokale Postgres-DB; .env liegt im Worktree. Ohne Mandanten läuft nur Teil 1.
import "dotenv/config";
import { join } from "node:path";
import { prisma } from "../src/server/db";
import { parsePackageFiles, reconcilePackage } from "../prisma/import-policies";
const SEED = join(process.cwd(), "seed", "isms-vorlagenpaket-v2");
let failed = 0;
function check(ok: boolean, label: string, detail = "") {
if (!ok) failed++;
console.log(` ${ok ? "✓" : "✗"} ${label}${detail ? " — " + detail : ""}`);
}
async function counts(tenantId: string) {
return {
tisax: await prisma.policyRequirement.count({ where: { tenantId, framework: "TISAX", archivedAt: null } }),
tisaxArch: await prisma.policyRequirement.count({ where: { tenantId, framework: "TISAX", archivedAt: { not: null } } }),
iso: await prisma.policyRequirement.count({ where: { tenantId, framework: "ISO_27001", archivedAt: null } }),
docs: await prisma.policyDocument.count({ where: { tenantId, archivedAt: null } }),
};
}
async function main() {
console.log("1. Paketebene");
const iso = parsePackageFiles(SEED, "mapping-iso.json");
const tis = parsePackageFiles(SEED, "mapping.json");
const isoCodes = iso.documents.map((d) => d.code).sort();
const tisCodes = tis.documents.map((d) => d.code).sort();
check(JSON.stringify(isoCodes) === JSON.stringify(tisCodes),
"beide Mappings lesen denselben Dokumentensatz", `${isoCodes.length} Dokumente`);
check(iso.requirements.length === 120,
"ISO-Mapping enthält 120 Anforderungen", `${iso.requirements.length}`);
check(iso.requirements.filter((r) => !r.implementation.trim()).length === 0,
"kein ISO-Umsetzungstext ist leer");
check(iso.requirements.every((r) => !!r.control),
"jede ISO-Anforderung trägt eine Control-Referenz");
const overlap = iso.requirements.map((r) => r.reqId).filter((id) => tis.requirements.some((t) => t.reqId === id));
check(overlap.length === 0, "Anforderungs-IDs beider Frameworks überschneiden sich nicht",
overlap.length ? overlap.slice(0, 3).join(", ") : "");
// Englische Fassung: gleiche Struktur, übersetzte Texte.
const SEED_EN = join(process.cwd(), "seed", "isms-vorlagenpaket-v2-en");
const isoEn = parsePackageFiles(SEED_EN, "mapping-iso.json");
check(isoEn.requirements.length === iso.requirements.length,
"EN-Fassung enthält gleich viele ISO-Anforderungen", `${isoEn.requirements.length}`);
check(JSON.stringify(isoEn.requirements.map((r) => r.reqId).sort())
=== JSON.stringify(iso.requirements.map((r) => r.reqId).sort()),
"EN- und DE-Fassung haben identische Anforderungs-IDs");
check(isoEn.requirements.filter((r) => !r.implementation.trim()).length === 0,
"kein EN-Umsetzungstext ist leer");
check(isoEn.documents.length === iso.documents.length,
"EN-Fassung hat denselben Dokumentenumfang", `${isoEn.documents.length}`);
const tenants = await prisma.tenant.findMany({ select: { id: true, name: true, slug: true } });
if (!tenants.length) {
console.log("\n(keine Mandanten in der DB — Teil 2 übersprungen)");
return;
}
for (const t of tenants) {
console.log(`\n2. Trockenlauf ISO_27001 gegen „${t.name}" (${t.slug})`);
const before = await counts(t.id);
console.log(` vorher: TISAX=${before.tisax} (archiviert ${before.tisaxArch}) · ISO=${before.iso} · Dokumente=${before.docs}`);
const { report } = await reconcilePackage(prisma, t.id, iso, { dryRun: true, framework: "ISO_27001" });
const r = report.requirements, d = report.documents;
console.log(` Anforderungen: +${r.added} ~${r.updated} =${r.unchanged} archiviert=${r.archived}`);
console.log(` Dokumente: +${d.added} ~${d.updated} =${d.unchanged} reaktiviert=${d.reactivated} archiviert=${d.archived}`);
check(r.archived === 0, "Falle 1.1 — keine fremde Anforderung archiviert", `archived=${r.archived}`);
check(r.added + r.unchanged + r.updated === 120, "alle 120 ISO-Anforderungen erfasst");
check(d.archived === 0, "kein geteiltes Dokument wird stillgelegt", `archived=${d.archived}`);
check(report.variables.obsolete === 0 && report.baseline.obsolete === 0 && report.evidence.obsolete === 0,
"geteilte Inhalte verlieren nichts (Variablen, Baseline, Nachweisregister)");
// Mandant hat das Paket bereits: jedes vorhandene Paket-Dokument muss wiedererkannt
// werden; angelegt wird nur, was im Mandanten wirklich fehlt.
const vorhanden = await prisma.policyDocument.findMany({
where: { tenantId: t.id, code: { in: iso.documents.map((x) => x.code) } },
select: { code: true },
});
// Ein vorhandenes Dokument zählt je nach Zustand als unchanged, updated ODER
// reactivated (wenn es beim Mandanten stillgelegt war) — alle drei sind „wiedererkannt".
const wiedererkannt = d.unchanged + d.updated + d.reactivated;
check(wiedererkannt === vorhanden.length,
"jedes vorhandene Paket-Dokument wird wiedererkannt",
`vorhanden=${vorhanden.length} unchanged=${d.unchanged} updated=${d.updated} reaktiviert=${d.reactivated}`);
check(d.added === iso.documents.length - vorhanden.length,
"angelegt wird nur, was im Mandanten fehlt",
`added=${d.added} erwartet=${iso.documents.length - vorhanden.length}`);
const after = await counts(t.id);
check(JSON.stringify(before) === JSON.stringify(after),
"Trockenlauf hat nichts geschrieben", JSON.stringify(after));
}
}
main()
.catch((e) => { console.error(e); failed++; })
.finally(async () => {
await prisma.$disconnect();
console.log(`\n${failed === 0 ? "OK — alle Prüfungen bestanden" : `FEHLGESCHLAGEN — ${failed} Prüfung(en)`}`);
process.exit(failed === 0 ? 0 : 1);
});
-106
View File
@@ -1,106 +0,0 @@
// AP2 — Provisionierung & Sichtbarkeits-Flags (docs/UEBERGABE-framework-iso27001.md §2).
//
// Provisioniert je einen Mandanten als reines TISAX, reines ISO und Doppel-Framework
// und prüft:
// - TenantFramework-Zeilen (Primär zuerst),
// - Import je Framework (Anforderungszahlen, keine Cross-Archivierung, Dokumente
// nicht dupliziert),
// - FLAG_FW_TISAX / FLAG_FW_ISO27001 NACH dem Import korrekt gesetzt,
// - AL-/Schutzbedarf-Flags nur bei TISAX (reiner ISO-Mandant: nicht gesetzt).
// Räumt die Test-Mandanten (inkl. Identitäten) vor und nach dem Lauf ab.
//
// Lauf: npx tsx scripts/test-framework-provision.ts
import "dotenv/config";
import { join } from "node:path";
import { prisma } from "../src/server/db";
import { provisionTenant } from "../src/server/provision";
const SEED_DIR = join(process.cwd(), "seed", "isms-vorlagenpaket-v2");
let failures = 0;
const ok = (c: boolean, m: string) => { console.log(`${c ? "✓" : "✗ FEHLER"} ${m}`); if (!c) failures++; };
const T = {
tisax: { slug: "ap2-test-tisax", email: "admin@ap2-test-tisax.example" },
iso: { slug: "ap2-test-iso", email: "admin@ap2-test-iso.example" },
dual: { slug: "ap2-test-dual", email: "admin@ap2-test-dual.example" },
};
const SLUGS = Object.values(T).map((t) => t.slug);
const EMAILS = Object.values(T).map((t) => t.email);
async function cleanup() {
const tenants = await prisma.tenant.findMany({ where: { slug: { in: SLUGS } }, select: { id: true } });
const ids = tenants.map((t) => t.id);
if (ids.length) {
// FK-sichere Reihenfolge: Verknüpfungen → Kinder → Stamm.
await prisma.userRole.deleteMany({ where: { user: { tenantId: { in: ids } } } });
await prisma.rolePermission.deleteMany({ where: { role: { tenantId: { in: ids } } } });
await prisma.user.deleteMany({ where: { tenantId: { in: ids } } });
await prisma.role.deleteMany({ where: { tenantId: { in: ids } } });
for (const model of [
"policyRequirement", "policyVariable", "policyDocument", "policyBaselineParam",
"policyEvidence", "policyPackageState", "tenantFramework", "tenantModule",
"managedRegister", "auditLog",
] as const) {
// @ts-expect-error dynamischer Modellzugriff (alle tragen tenantId)
await prisma[model].deleteMany({ where: { tenantId: { in: ids } } });
}
await prisma.tenantSettings.deleteMany({ where: { tenantId: { in: ids } } });
await prisma.tenant.deleteMany({ where: { id: { in: ids } } });
}
await prisma.identity.deleteMany({ where: { email: { in: EMAILS } } });
}
const flag = async (tenantId: string, key: string) =>
(await prisma.policyVariable.findFirst({ where: { tenantId, key }, select: { value: true } }))?.value ?? null;
const reqCount = (tenantId: string, framework: "TISAX" | "ISO_27001") =>
prisma.policyRequirement.count({ where: { tenantId, framework, archivedAt: null } });
async function provision(slug: string, email: string, frameworks: ("TISAX" | "ISO_27001")[]) {
return provisionTenant(prisma, {
name: slug, slug, admin: { email, name: "AP2 Test", password: "Str0ng-Passw0rt!" },
frameworks, seedPoliciesDir: SEED_DIR, actorId: null,
});
}
async function main() {
await cleanup();
// ── Reines TISAX ────────────────────────────────────────────────────────────
const tisax = await provision(T.tisax.slug, T.tisax.email, ["TISAX"]);
const tisaxFw = await prisma.tenantFramework.findMany({ where: { tenantId: tisax.id }, select: { framework: true, isPrimary: true } });
ok(tisaxFw.length === 1 && tisaxFw[0].framework === "TISAX" && tisaxFw[0].isPrimary, "TISAX-Mandant: genau eine TenantFramework-Zeile (TISAX, primär)");
ok((await reqCount(tisax.id, "TISAX")) === 321 && (await reqCount(tisax.id, "ISO_27001")) === 0, "TISAX-Mandant: 321 TISAX-, 0 ISO-Anforderungen");
ok((await flag(tisax.id, "FLAG_FW_TISAX")) === "true" && (await flag(tisax.id, "FLAG_FW_ISO27001")) === "false", "TISAX-Mandant: FLAG_FW_TISAX=true, FLAG_FW_ISO27001=false");
ok((await flag(tisax.id, "FLAG_HIGH_PROTECTION")) === "true", "TISAX-Mandant: AL-Flag FLAG_HIGH_PROTECTION gesetzt");
// ── Reines ISO ──────────────────────────────────────────────────────────────
const iso = await provision(T.iso.slug, T.iso.email, ["ISO_27001"]);
const isoFw = await prisma.tenantFramework.findMany({ where: { tenantId: iso.id }, select: { framework: true, isPrimary: true } });
ok(isoFw.length === 1 && isoFw[0].framework === "ISO_27001" && isoFw[0].isPrimary, "ISO-Mandant: genau eine TenantFramework-Zeile (ISO, primär)");
ok((await reqCount(iso.id, "ISO_27001")) === 120 && (await reqCount(iso.id, "TISAX")) === 0, "ISO-Mandant: 120 ISO-, 0 TISAX-Anforderungen");
ok((await flag(iso.id, "FLAG_FW_ISO27001")) === "true" && (await flag(iso.id, "FLAG_FW_TISAX")) === "false", "ISO-Mandant: FLAG_FW_ISO27001=true, FLAG_FW_TISAX=false");
ok((await flag(iso.id, "FLAG_HIGH_PROTECTION")) === "false", "ISO-Mandant: KEIN AL-Flag gesetzt (Default false, Übergabe §1.3)");
const isoDocs = await prisma.policyDocument.count({ where: { tenantId: iso.id } });
const isoDocCodes = (await prisma.policyDocument.findMany({ where: { tenantId: iso.id }, select: { code: true } })).map((d) => d.code);
ok(isoDocs === new Set(isoDocCodes).size, "ISO-Mandant: Dokumente nicht dupliziert (eindeutige Codes)");
// ── Doppel-Framework ─────────────────────────────────────────────────────────
const dual = await provision(T.dual.slug, T.dual.email, ["TISAX", "ISO_27001"]);
const dualFw = await prisma.tenantFramework.findMany({ where: { tenantId: dual.id }, orderBy: { isPrimary: "desc" }, select: { framework: true, isPrimary: true } });
ok(dualFw.length === 2 && dualFw[0].framework === "TISAX" && dualFw[0].isPrimary && !dualFw[1].isPrimary, "Doppel-Mandant: zwei Zeilen, TISAX primär");
const dTisax = await reqCount(dual.id, "TISAX");
const dIso = await reqCount(dual.id, "ISO_27001");
ok(dTisax === 321 && dIso === 120, `Doppel-Mandant: 321 TISAX + 120 ISO koexistieren (${dTisax}+${dIso})`);
ok((await flag(dual.id, "FLAG_FW_TISAX")) === "true" && (await flag(dual.id, "FLAG_FW_ISO27001")) === "true", "Doppel-Mandant: beide FLAG_FW_* = true");
const dualDocs = await prisma.policyDocument.count({ where: { tenantId: dual.id } });
const dualCodes = (await prisma.policyDocument.findMany({ where: { tenantId: dual.id }, select: { code: true } })).map((d) => d.code);
ok(dualDocs === new Set(dualCodes).size, `Doppel-Mandant: Dokumente NICHT dupliziert (${dualDocs} eindeutige Codes)`);
await cleanup();
console.log("\n✓ aufgeräumt (Test-Mandanten + Identitäten entfernt)");
}
main()
.then(() => { console.log(failures === 0 ? "\nAP2-Provisionierung 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); });
-62
View File
@@ -1,62 +0,0 @@
// Vorlagen-Versionierung je Framework (Falle 1.2, docs/UEBERGABE-framework-iso27001.md §1).
//
// Sichert die Daten-Invariante, auf der der framework-fähige Vorlagen-Editor
// (policy-templates.ts) aufsetzt — ohne die session-geschützten Actions aufzurufen:
// 1. Dieselbe Versionsnummer darf je Framework existieren (@@unique([framework, version])):
// (TISAX, "99.1") UND (ISO_27001, "99.1") koexistieren.
// 2. Das Publish-Archivieren ist framework-scoped: ein `updateMany` mit
// `framework=ISO_27001` archiviert NUR die ISO-Version, die TISAX-Version bleibt
// PUBLISHED (sonst archivierte ein ISO-Publish die TISAX-Vorlage und umgekehrt).
// Nutzt eindeutige Wegwerf-Versionen ("99.1") und räumt sie wieder ab.
//
// Lauf: npx tsx scripts/test-framework-templates.ts
import "dotenv/config";
import { prisma } from "../src/server/db";
let failures = 0;
const ok = (c: boolean, m: string) => { console.log(`${c ? "✓" : "✗ FEHLER"} ${m}`); if (!c) failures++; };
const V = "99.1"; // Wegwerf-Version, kollidiert nicht mit echten Paketständen
async function cleanup() {
await prisma.policyTemplateVersion.deleteMany({ where: { version: V } });
}
async function main() {
await cleanup();
// ── 1. Gleiche Version je Framework koexistiert (Unique-Constraint) ──────────
const tisax = await prisma.policyTemplateVersion.create({ data: { framework: "TISAX", version: V, status: "PUBLISHED", publishedAt: new Date() } });
let isoCreated = false;
try {
await prisma.policyTemplateVersion.create({ data: { framework: "ISO_27001", version: V, status: "PUBLISHED", publishedAt: new Date() } });
isoCreated = true;
} catch {
isoCreated = false;
}
ok(isoCreated, "gleiche Versionsnummer je Framework erlaubt (@@unique([framework, version]))");
const both = await prisma.policyTemplateVersion.findMany({ where: { version: V }, select: { framework: true } });
ok(both.length === 2, "beide Versionen (TISAX + ISO) existieren nebeneinander");
// ── 2. Framework-scopes Archivieren (wie publishDraft) ──────────────────────
const ids = (await prisma.policyTemplateVersion.findMany({ where: { version: V }, select: { id: true } })).map((r) => r.id);
// Repliziert die Publish-Bedingung `where { status: PUBLISHED, framework }` — zusätzlich
// auf die Testzeilen eingegrenzt, um echte Paketstände nicht anzufassen.
await prisma.policyTemplateVersion.updateMany({
where: { status: "PUBLISHED", framework: "ISO_27001", id: { in: ids } },
data: { status: "ARCHIVED" },
});
const tisaxAfter = await prisma.policyTemplateVersion.findFirst({ where: { framework: "TISAX", version: V }, select: { status: true } });
const isoAfter = await prisma.policyTemplateVersion.findFirst({ where: { framework: "ISO_27001", version: V }, select: { status: true } });
ok(tisaxAfter?.status === "PUBLISHED", "TISAX-Version bleibt PUBLISHED, wenn ISO archiviert wird");
ok(isoAfter?.status === "ARCHIVED", "ISO-Version wird archiviert (framework-scoped)");
void tisax;
await cleanup();
console.log("\n✓ aufgeräumt (Wegwerf-Versionen entfernt)");
}
main()
.then(() => { console.log(failures === 0 ? "\nVorlagen-Versionierung je Framework 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); });
-150
View File
@@ -1,150 +0,0 @@
// Test der nachträglichen Framework-Umschaltung (Admin: setTenantFrameworks).
//
// Spiegelt die Datenoperationen der Server-Action gegen die lokale DB und stellt den
// Ausgangszustand danach wieder her. Der Auth-Guard (`requirePlatformFullAdmin`) wird
// dabei NICHT durchlaufen — er ist identisch zu den sechs übrigen Admin-Aktionen.
//
// Geprüft wird:
// 1. Aktivieren — Framework-Zeile entsteht, 120 ISO-Anforderungen werden angelegt,
// die 321 TISAX-Anforderungen bleiben unangetastet, Flags stehen richtig.
// 2. Deaktivieren — Zugehörigkeit fällt weg, ISO-Anforderungen werden STILLGELEGT
// (archivedAt), nicht gelöscht; SoA-Einträge bleiben vollständig erhalten.
// 3. Wiederherstellung — der Mandant steht am Ende exakt wie vorher.
//
// Lauf: npx tsx scripts/test-framework-toggle.ts
// Nutzt die lokale Postgres-DB; .env liegt im Worktree.
import "dotenv/config";
import { join } from "node:path";
import type { Framework } from "@prisma/client";
import { prisma, dbForTenant } from "../src/server/db";
import { reconcilePackage, stampPackageVersion } from "../prisma/import-policies";
import { resolvePackageForTenant, getTenantFrameworks } from "../prisma/template-store";
const SEED = join(process.cwd(), "seed", "isms-vorlagenpaket-v2");
let failed = 0;
function check(ok: boolean, label: string, detail = "") {
if (!ok) failed++;
console.log(` ${ok ? "✓" : "✗"} ${label}${detail ? " — " + detail : ""}`);
}
async function state(tenantId: string) {
const db = dbForTenant(tenantId);
const [fw, tisax, tisaxArch, iso, isoArch, soa, flags] = await Promise.all([
getTenantFrameworks(prisma, tenantId),
db.policyRequirement.count({ where: { framework: "TISAX", archivedAt: null } }),
db.policyRequirement.count({ where: { framework: "TISAX", archivedAt: { not: null } } }),
db.policyRequirement.count({ where: { framework: "ISO_27001", archivedAt: null } }),
db.policyRequirement.count({ where: { framework: "ISO_27001", archivedAt: { not: null } } }),
db.soaEntry.count(),
db.policyVariable.findMany({
where: { key: { in: ["FLAG_FW_TISAX", "FLAG_FW_ISO27001"] } },
select: { key: true, value: true }, orderBy: { key: "asc" },
}),
]);
return { fw, tisax, tisaxArch, iso, isoArch, soa, flags: flags.map((f) => `${f.key}=${f.value}`).join(" ") };
}
/** Datenoperationen aus `setTenantFrameworks` (ohne Auth-Guard und revalidatePath). */
async function apply(tenantId: string, wanted: Framework[]) {
const current = await getTenantFrameworks(prisma, tenantId);
const added = wanted.filter((f) => !current.includes(f));
const removed = current.filter((f) => !wanted.includes(f));
const db = dbForTenant(tenantId);
for (const [i, framework] of wanted.entries()) {
await prisma.tenantFramework.upsert({
where: { tenantId_framework: { tenantId, framework } },
update: { isPrimary: i === 0 },
create: { tenantId, framework, isPrimary: i === 0 },
});
}
for (const [i, framework] of added.entries()) {
const { pkg } = await resolvePackageForTenant(prisma, tenantId, SEED, framework);
await reconcilePackage(prisma, tenantId, pkg, { framework, reconcileShared: i === 0 });
await stampPackageVersion(prisma, tenantId, pkg.version, framework);
}
if (removed.length > 0) {
await prisma.tenantFramework.deleteMany({ where: { tenantId, framework: { in: removed } } });
await db.policyRequirement.updateMany({
where: { framework: { in: removed }, archivedAt: null },
data: { archivedAt: new Date() },
});
}
await db.policyVariable.updateMany({ where: { key: "FLAG_FW_TISAX" }, data: { value: String(wanted.includes("TISAX")) } });
await db.policyVariable.updateMany({ where: { key: "FLAG_FW_ISO27001" }, data: { value: String(wanted.includes("ISO_27001")) } });
}
async function main() {
const tenant = await prisma.tenant.findFirst({ where: { slug: "demo" }, select: { id: true, name: true } });
if (!tenant) { console.log("Mandant demo nicht gefunden — Test uebersprungen."); return; }
const before = await state(tenant.id);
console.log(`Mandant: ${tenant.name}`);
console.log(` vorher: ${before.fw.join("+")} · TISAX=${before.tisax}/${before.tisaxArch} · ISO=${before.iso}/${before.isoArch} · SoA=${before.soa} · ${before.flags}`);
try {
console.log("\n1. ISO aktivieren");
await apply(tenant.id, [...before.fw, "ISO_27001"] as Framework[]);
const on = await state(tenant.id);
console.log(` ${on.fw.join("+")} · TISAX=${on.tisax}/${on.tisaxArch} · ISO=${on.iso}/${on.isoArch} · ${on.flags}`);
check(on.fw.includes("ISO_27001"), "Framework-Zugehörigkeit angelegt");
check(on.iso === 120, "120 ISO-Anforderungen aktiv", `${on.iso}`);
check(on.tisax === before.tisax && on.tisaxArch === before.tisaxArch,
"TISAX-Anforderungen unangetastet", `${on.tisax} aktiv / ${on.tisaxArch} archiviert`);
check(on.flags.includes("FLAG_FW_ISO27001=true") && on.flags.includes("FLAG_FW_TISAX=true"),
"Sichtbarkeits-Flags gesetzt", on.flags);
// Gepflegte SoA-Inhalte anlegen — sonst liefe die Erhaltungsprüfung unten über 0 → 0.
const db0 = dbForTenant(tenant.id);
for (const control of ["A.5.15", "A.8.5", "A.7.7"]) {
await db0.soaEntry.upsert({
where: { tenantId_framework_control: { tenantId: tenant.id, framework: "ISO_27001", control } },
update: { justification: "Testbegruendung", implementationStatus: "umgesetzt" },
create: {
tenantId: tenant.id, framework: "ISO_27001", control,
justification: "Testbegruendung", implementationStatus: "umgesetzt", applicable: true,
},
});
}
const gepflegt = await db0.soaEntry.count({ where: { justification: "Testbegruendung" } });
check(gepflegt === 3, "SoA-Einträge zum Test gepflegt", `${gepflegt}`);
console.log("\n2. ISO wieder deaktivieren");
await apply(tenant.id, before.fw as Framework[]);
const off = await state(tenant.id);
console.log(` ${off.fw.join("+")} · TISAX=${off.tisax}/${off.tisaxArch} · ISO=${off.iso}/${off.isoArch} · SoA=${off.soa} · ${off.flags}`);
check(!off.fw.includes("ISO_27001"), "Zugehörigkeit entfernt");
check(off.iso === 0 && off.isoArch === 120,
"ISO-Anforderungen stillgelegt statt gelöscht", `aktiv=${off.iso} archiviert=${off.isoArch}`);
check(off.tisax === before.tisax, "TISAX weiterhin unangetastet", `${off.tisax}`);
const ueberlebt = await dbForTenant(tenant.id).soaEntry.count({ where: { justification: "Testbegruendung" } });
check(ueberlebt === 3, "gepflegte SoA-Begründungen überleben das Deaktivieren unverändert", `${ueberlebt} von 3`);
check(off.flags.includes("FLAG_FW_ISO27001=false"), "Flag zurückgesetzt", off.flags);
console.log("\n3. Erneut aktivieren — stillgelegte Anforderungen reaktivieren");
await apply(tenant.id, [...before.fw, "ISO_27001"] as Framework[]);
const again = await state(tenant.id);
check(again.iso === 120 && again.isoArch === 0,
"Re-Import reaktiviert statt zu duplizieren", `aktiv=${again.iso} archiviert=${again.isoArch}`);
} finally {
console.log("\n4. Ausgangszustand wiederherstellen");
await apply(tenant.id, before.fw as Framework[]);
const db = dbForTenant(tenant.id);
await db.policyRequirement.deleteMany({ where: { framework: "ISO_27001" } });
await db.soaEntry.deleteMany({ where: { framework: "ISO_27001" } });
const end = await state(tenant.id);
console.log(` ${end.fw.join("+")} · TISAX=${end.tisax}/${end.tisaxArch} · ISO=${end.iso}/${end.isoArch} · ${end.flags}`);
check(JSON.stringify(end) === JSON.stringify(before), "Mandant steht wie vorher",
JSON.stringify(end) === JSON.stringify(before) ? "" : `${JSON.stringify(before)} → ${JSON.stringify(end)}`);
}
}
main()
.catch((e) => { console.error(e); failed++; })
.finally(async () => {
await prisma.$disconnect();
console.log(`\n${failed === 0 ? "OK — alle Prüfungen bestanden" : `FEHLGESCHLAGEN — ${failed} Prüfung(en)`}`);
process.exit(failed === 0 ? 0 : 1);
});
-60
View File
@@ -1,60 +0,0 @@
// Akzeptanztest der Funktionstrennungs-Regeln FT-01…FT-06 (Story A4-2, C7 §2).
// Reine Logik, kein DB-Zugriff. Lauf: npx tsx scripts/test-ft-rules.ts
import { evaluateFt, type FtRoleInput } from "../src/lib/ft-rules";
let failures = 0;
const ok = (cond: boolean, msg: string) => {
console.log(`${cond ? "✓" : "✗ FEHLER"} ${msg}`);
if (!cond) failures++;
};
const base: FtRoleInput = {
roles: { ISB: "Frau A", MANAGEMENT: "Herr B", IT_LEAD: "Herr C", HR_LEAD: "Frau D", DPO: "Herr E" },
isbNamed: true, isbIsIt: false, isbInternal: false, personalData: false,
};
const codes = (i: FtRoleInput) => evaluateFt(i).map((f) => f.code);
const byCode = (i: FtRoleInput, c: string) => evaluateFt(i).find((f) => f.code === c);
// — Sauberer Fall: nur FT-06 (manueller Hinweis) —
ok(JSON.stringify(codes(base)) === JSON.stringify(["FT-06"]), "sauber → nur FT-06 (manuell)");
// — FT-04: ISB nicht benannt → Lücke + Aufgabe (reuse isb_not_named) —
{
const f = byCode({ ...base, isbNamed: false }, "FT-04");
ok(!!f && f.severity === "luecke" && f.task?.origin === "isb_not_named" && f.task?.reuse === true, "FT-04 ISB nicht benannt → Lücke + Aufgabe (reuse)");
}
// — FT-01: ISB = IT → Konflikt + Aufgabe (reuse isb_equals_it) —
{
const f = byCode({ ...base, isbIsIt: true }, "FT-01");
ok(!!f && f.severity === "konflikt" && f.task?.origin === "isb_equals_it" && f.task?.reuse === true, "FT-01 ISB=IT → Konflikt + Aufgabe (reuse)");
}
// — FT-03: ISB = Leitung → Konflikt + neue Aufgabe ft-03 —
{
const f = byCode({ ...base, roles: { ...base.roles, MANAGEMENT: "Frau A" } }, "FT-03");
ok(!!f && f.severity === "konflikt" && f.task?.origin === "ft-03" && f.task?.reuse === false, "FT-03 ISB=Leitung → Konflikt + neue Aufgabe");
}
// — FT-05: DPO fehlt trotz personenbezogener Daten → Lücke + neue Aufgabe ft-05 —
{
const f = byCode({ ...base, personalData: true, roles: { ...base.roles, DPO: "" } }, "FT-05");
ok(!!f && f.severity === "luecke" && f.task?.origin === "ft-05" && f.task?.reuse === false, "FT-05 DPO fehlt + FLAG_PERSONAL_DATA → Lücke + neue Aufgabe");
// Kein FT-05, wenn DPO gesetzt:
ok(!byCode({ ...base, personalData: true }, "FT-05"), "FT-05 nicht, wenn DPO benannt");
// Kein FT-05, wenn keine personenbezogenen Daten:
ok(!byCode({ ...base, roles: { ...base.roles, DPO: "" } }, "FT-05"), "FT-05 nicht ohne FLAG_PERSONAL_DATA");
}
// — FT-02: interner ISB (nicht IT) → schwacher Hinweis, keine Aufgabe —
{
const f = byCode({ ...base, isbInternal: true }, "FT-02");
ok(!!f && f.severity === "schwach" && !f.task, "FT-02 interner ISB → Hinweis ohne Aufgabe");
}
// — FT-06 immer vorhanden (manuell) —
ok(!!byCode(base, "FT-06") && !byCode(base, "FT-06")!.task, "FT-06 immer vorhanden, ohne Aufgabe");
console.log(failures === 0 ? "\nOK — alle FT-Tests grün" : `\nPRUEFEN — ${failures} Fehler`);
process.exit(failures === 0 ? 0 : 1);
-91
View File
@@ -1,91 +0,0 @@
// Unit-Tests (Story A8) für die Gap-Konsolidierung gegen die C8-Beispiele.
// Lauf: npx tsx scripts/test-gap-consolidation.ts
import { consolidate, gapPriority, isQuickWin, summarize, type RawGap } from "../src/lib/gap-consolidation";
let failed = 0;
function check(name: string, cond: boolean, detail = "") {
if (cond) console.log(` ok ${name}`);
else {
failed++;
console.error(`FAIL ${name}${detail ? ` — ${detail}` : ""}`);
}
}
function raw(p: Partial<RawGap>): RawGap {
return {
id: p.id ?? "x",
source: p.source ?? "control",
title: p.title ?? "",
action: p.action ?? "",
controls: p.controls ?? [],
effort: p.effort ?? "mittel",
maturityImpact: p.maturityImpact ?? 1,
hasDependency: p.hasDependency ?? false,
taskOrigin: p.taskOrigin ?? "wizard:gap",
...p,
};
}
// C8 §1 Prioritätsregeln + §4 Beispieltabelle.
console.log("§1/§4 Priorität:");
check(
"Kein Patchmanagement (MUSS<2 + Risiko) → Hoch",
gapPriority(raw({ requirementType: "MUSS", maturity: 1, riskScore: 16, aboveAcceptance: true })) === "hoch",
);
check(
"Restore-Test fehlt (MUSS-Nachweis) → Hoch",
gapPriority(raw({ requirementType: "MUSS", maturity: 1, kind: "nachweis" })) === "hoch",
);
check("ISB=IT (FT-01 Konflikt) → Hoch", gapPriority(raw({ ftConflict: true, source: "role" })) === "hoch");
check(
"SOLL Berechtigungs-Review nur Vorlage → Mittel",
gapPriority(raw({ requirementType: "SOLL", kind: "verfahren", maturity: 1 })) === "mittel",
);
check("Zonenbezeichnung redaktionell → Niedrig", gapPriority(raw({ kind: "redaktionell", maturity: 3 })) === "niedrig");
check(
"MUSS-Control Reifegrad 2 unter Ziel 3 → Mittel",
gapPriority(raw({ requirementType: "MUSS", maturity: 2, target: 3, kind: "nachweis" })) === "mittel",
);
check("HOCH-Zusatzanforderung offen → Hoch", gapPriority(raw({ requirementType: "HOCH", maturity: 2, target: 3 })) === "hoch");
// C8 §3 Quick-Wins.
console.log("§3 Quick-Wins:");
check("Restore-Test (organisatorisch, +1) → Quick-Win", isQuickWin(raw({ effort: "gering", maturityImpact: 1 })));
check("Patchmanagement (Tool) → kein Quick-Win", !isQuickWin(raw({ effort: "hoch", maturityImpact: 1 })));
check("Abhängigkeit → kein Quick-Win", !isQuickWin(raw({ effort: "gering", maturityImpact: 1, hasDependency: true })));
check("keine Reifegrad-Wirkung → kein Quick-Win", !isQuickWin(raw({ effort: "gering", maturityImpact: 0 })));
// C8 §2 Deduplizierung.
console.log("§2 Deduplizierung:");
const dupSame = consolidate([
raw({ id: "control:5.2.3:nachweis", source: "control", controls: ["5.2.3"], kind: "nachweis", requirementType: "MUSS", maturity: 2, target: 3 }),
raw({ id: "control:5.2.3:nachweis", source: "control", controls: ["5.2.3"], kind: "nachweis", requirementType: "MUSS", maturity: 2, target: 3 }),
]);
check("gleiche Teilanforderung → ein Punkt", dupSame.length === 1);
// Cross-Source-Merge: Risiko-Maßnahme + Control-Gaps derselben Controls (C8 §2, R-OPS-03-Beispiel).
const crossMerge = consolidate([
raw({ id: "risk:R1", source: "risk", controls: ["5.2.3", "5.2.5"], riskScore: 16, aboveAcceptance: true, title: "Patchmanagement", taskId: "t1" }),
raw({ id: "control:5.2.3:nachweis", source: "control", controls: ["5.2.3"], kind: "nachweis", requirementType: "MUSS", maturity: 2, target: 3 }),
raw({ id: "control:5.2.5:nachweis", source: "control", controls: ["5.2.5"], kind: "nachweis", requirementType: "MUSS", maturity: 2, target: 3 }),
raw({ id: "control:1.1.1:nachweis", source: "control", controls: ["1.1.1"], kind: "nachweis", requirementType: "MUSS", maturity: 2, target: 3 }),
]);
check("Risiko absorbiert zugehörige Control-Gaps → 2 Punkte (Risiko + 1.1.1)", crossMerge.length === 2);
const riskItem = crossMerge.find((i) => i.source === "risk")!;
check("gemergter Risiko-Punkt hat Priorität Hoch", riskItem.priority === "hoch");
check("gemergter Risiko-Punkt vereint 5.2.3/5.2.5", riskItem.controls.includes("5.2.3") && riskItem.controls.includes("5.2.5"));
check("gemergter Risiko-Punkt behält bestehende Aufgabe", riskItem.taskId === "t1");
check("Risiko-Punkt vor Control-Punkt sortiert (Priorität + betroffene Controls)", crossMerge[0].source === "risk");
// Sortierung: Hoch vor Mittel.
console.log("Sortierung & Summary:");
const sorted = consolidate([
raw({ id: "a", source: "control", controls: ["3.1.1"], kind: "redaktionell", maturity: 3 }),
raw({ id: "b", source: "control", controls: ["1.4.1"], requirementType: "MUSS", maturity: 1, kind: "verfahren" }),
]);
check("Hoch (b) vor Niedrig (a)", sorted[0].id === "b" && sorted[1].id === "a");
const sum = summarize(sorted);
check("Summary zählt korrekt", sum.total === 2 && sum.hoch === 1 && sum.niedrig === 1);
console.log(failed === 0 ? "\nAlle Gap-Konsolidierungs-Tests grün." : `\n${failed} Test(s) fehlgeschlagen.`);
process.exit(failed === 0 ? 0 : 1);
-205
View File
@@ -1,205 +0,0 @@
// Reiner Test der Fristen-/Timer-Logik für Vorfälle (IM-B, Fachkonzept §6).
//
// Prüft:
// 1. Fristen aus dem Kenntniszeitpunkt (detectedAt/reportedAt) korrekt berechnet.
// 2. NIS2-Timer greifen NUR bei nis2Category ∈ {wichtig, wesentlich} UND nis2Relevant.
// 3. DSGVO-Frist (72 h) greift bei Personenbezug (dsgvoRelevant).
// 4. Interne SLA je Severity (Reaktions-/Behebungsfrist).
// 5. Überfällig-Erkennung + offene/erledigte Meldestufen (reportStatus).
//
// Lauf: npx tsx scripts/test-incident-deadlines.ts (rein, keine DB).
import {
addHours,
addMonths,
computeDeadlines,
computeSla,
deadlineItems,
isDueSoon,
isOverdue,
isReportable,
knowledgeTime,
nextReportStatus,
nis2TimersApply,
openReportDeadlines,
remainingMs,
slaForSeverity,
stageDone,
NIS2_ERSTMELDUNG_HOURS,
NIS2_FOLGEMELDUNG_HOURS,
} from "../src/lib/incident-deadlines";
let failures = 0;
const ok = (cond: boolean, msg: string) => {
console.log(`${cond ? "✓" : "✗ FEHLER"} ${msg}`);
if (!cond) failures++;
};
const HOUR = 3_600_000;
const known = new Date("2026-03-10T08:00:00.000Z");
// ── 1. Kenntniszeitpunkt-Priorität ──────────────────────────────────────────
{
const detected = new Date("2026-03-10T08:00:00Z");
const reported = new Date("2026-03-11T08:00:00Z");
ok(
knowledgeTime({ detectedAt: detected, reportedAt: reported })?.getTime() === detected.getTime(),
"knowledgeTime: detectedAt hat Vorrang vor reportedAt",
);
ok(
knowledgeTime({ detectedAt: null, reportedAt: reported })?.getTime() === reported.getTime(),
"knowledgeTime: ohne detectedAt greift reportedAt",
);
ok(
knowledgeTime({ detectedAt: null, reportedAt: null, occurredAt: null, createdAt: known })?.getTime() ===
known.getTime(),
"knowledgeTime: Fallback auf createdAt",
);
}
// ── 1b. Fristen aus Kenntniszeitpunkt (NIS2 wesentlich) ─────────────────────
{
const d = computeDeadlines({
nis2Category: "wesentlich",
nis2Relevant: true,
dsgvoRelevant: false,
knownAt: known,
});
ok(
d.erstmeldungDueAt?.getTime() === known.getTime() + NIS2_ERSTMELDUNG_HOURS * HOUR,
"NIS2 Erstmeldung = Kenntnis + 24 h",
);
ok(
d.folgemeldungDueAt?.getTime() === known.getTime() + NIS2_FOLGEMELDUNG_HOURS * HOUR,
"NIS2 Folgemeldung = Kenntnis + 72 h",
);
ok(d.abschlussDueAt !== null, "NIS2 Abschlussbericht gesetzt");
ok(
d.abschlussDueAt!.getMonth() === addMonths(known, 1).getMonth(),
"NIS2 Abschlussbericht = Kenntnis + 1 Monat (Monat +1)",
);
ok(d.dsgvoDueAt === null, "ohne Personenbezug keine DSGVO-Frist");
}
// ── 2. NIS2-Timer nur bei passender Kategorie UND Relevanz ──────────────────
{
ok(nis2TimersApply("wichtig", true), "nis2TimersApply: wichtig + relevant → true");
ok(nis2TimersApply("wesentlich", true), "nis2TimersApply: wesentlich + relevant → true");
ok(!nis2TimersApply("keine", true), "nis2TimersApply: keine → false (auch wenn relevant)");
ok(!nis2TimersApply("wesentlich", false), "nis2TimersApply: nicht relevant → false");
const noCat = computeDeadlines({
nis2Category: "keine",
nis2Relevant: true,
dsgvoRelevant: false,
knownAt: known,
});
ok(
noCat.erstmeldungDueAt === null && noCat.folgemeldungDueAt === null && noCat.abschlussDueAt === null,
"computeDeadlines: nis2Category=keine → keine NIS2-Fristen",
);
const notRelevant = computeDeadlines({
nis2Category: "wesentlich",
nis2Relevant: false,
dsgvoRelevant: false,
knownAt: known,
});
ok(
notRelevant.erstmeldungDueAt === null,
"computeDeadlines: nis2Relevant=false → keine NIS2-Fristen (trotz Kategorie)",
);
}
// ── 3. DSGVO bei Personenbezug ──────────────────────────────────────────────
{
const d = computeDeadlines({
nis2Category: "keine",
nis2Relevant: false,
dsgvoRelevant: true,
knownAt: known,
});
ok(d.dsgvoDueAt?.getTime() === known.getTime() + 72 * HOUR, "DSGVO-Frist = Kenntnis + 72 h");
ok(d.erstmeldungDueAt === null, "DSGVO ohne NIS2: keine NIS2-Fristen");
ok(
isReportable({ nis2Category: "keine", nis2Relevant: false, dsgvoRelevant: true }),
"isReportable: nur DSGVO → meldepflichtig",
);
ok(
!isReportable({ nis2Category: "keine", nis2Relevant: true, dsgvoRelevant: false }),
"isReportable: NIS2-relevant aber Kategorie keine → nicht meldepflichtig",
);
}
// ── 4. Interne SLA je Severity ──────────────────────────────────────────────
{
ok(slaForSeverity("kritisch").reactionHours === 1, "SLA kritisch: Reaktion 1 h");
ok(slaForSeverity("kritisch").resolutionHours === 24, "SLA kritisch: Behebung 24 h");
ok(
slaForSeverity("niedrig").reactionHours > slaForSeverity("hoch").reactionHours,
"SLA: niedrigere Severity → längere Reaktionsfrist",
);
const sla = computeSla("hoch", known);
ok(
sla.reactionDueAt?.getTime() === addHours(known, 4).getTime(),
"computeSla hoch: Reaktionsfrist = Kenntnis + 4 h",
);
ok(computeSla("mittel", null).reactionDueAt === null, "computeSla ohne Kenntnis → null");
}
// ── 5. Überfällig-Erkennung + Meldestufen ───────────────────────────────────
{
const now = new Date("2026-03-11T10:00:00.000Z"); // 26 h nach Kenntnis
ok(isOverdue(addHours(known, 24), now), "isOverdue: 24-h-Frist ist nach 26 h überfällig");
ok(!isOverdue(addHours(known, 72), now), "isOverdue: 72-h-Frist noch nicht überfällig");
ok(remainingMs(addHours(known, 72), now)! > 0, "remainingMs: 72-h-Frist hat positive Restzeit");
ok(isDueSoon(addHours(known, 30), now), "isDueSoon: Frist in 4 h ist bald fällig");
ok(!isDueSoon(addHours(known, 240), now), "isDueSoon: Frist in >24 h ist nicht bald fällig");
ok(stageDone("erstmeldung", "erstmeldung"), "stageDone: reportStatus=erstmeldung → Erstmeldung erledigt");
ok(!stageDone("pruefung", "erstmeldung"), "stageDone: reportStatus=pruefung → Erstmeldung offen");
ok(stageDone("folgemeldung", "erstmeldung"), "stageDone: reportStatus=folgemeldung → Erstmeldung erledigt");
const inc = {
severity: "hoch",
reportStatus: "pruefung",
detectedAt: known,
reportedAt: null,
occurredAt: null,
createdAt: known,
erstmeldungDueAt: addHours(known, 24),
folgemeldungDueAt: addHours(known, 72),
abschlussDueAt: addMonths(known, 1),
dsgvoDueAt: null,
};
const items = deadlineItems(inc, now);
const erst = items.find((i) => i.kind === "erstmeldung")!;
ok(erst.overdue, "deadlineItems: offene Erstmeldung nach 26 h ist überfällig");
ok(items.some((i) => i.kind === "reaction"), "deadlineItems: interne SLA-Reaktionsfrist enthalten");
const open = openReportDeadlines(inc, now);
ok(
open.every((i) => !i.done) && open.some((i) => i.kind === "erstmeldung"),
"openReportDeadlines: nur offene Meldestufen, Erstmeldung enthalten",
);
const submitted = openReportDeadlines({ ...inc, reportStatus: "folgemeldung" }, now);
ok(
!submitted.some((i) => i.kind === "erstmeldung" || i.kind === "folgemeldung"),
"openReportDeadlines: eingereichte Stufen (reportStatus=folgemeldung) fallen raus",
);
}
// ── 6. Meldung-Track Vorwärts-Übergänge ─────────────────────────────────────
{
ok(nextReportStatus("pruefung") === "erstmeldung", "nextReportStatus: pruefung → erstmeldung");
ok(nextReportStatus("erstmeldung") === "folgemeldung", "nextReportStatus: erstmeldung → folgemeldung");
ok(nextReportStatus("folgemeldung") === "abschluss", "nextReportStatus: folgemeldung → abschluss");
ok(nextReportStatus("abschluss") === null, "nextReportStatus: abschluss ist Endzustand");
}
if (failures) {
console.error(`\n✗ ${failures} Prüfung(en) fehlgeschlagen.`);
process.exit(1);
}
console.log("\n✓ Alle Fristen-/Timer-Prüfungen bestanden.");
-179
View File
@@ -1,179 +0,0 @@
// Reiner Test der Inbound-Mail-Logik für Vorfälle (IM-D, Fachkonzept §2/§14.3).
//
// Prüft parse.ts + route.ts OHNE Netz/DB/IMAP (Fixtures):
// 1. Token wird aus Delivered-To/X-Envelope-To gezogen — NICHT aus To (Weiterleitung).
// 2. Weiterleitung: SPF-Fail wird toleriert, DKIM-pass + Allowlist tragen → Vorfall.
// 3. Auto-Reply/Bounce (Auto-Submitted/Precedence/leerer Return-Path) → ignorieren.
// 4. Dedupe per Message-ID (alreadySeen) → ignorieren.
// 5. Unbekannter Token → Betreiber-Review (nicht droppen).
// 6. Kein Token → Betreiber-Review.
// 7. Absender nicht in Allowlist → Review (fail-closed).
// 8. DKIM=fail → Review (auch bei getroffener Allowlist).
//
// Lauf: npx tsx scripts/test-incident-inbound.ts (rein, keine DB).
import {
parseInbound,
deliveryRecipients,
tokenFromAddress,
isAutoSubmitted,
authResults,
intakeAddress,
type RawHeaders,
} from "../src/server/incident-inbound/parse";
import { decideRoute, generateIntakeToken, type IntakeConfigView } from "../src/server/incident-inbound/route";
let failures = 0;
const ok = (cond: boolean, msg: string) => {
console.log(`${cond ? "✓" : "✗ FEHLER"} ${msg}`);
if (!cond) failures++;
};
const DOMAIN = "in.certvia.de";
const TOKEN = "abc123def456"; // passt auf [a-z0-9]{8,64}
const INTAKE = intakeAddress(TOKEN, DOMAIN); // vorfall-abc123def456@in.certvia.de
const config: IntakeConfigView = {
tenantId: "tenant-1",
token: TOKEN,
allowlistDomains: ["kunde.de"],
sourceAddress: "vorfall@kunde.de",
};
// Hilfsbau einer Fixture-Mail.
function mail(headers: RawHeaders, extra: Partial<{ from: string; subject: string; text: string; messageId: string }> = {}) {
return parseInbound({ headers, ...extra }, DOMAIN);
}
// --- 1) Token aus Delivered-To / X-Envelope-To, nicht aus To -------------------
{
const parsed = mail(
{
"To": "vorfall@kunde.de", // Kundenadresse — hier steht KEIN Token
"Delivered-To": INTAKE, // Zustelladresse trägt den Token
"From": "Melder <melder@kunde.de>",
"Message-ID": "<m1@kunde.de>",
"Authentication-Results": "mx.certvia.de; dkim=pass header.d=kunde.de; spf=fail",
},
{ subject: "Serverausfall", text: "Bitte prüfen." },
);
ok(parsed.token === TOKEN, "Token wird aus Delivered-To gezogen");
ok(parsed.recipientForToken === INTAKE, "recipientForToken = Intake-Adresse");
ok(tokenFromAddress("vorfall@kunde.de", DOMAIN) === null, "To-Adresse liefert keinen Token");
ok(deliveryRecipients({ To: "vorfall@kunde.de" }).length === 0, "To zählt nicht als Zustell-Empfänger");
}
// --- 2) Weiterleitung: SPF-Fail toleriert, DKIM+Allowlist → Vorfall ------------
{
const parsed = mail(
{
"X-Envelope-To": INTAKE,
"From": "Alice <alice@kunde.de>",
"Message-ID": "<m2@kunde.de>",
"Authentication-Results": "mx; dkim=pass; spf=fail", // Weiterleitung bricht SPF
},
{ subject: "Phishing gemeldet", text: "Verdächtige Mail erhalten." },
);
ok(parsed.spf === "fail" && parsed.dkim === "pass", "SPF=fail, DKIM=pass erkannt");
const d = decideRoute(parsed, { config, alreadySeen: false });
ok(d.action === "incident", "trotz SPF-Fail → Vorfall (DKIM+Allowlist tragen)");
if (d.action === "incident") {
ok(d.incident.tenantId === "tenant-1", "Vorfall im richtigen Mandanten");
ok(d.incident.title === "Phishing gemeldet", "Betreff → Titel");
ok(d.incident.reporterContact === "alice@kunde.de", "Absender → Melder-Kontakt");
ok(d.spfWarning === true, "SPF-Fail als Warnung markiert (nicht blockierend)");
ok(d.incident.inboundMessageId === "m2@kunde.de", "Message-ID normalisiert übernommen");
}
}
// --- 3) Auto-Reply / Bounce → ignorieren --------------------------------------
{
ok(isAutoSubmitted({ "Auto-Submitted": "auto-replied" }), "Auto-Submitted erkannt");
ok(isAutoSubmitted({ "Precedence": "bulk" }), "Precedence: bulk erkannt");
ok(isAutoSubmitted({ "Return-Path": "<>" }), "Leerer Return-Path (Bounce) erkannt");
ok(!isAutoSubmitted({ "Auto-Submitted": "no" }), "Auto-Submitted: no ist normal");
const parsed = mail({
"Delivered-To": INTAKE,
"From": "mailer@kunde.de",
"Auto-Submitted": "auto-replied",
"Message-ID": "<auto1@kunde.de>",
"Authentication-Results": "mx; dkim=pass; spf=pass",
});
const d = decideRoute(parsed, { config, alreadySeen: false });
ok(d.action === "ignore" && d.reason === "auto_submitted", "Auto-Reply → ignore");
}
// --- 4) Dedupe per Message-ID → ignorieren ------------------------------------
{
const parsed = mail({
"Delivered-To": INTAKE,
"From": "alice@kunde.de",
"Message-ID": "<dup@kunde.de>",
"Authentication-Results": "mx; dkim=pass; spf=pass",
});
const d = decideRoute(parsed, { config, alreadySeen: true });
ok(d.action === "ignore" && d.reason === "duplicate", "bereits gesehene Message-ID → ignore");
}
// --- 5) Unbekannter Token → Review --------------------------------------------
{
const parsed = mail({
"Delivered-To": intakeAddress("unknowntoken99", DOMAIN),
"From": "alice@kunde.de",
"Message-ID": "<u1@kunde.de>",
"Authentication-Results": "mx; dkim=pass; spf=pass",
});
ok(parsed.token === "unknowntoken99", "Token geparst, aber unbekannt");
const d = decideRoute(parsed, { config: null, alreadySeen: false });
ok(d.action === "review" && d.review.reason === "unknown_token", "unbekannter Token → Review");
ok(d.action === "review" && d.review.tenantId === null, "kein Mandant bei unbekanntem Token");
}
// --- 6) Kein Token → Review ---------------------------------------------------
{
const parsed = mail({
"Delivered-To": "hallo@in.certvia.de", // kein vorfall-<token>
"From": "alice@kunde.de",
"Message-ID": "<n1@kunde.de>",
});
ok(parsed.token === null, "kein Token erkannt");
const d = decideRoute(parsed, { config: null, alreadySeen: false });
ok(d.action === "review" && d.review.reason === "no_token", "kein Token → Review");
}
// --- 7) Absender nicht in Allowlist → Review (fail-closed) --------------------
{
const parsed = mail({
"Delivered-To": INTAKE,
"From": "fremd@boese.example", // nicht in Allowlist (kunde.de)
"Message-ID": "<a1@boese.example>",
"Authentication-Results": "mx; dkim=pass; spf=pass",
});
const d = decideRoute(parsed, { config, alreadySeen: false });
ok(d.action === "review" && d.review.reason === "allowlist_failed", "Absender außerhalb Allowlist → Review");
ok(d.action === "review" && d.review.tenantId === "tenant-1", "Mandant trotzdem aufgelöst (Token bekannt)");
}
// --- 8) DKIM=fail → Review (auch bei getroffener Allowlist) -------------------
{
const parsed = mail({
"Delivered-To": INTAKE,
"From": "alice@kunde.de",
"Message-ID": "<dk1@kunde.de>",
"Authentication-Results": "mx; dkim=fail; spf=pass",
});
ok(authResults({ "Authentication-Results": "mx; dkim=fail" }).dkim === "fail", "DKIM=fail geparst");
const d = decideRoute(parsed, { config, alreadySeen: false });
ok(d.action === "review" && d.review.reason === "dkim_failed", "DKIM-Fail → Review");
}
// --- 9) Token-Generator liefert gültiges Format -------------------------------
{
const tok = generateIntakeToken();
ok(/^[a-z0-9]{8,64}$/.test(tok), "generateIntakeToken erfüllt Token-Format");
ok(tokenFromAddress(intakeAddress(tok, DOMAIN), DOMAIN) === tok, "Round-Trip Token↔Adresse");
}
console.log(failures === 0 ? "\nAlle Inbound-Tests grün." : `\n${failures} Test(s) fehlgeschlagen.`);
process.exit(failures === 0 ? 0 : 1);
-212
View File
@@ -1,212 +0,0 @@
// Akzeptanztest Modul „Vorfälle" (IM-C): Verknüpfungen, Abschluss-Pflicht & Export.
//
// Prüft:
// 1. Maßnahme direkt aus dem Vorfall anlegen → landet im ZENTRALEN Maßnahmen-Modul
// (measures-Tabelle) UND ist mit dem Vorfall verknüpft (IncidentMeasure).
// 2. Risiko neu aus dem Vorfall erzeugen → im Risiko-Register + verknüpft (IncidentRisk).
// 3. Nachweis anlegen + verknüpfen (Evidence + IncidentEvidence).
// 4. Abschluss-Pflichtfelder (§8): abgeschlossen erzwingt Ursache/Lösung/Lessons
// Learned (+ Abschlussnotiz); Post-Incident-Review/Wirksamkeit sind NICHT Pflicht.
// 5. Register-Export (CSV) enthält die erwarteten Spalten/Werte.
// 6. NIS2- und DSGVO-Meldevorlagen sind vorbefüllt (Zeiten, Kategorie, Fristen).
//
// Lauf: npx tsx scripts/test-incident-links.ts (lokale isms-DB, .env im Repo).
import "dotenv/config";
import { prisma, dbForTenant } from "../src/server/db";
import { nextIncidentRefNo } from "../src/server/incident-refno";
import { missingRequiredFields } from "../src/lib/incident";
import {
INCIDENT_REGISTER_COLUMNS,
toRegisterCsv,
buildNis2Template,
buildDsgvoTemplate,
type IncidentRegisterInput,
type IncidentTemplateInput,
} from "../src/lib/incident-export";
let failures = 0;
const ok = (cond: boolean, msg: string) => {
console.log(`${cond ? "✓" : "✗ FEHLER"} ${msg}`);
if (!cond) failures++;
};
const SLUG = "zz-inc-links-a";
const EMAIL = "zz-inc-links-owner@test.example";
async function cleanup() {
const t = await prisma.tenant.findFirst({ where: { slug: SLUG }, select: { id: true } });
if (t) {
await prisma.incident.deleteMany({ where: { tenantId: t.id } });
await prisma.measure.deleteMany({ where: { tenantId: t.id } });
await prisma.risk.deleteMany({ where: { tenantId: t.id } });
await prisma.evidence.deleteMany({ where: { tenantId: t.id } });
await prisma.user.deleteMany({ where: { tenantId: t.id } });
await prisma.tenant.delete({ where: { id: t.id } });
}
await prisma.identity.deleteMany({ where: { email: EMAIL } });
}
async function nextMeasureRef(tenantId: string) {
const last = await prisma.measure.aggregate({ where: { tenantId }, _max: { refNo: true } });
return (last._max.refNo ?? 0) + 1;
}
async function nextRiskRef(tenantId: string) {
const last = await prisma.risk.aggregate({ where: { tenantId }, _max: { refNo: true } });
return (last._max.refNo ?? 0) + 1;
}
async function main() {
await cleanup();
const tenant = await prisma.tenant.create({ data: { name: "IM-C Test", slug: SLUG } });
const identity = await prisma.identity.create({ data: { email: EMAIL, passwordHash: "x" } });
const user = await prisma.user.create({ data: { tenantId: tenant.id, identityId: identity.id, email: EMAIL, name: "Owner" } });
const db = dbForTenant(tenant.id);
const occurredAt = new Date("2026-08-15T08:00:00Z");
const detectedAt = new Date("2026-08-16T09:30:00Z");
const incident = await db.incident.create({
data: {
tenantId: tenant.id,
refNo: await nextIncidentRefNo(prisma, tenant.id),
title: "Ransomware auf Fileserver",
description: "Verschlüsselte Freigaben; Verdacht auf Datenabfluss.",
category: "malware",
severity: "hoch",
status: "in_bearbeitung",
occurredAt,
detectedAt,
impactC: 3,
impactI: 4,
impactA: 4,
urgency: 4,
affectedDataCategories: ["Kundendaten", "Zugangsdaten"],
personalData: true,
dsgvoRelevant: true,
nis2Relevant: true,
ownerId: user.id,
// Meldefristen wie von IM-B aus dem Kenntniszeitpunkt gesetzt (24h/72h/1M).
erstmeldungDueAt: new Date(detectedAt.getTime() + 24 * 3600_000),
folgemeldungDueAt: new Date(detectedAt.getTime() + 72 * 3600_000),
dsgvoDueAt: new Date(detectedAt.getTime() + 72 * 3600_000),
reportStatus: "pruefung",
},
});
// ── 1. Maßnahme direkt aus dem Vorfall anlegen + verknüpfen ────────────────
const measure = await db.measure.create({
data: { tenantId: tenant.id, refNo: await nextMeasureRef(tenant.id), title: "Systeme isolieren & Backups prüfen", priority: "HIGH", ownerId: user.id, createdBy: user.id },
});
await db.incidentMeasure.create({ data: { tenantId: tenant.id, incidentId: incident.id, measureId: measure.id } });
const centralMeasure = await db.measure.findFirst({ where: { id: measure.id } });
ok(!!centralMeasure && centralMeasure.tenantId === tenant.id, "Maßnahme liegt im zentralen Maßnahmen-Modul (measures-Tabelle)");
const linkedMeasures = await db.incidentMeasure.findMany({ where: { incidentId: incident.id }, include: { measure: true } });
ok(linkedMeasures.length === 1 && linkedMeasures[0].measure.title === measure.title, "Maßnahme ist mit dem Vorfall verknüpft (IncidentMeasure, Titel/Status aus dem Modul)");
ok(linkedMeasures[0].measure.status === "OPEN" && linkedMeasures[0].measure.priority === "HIGH", "Verknüpfte Maßnahme trägt Status/Priorität aus dem Maßnahmen-Modul");
// ── 2. Risiko neu erzeugen + verknüpfen ────────────────────────────────────
const risk = await db.risk.create({
data: { tenantId: tenant.id, refNo: await nextRiskRef(tenant.id), title: "Unzureichende Backup-Isolierung", likelihood: 3, impact: 5, score: 15, createdBy: user.id },
});
await db.incidentRisk.create({ data: { tenantId: tenant.id, incidentId: incident.id, riskId: risk.id } });
const centralRisk = await db.risk.findFirst({ where: { id: risk.id } });
ok(!!centralRisk && centralRisk.score === 15, "Risiko im Register angelegt (score = E×S)");
const linkedRisks = await db.incidentRisk.count({ where: { incidentId: incident.id } });
ok(linkedRisks === 1, "Risiko ist mit dem Vorfall verknüpft (IncidentRisk)");
// ── 3. Nachweis anlegen + verknüpfen ───────────────────────────────────────
const ev = await db.evidence.create({ data: { tenantId: tenant.id, title: "Forensik-Report Erstsichtung", kind: "record", createdById: user.id } });
await db.incidentEvidence.create({ data: { tenantId: tenant.id, incidentId: incident.id, evidenceId: ev.id, note: "PDF im DMS" } });
const linkedEv = await db.incidentEvidence.findMany({ where: { incidentId: incident.id }, include: { evidence: true } });
ok(linkedEv.length === 1 && linkedEv[0].evidence.title === ev.title, "Nachweis verknüpft (IncidentEvidence)");
// ── 4. Abschluss-Pflichtfelder (§8) ────────────────────────────────────────
const missWithout = missingRequiredFields("abgeschlossen", { rootCause: "x", resolution: "y", closingNote: "z", lessonsLearned: "" });
ok(missWithout.includes("lessonsLearned"), "Abschluss erzwingt Lessons Learned (fehlt → blockiert)");
const missAll = missingRequiredFields("abgeschlossen", { rootCause: "Ursache", resolution: "Lösung", closingNote: "Notiz", lessonsLearned: "LL", postIncidentReview: null, measuresEffectiveness: null });
ok(missAll.length === 0, "Abschluss mit Ursache/Lösung/Abschlussnotiz/Lessons Learned zulässig — Review/Wirksamkeit NICHT erzwungen");
// ── 5. Register-Export (CSV) ───────────────────────────────────────────────
const regInput: IncidentRegisterInput = {
refNo: incident.refNo,
title: incident.title,
category: incident.category,
severity: incident.severity,
priority: incident.priority,
status: incident.status,
source: incident.source,
occurredAt: incident.occurredAt,
detectedAt: incident.detectedAt,
reportedAt: incident.reportedAt,
ownerName: user.name,
assigneeName: null,
nis2Relevant: incident.nis2Relevant,
dsgvoRelevant: incident.dsgvoRelevant,
personalData: incident.personalData,
prototypeData: incident.prototypeData,
reportStatus: incident.reportStatus,
erstmeldungDueAt: incident.erstmeldungDueAt,
dsgvoDueAt: incident.dsgvoDueAt,
abschlussDueAt: incident.abschlussDueAt,
measureCount: 1,
riskCount: 1,
assetCount: 0,
controlCount: 0,
createdAt: incident.createdAt,
};
const csv = toRegisterCsv([regInput]);
const header = csv.split("\r\n")[0];
ok(INCIDENT_REGISTER_COLUMNS.every((c) => header.includes(c)), "Register-CSV enthält alle erwarteten Spalten (Kennung … Maßnahmen … Erstellt)");
ok(csv.includes(incident.refNo) && csv.includes("Ransomware auf Fileserver"), "Register-CSV-Zeile enthält refNo + Titel");
ok(csv.includes("Schadsoftware"), "Register-CSV übersetzt die Kategorie (Schadsoftware)");
// ── 6. Meldevorlagen (NIS2 / DSGVO) ────────────────────────────────────────
const tmpl: IncidentTemplateInput = {
refNo: incident.refNo,
title: incident.title,
description: incident.description,
category: incident.category,
severity: incident.severity,
organisation: tenant.name,
occurredAt: incident.occurredAt,
detectedAt: incident.detectedAt,
reportedAt: incident.reportedAt,
impactC: incident.impactC,
impactI: incident.impactI,
impactA: incident.impactA,
affectedDataCategories: incident.affectedDataCategories,
personalData: incident.personalData,
nis2Relevant: incident.nis2Relevant,
reporterName: incident.reporterName,
reporterContact: incident.reporterContact,
erstmeldungDueAt: incident.erstmeldungDueAt,
folgemeldungDueAt: incident.folgemeldungDueAt,
abschlussDueAt: incident.abschlussDueAt,
dsgvoDueAt: incident.dsgvoDueAt,
};
const nis2 = buildNis2Template(tmpl);
ok(nis2.includes("NIS2") && nis2.includes(incident.refNo) && nis2.includes("Schadsoftware"), "NIS2-Vorlage vorbefüllt (refNo + Kategorie)");
ok(nis2.includes("24 h") && nis2.includes("72 h") && nis2.includes("1 Monat"), "NIS2-Vorlage nennt die Meldefristen 24 h / 72 h / 1 Monat");
ok(nis2.includes("IM-C Test"), "NIS2-Vorlage trägt die meldende Organisation");
const dsgvo = buildDsgvoTemplate(tmpl);
ok(dsgvo.includes("Art. 33") && dsgvo.includes("72 h"), "DSGVO-Vorlage referenziert Art. 33 (72 h)");
ok(dsgvo.includes("Kundendaten") && dsgvo.includes("Art. 34"), "DSGVO-Vorlage enthält betroffene Datenkategorien + Art.-34-Hinweis");
await cleanup();
if (failures) {
console.error(`\n✗ ${failures} Prüfung(en) fehlgeschlagen.`);
process.exit(1);
}
console.log("\n✓ Alle IM-C-Verknüpfungs-/Export-Prüfungen bestanden.");
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(() => prisma.$disconnect());
-153
View File
@@ -1,153 +0,0 @@
// Akzeptanztest Modul „Vorfälle" (IM-A).
//
// Prüft:
// 1. Mandantenisolation: ein Vorfall aus Mandant A ist über dbForTenant(B) NICHT
// sichtbar (RLS/Guard) — und der Eigenzugriff funktioniert.
// 2. refNo-Format `INC-<JJJJ>-<lfd>` + Fortlauf je Mandant/Jahr.
// 3. Statusübergang mit Pflichtfeld: nach „behoben"/„abgeschlossen" fehlen die
// Pflichtfelder → Übergang unzulässig; mit gesetzten Feldern → zulässig.
// 4. Vertraulichkeit: ein restricted-Vorfall wird nur für owner/manage sichtbar,
// für andere über den serverseitigen Filter ausgeblendet.
//
// Lauf: npx tsx scripts/test-incidents.ts (lokale isms-DB, .env im Repo).
import "dotenv/config";
import { prisma, dbForTenant } from "../src/server/db";
import { nextIncidentRefNo } from "../src/server/incident-refno";
import { canTransition, missingRequiredFields } from "../src/lib/incident";
import { computeSeverity, impactFromCia } from "../src/lib/incident-severity";
import type { Prisma } from "@prisma/client";
let failures = 0;
const ok = (cond: boolean, msg: string) => {
console.log(`${cond ? "✓" : "✗ FEHLER"} ${msg}`);
if (!cond) failures++;
};
async function expectNull(fn: () => Promise<unknown>, msg: string) {
const r = await fn();
ok(r === null, `${msg}${r === null ? "" : ` — statt null: ${JSON.stringify(r)}`}`);
}
const SLUG_A = "zz-inc-test-a";
const SLUG_B = "zz-inc-test-b";
async function cleanup() {
const tenants = await prisma.tenant.findMany({
where: { slug: { in: [SLUG_A, SLUG_B] } },
select: { id: true },
});
const ids = tenants.map((t) => t.id);
if (ids.length) {
await prisma.incident.deleteMany({ where: { tenantId: { in: ids } } });
await prisma.user.deleteMany({ where: { tenantId: { in: ids } } });
await prisma.tenant.deleteMany({ where: { id: { in: ids } } });
}
await prisma.identity.deleteMany({ where: { email: { in: ["zz-inc-owner@test.example", "zz-inc-other@test.example"] } } });
}
async function main() {
await cleanup();
const tenantA = await prisma.tenant.create({ data: { name: "INC-Test A", slug: SLUG_A } });
const tenantB = await prisma.tenant.create({ data: { name: "INC-Test B", slug: SLUG_B } });
// Nutzer (owner + anderer) im Mandant A für die Vertraulichkeitsprüfung.
const idOwner = await prisma.identity.create({ data: { email: "zz-inc-owner@test.example", passwordHash: "x" } });
const idOther = await prisma.identity.create({ data: { email: "zz-inc-other@test.example", passwordHash: "x" } });
const owner = await prisma.user.create({ data: { tenantId: tenantA.id, identityId: idOwner.id, email: "zz-inc-owner@test.example", name: "Owner" } });
const other = await prisma.user.create({ data: { tenantId: tenantA.id, identityId: idOther.id, email: "zz-inc-other@test.example", name: "Other" } });
const dbA = dbForTenant(tenantA.id);
const dbB = dbForTenant(tenantB.id);
// ── 2. refNo-Format + Fortlauf ────────────────────────────────────────────
const ref1 = await nextIncidentRefNo(prisma, tenantA.id);
ok(/^INC-\d{4}-\d{4}$/.test(ref1), `refNo-Format korrekt (${ref1})`);
const year = new Date().getFullYear();
ok(ref1 === `INC-${year}-0001`, `erste refNo ist INC-${year}-0001 (${ref1})`);
const incA1 = await dbA.incident.create({
data: { tenantId: tenantA.id, refNo: ref1, title: "Vorfall A1", category: "phishing", severity: "mittel", status: "neu" },
});
const ref2 = await nextIncidentRefNo(prisma, tenantA.id);
ok(ref2 === `INC-${year}-0002`, `zweite refNo läuft fort → INC-${year}-0002 (${ref2})`);
await dbA.incident.create({
data: { tenantId: tenantA.id, refNo: ref2, title: "Vorfall A2", category: "outage", severity: "niedrig", status: "neu" },
});
// Mandant B startet unabhängig wieder bei 0001.
const refB1 = await nextIncidentRefNo(prisma, tenantB.id);
ok(refB1 === `INC-${year}-0001`, `Fortlauf ist mandantenlokal (B startet bei 0001: ${refB1})`);
const incB1 = await dbB.incident.create({
data: { tenantId: tenantB.id, refNo: refB1, title: "Vorfall B1", category: "malware", severity: "hoch", status: "neu" },
});
// ── 1. Mandantenisolation ─────────────────────────────────────────────────
const ownA = await dbA.incident.findFirst({ where: { id: incA1.id } });
ok(ownA?.id === incA1.id, "Eigenzugriff: Mandant A sieht seinen Vorfall");
await expectNull(
() => dbB.incident.findFirst({ where: { id: incA1.id } }),
"Isolation: Mandant B sieht den Vorfall von A NICHT (findFirst → null)",
);
const bCount = await dbB.incident.count({ where: {} });
ok(bCount === 1, `Isolation: Mandant B zählt nur seine eigenen Vorfälle (${bCount} === 1)`);
// Gegenrichtung
await expectNull(
() => dbA.incident.findFirst({ where: { id: incB1.id } }),
"Isolation: Mandant A sieht den Vorfall von B NICHT",
);
// ── 3. Statusübergang mit Pflichtfeld ─────────────────────────────────────
ok(canTransition("in_bearbeitung", "behoben"), "Übergang in_bearbeitung → behoben ist erlaubt");
ok(!canTransition("neu", "abgeschlossen"), "Übergang neu → abgeschlossen ist NICHT erlaubt");
// ohne Pflichtfelder → fehlend gemeldet
const missBehoben = missingRequiredFields("behoben", { rootCause: null, resolution: "" });
ok(missBehoben.includes("rootCause") && missBehoben.includes("resolution"), "behoben verlangt rootCause + resolution (fehlen erkannt)");
const missClose = missingRequiredFields("abgeschlossen", { rootCause: "x", resolution: "y", closingNote: "", lessonsLearned: null });
ok(missClose.includes("closingNote") && missClose.includes("lessonsLearned"), "abgeschlossen verlangt zusätzlich closingNote + lessonsLearned");
// mit allen Feldern → nichts fehlt
const missOk = missingRequiredFields("abgeschlossen", { rootCause: "Ursache", resolution: "Lösung", closingNote: "Notiz", lessonsLearned: "LL" });
ok(missOk.length === 0, "abgeschlossen mit allen Pflichtfeldern → zulässig (nichts fehlt)");
// Severity-Matrix (Default, §5)
ok(computeSeverity(impactFromCia(4, 0, 0), 4) === "kritisch", "Severity-Matrix: hohe Auswirkung + hohe Dringlichkeit → kritisch");
ok(computeSeverity(impactFromCia(0, 0, 0), 0) === "niedrig", "Severity-Matrix: keine Auswirkung + keine Dringlichkeit → niedrig");
// ── 4. Vertraulichkeit (restricted) ───────────────────────────────────────
const restricted = await dbA.incident.create({
data: { tenantId: tenantA.id, refNo: await nextIncidentRefNo(prisma, tenantA.id), title: "Vertraulicher Vorfall", category: "unauthorized_access", severity: "hoch", status: "neu", restricted: true, ownerId: owner.id },
});
// Filter wie in der Liste/Detail (page.tsx): manage/close sieht alles; sonst nur
// unrestricted + eigene (ownerId == userId).
const restrictedWhere = (canSee: boolean, userId: string): Prisma.IncidentWhereInput =>
canSee ? {} : { OR: [{ restricted: false }, { ownerId: userId }] };
// manage/close → sichtbar
const seenByManager = await dbA.incident.findFirst({ where: { AND: [{ id: restricted.id }, restrictedWhere(true, other.id)] } });
ok(seenByManager?.id === restricted.id, "restricted: Rolle mit manage/close sieht den vertraulichen Vorfall");
// owner → sichtbar
const seenByOwner = await dbA.incident.findFirst({ where: { AND: [{ id: restricted.id }, restrictedWhere(false, owner.id)] } });
ok(seenByOwner?.id === restricted.id, "restricted: der owner sieht seinen vertraulichen Vorfall");
// anderer ohne manage/close → NICHT sichtbar
await expectNull(
() => dbA.incident.findFirst({ where: { AND: [{ id: restricted.id }, restrictedWhere(false, other.id)] } }),
"restricted: anderer Nutzer ohne manage/close sieht ihn NICHT",
);
await cleanup();
if (failures) {
console.error(`\n✗ ${failures} Prüfung(en) fehlgeschlagen.`);
process.exit(1);
}
console.log("\n✓ Alle Incident-Prüfungen bestanden.");
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(() => prisma.$disconnect());
+1 -1
View File
@@ -40,7 +40,7 @@ const SAMPLE = {
email_change_verify: { name: "Erika Muster", actionUrl: "https://example.test/v", expires: "in 60 Minuten", newEmail: "neu@example.test" }, email_change_verify: { name: "Erika Muster", actionUrl: "https://example.test/v", expires: "in 60 Minuten", newEmail: "neu@example.test" },
email_changed_notice: { name: "Erika Muster", newEmail: "neu@example.test", when: "heute" }, email_changed_notice: { name: "Erika Muster", newEmail: "neu@example.test", when: "heute" },
mfa_changed: { name: "Erika Muster", change: "aktiviert", when: "heute" }, mfa_changed: { name: "Erika Muster", change: "aktiviert", when: "heute" },
notification: { name: "Erika Muster", subject: "Neue Aufgabe", body: "Text", actionUrl: "https://example.test/t", taskType: "policy_approval" }, notification: { name: "Erika Muster", subject: "Neuer Auftrag", body: "Text", actionUrl: "https://example.test/t", eventType: "work_order_assigned" },
incident_notification: { name: "Erika Muster", subject: "Neuer Vorfall gemeldet", body: "Text", actionUrl: "https://example.test/i", refNo: "INC-2026-0042" }, incident_notification: { name: "Erika Muster", subject: "Neuer Vorfall gemeldet", body: "Text", actionUrl: "https://example.test/i", refNo: "INC-2026-0042" },
test: { name: "Erika Muster", when: "heute" }, test: { name: "Erika Muster", when: "heute" },
} as const; } as const;
-98
View File
@@ -1,98 +0,0 @@
// Unit-Tests (Story A7-2) für die Reifegrad-Engine gegen die C5-Regelbeispiele.
// Lauf: npx tsx scripts/test-maturity.ts
import {
suggestMaturity,
targetMaturity,
openPoints,
specForControl,
type ControlEvidence,
} from "../src/lib/maturity";
let failed = 0;
function check(name: string, cond: boolean, detail = "") {
if (cond) console.log(` ok ${name}`);
else {
failed++;
console.error(`FAIL ${name}${detail ? ` — ${detail}` : ""}`);
}
}
const base: ControlEvidence = {
policy: "fehlt",
verfahren: "fehlt",
assetLinked: false,
riskLinked: false,
operationalProof: false,
};
// Control mit Richtlinie + Verfahren + Asset (1.3.1: R02 / VA-08 / (A)).
const s131 = specForControl("1.3.1");
check("1.3.1 hat Verfahren + needsAsset", s131.verfahren.length === 1 && s131.needsAsset);
console.log("§2 Belegkonstellationen (Control 1.3.1):");
check("R0 — keine Belege → 0", suggestMaturity(s131, base).value === 0);
check("R1a — Richtlinie verknüpft, unvalidiert → 1", suggestMaturity(s131, { ...base, policy: "verknuepft" }).rule === "R1a");
check(
"R1b — Richtlinie validiert, Verfahren fehlt → 1",
suggestMaturity(s131, { ...base, policy: "validiert" }).rule === "R1b",
);
check(
"R1c — nur operativer Nachweis, keine validierte Richtlinie → 1",
suggestMaturity(s131, { ...base, operationalProof: true }).rule === "R1c",
);
check(
"R2 — Richtlinie+Verfahren validiert + Asset, kein Nachweis → 2",
suggestMaturity(s131, { ...base, policy: "validiert", verfahren: "validiert", assetLinked: true }).value === 2,
);
check(
"R2 verweigert bei fehlender Asset-Verknüpfung → 1",
suggestMaturity(s131, { ...base, policy: "validiert", verfahren: "validiert", assetLinked: false }).value === 1,
);
check(
"R3 — R2 + aktueller Wirksamkeitsnachweis → 3",
suggestMaturity(s131, { ...base, policy: "validiert", verfahren: "validiert", assetLinked: true, operationalProof: true }).value === 3,
);
check(
"Deckelung — Widerspruch/Findings → max. 1 (CAP)",
suggestMaturity(s131, { ...base, policy: "validiert", verfahren: "validiert", assetLinked: true, operationalProof: true, contradiction: true }).value === 1,
);
// Control ohne Verfahren (1.1.1: L00 / — / …): Grad 2 über validierte Umsetzungsregelung (N-basisch).
console.log("§2 Sonderfall ohne Verfahren (Control 1.1.1):");
const s111 = specForControl("1.1.1");
check("1.1.1 ohne Verfahren", s111.verfahren.length === 0);
check(
"R2 ohne V — Richtlinie validiert + validierte Umsetzungsregelung → 2",
suggestMaturity(s111, { ...base, policy: "validiert", implementationRule: true }).value === 2,
);
check(
"R1b ohne V — Richtlinie validiert, keine Umsetzungsregelung → 1",
suggestMaturity(s111, { ...base, policy: "validiert" }).value === 1,
);
check(
"R3 ohne V — Umsetzungsregelung + aktueller Wirksamkeitsnachweis → 3",
suggestMaturity(s111, { ...base, policy: "validiert", implementationRule: true, operationalProof: true }).value === 3,
);
// Zielreifegrad (§3).
console.log("§3 Zielreifegrad:");
check("AL3 → 3", targetMaturity({ level: "AL3", flags: {} }) === 3);
check("AL2 reiner MUSS-Scope → 2", targetMaturity({ level: "AL2", flags: {} }) === 2);
check("AL2 + SOLL → 3", targetMaturity({ level: "AL2", flags: { FLAG_INCLUDE_SHOULD: true } }) === 3);
check("AL2 + HOCH → 3", targetMaturity({ level: "AL2", flags: { FLAG_HIGH_PROTECTION: true } }) === 3);
// Offene Punkte (§4).
console.log("§4 Offene Punkte:");
const evGap: ControlEvidence = { ...base, policy: "validiert", verfahren: "fehlt", assetLinked: false };
const sugGap = suggestMaturity(s131, evGap);
const ops = openPoints(s131, evGap, sugGap, 3);
check("Verfahren-Gap erzeugt Punkt", ops.some((o) => o.kind === "verfahren"));
check("Asset-Verknüpfungs-Gap erzeugt Punkt", ops.some((o) => o.kind === "verknuepfung"));
check("Nachweis-Gap bei Ziel 3 erzeugt Punkt", ops.some((o) => o.kind === "nachweis"));
check(
"kein Gap bei erfülltem Ziel",
openPoints(s131, { ...base, policy: "validiert", verfahren: "validiert", assetLinked: true, operationalProof: true }, suggestMaturity(s131, { ...base, policy: "validiert", verfahren: "validiert", assetLinked: true, operationalProof: true }), 3).length === 0,
);
console.log(failed === 0 ? "\nAlle Reifegrad-Tests grün." : `\n${failed} Test(s) fehlgeschlagen.`);
process.exit(failed === 0 ? 0 : 1);
-56
View File
@@ -1,56 +0,0 @@
// Akzeptanztest der Readiness-Logik (Story B7-1, C9 §1/§2). Reine Logik, kein DB-Zugriff.
// Lauf: npx tsx scripts/test-readiness.ts (Exit 1 bei Fehler).
import { interpretationBand, averageReifegrad, buildNextSteps, computeReadiness, type ControlAssessment } from "../src/lib/readiness";
let failures = 0;
const ok = (cond: boolean, msg: string) => {
console.log(`${cond ? "✓" : "✗ FEHLER"} ${msg}`);
if (!cond) failures++;
};
// — C9 §1: Textbänder je Ø-Reifegrad —
ok(interpretationBand(0).band === "Aufbau", "Ø 0 → Aufbau");
ok(interpretationBand(1.49).band === "Aufbau", "Ø 1,49 → Aufbau");
ok(interpretationBand(1.5).band === "Etabliert im Aufbau", "Ø 1,5 → Etabliert im Aufbau");
ok(interpretationBand(2.49).band === "Etabliert im Aufbau", "Ø 2,49 → Etabliert im Aufbau");
ok(interpretationBand(2.5).band === "Assessment-nah", "Ø 2,5 → Assessment-nah");
ok(interpretationBand(2.99).band === "Assessment-nah", "Ø 2,99 → Assessment-nah");
ok(interpretationBand(3.0).band === "Assessment-reif", "Ø 3,0 → Assessment-reif");
// — Ø-Reifegrad: „unbestätigt zählt nicht als erfüllt" (geht mit 0 ein) —
{
const a: ControlAssessment[] = [
{ control: "1.1.1", chapter: "1", reifegrad: 3, bestaetigt: true },
{ control: "1.1.2", chapter: "1", reifegrad: 3, bestaetigt: false }, // zählt als 0
];
ok(averageReifegrad(a) === 1.5, "unbestätigt zählt als 0 → Ø(3,0) = 1,5");
ok(averageReifegrad([]) === null, "keine Assessments → null (A7 ausstehend)");
}
// — C9 §2: dynamische nächste Schritte —
{
const all = buildNextSteps({ openHighCount: 2, unvalidatedCount: 5, prototypeGap: true, evidenceUploadPending: true });
ok(all.some((s) => s.includes("2 Hoch-Punkte")), "Hoch-Punkte → Schritt mit Anzahl");
ok(all.some((s) => s.includes("5 Objekte")), "unvalidierte Objekte → Schritt mit Anzahl");
ok(all.some((s) => s.includes("Prototypen")), "Prototyp-Gap → Prototyp-Schritt");
ok(all.length === 4, "alle vier Regeln greifen");
const none = buildNextSteps({ openHighCount: 0, unvalidatedCount: 0, prototypeGap: false, evidenceUploadPending: false });
ok(none.length === 0, "keine Bedingung → keine Schritte");
}
// — computeReadiness: assessmentPending ohne A7-Daten —
{
const r = computeReadiness({
assessments: [], bestaetigtAnteil: 0,
offenJePrioritaet: { hoch: 1, mittel: 2, niedrig: 0 },
pruefzielAbdeckung: [{ pruefziel: "informationssicherheit", anforderungen: 300 }],
zielReifegrad: 3,
nextSteps: { openHighCount: 1, unvalidatedCount: 0, prototypeGap: false, evidenceUploadPending: false },
});
ok(r.assessmentPending === true && r.avgReifegrad === null && r.band === null, "ohne Assessments → pending, kein Reifegrad/Band");
ok(r.nextSteps.length === 1, "Kennzahlen-basierte Schritte trotzdem berechnet");
}
console.log(failures === 0 ? "\nOK — alle Readiness-Tests grün" : `\nPRUEFEN — ${failures} Fehler`);
process.exit(failures === 0 ? 0 : 1);
-88
View File
@@ -1,88 +0,0 @@
import "dotenv/config";
import { join } from "node:path";
import { prisma } from "../src/server/db";
import { importPolicies } from "../prisma/import-policies";
const SEED_DIR = join(process.cwd(), "seed", "isms-vorlagenpaket-v2");
const ok = (c: boolean, m: string) => console.log(`${c ? "✓" : "✗ FEHLER"} ${m}`);
async function main() {
const tenant = await prisma.tenant.findFirst({ where: { slug: "demo" } });
if (!tenant) throw new Error("Demo-Mandant fehlt");
const t = tenant.id;
// Ausgangszustand eines echten Dokuments + einer Variable sichern
const doc0 = await prisma.policyDocument.findFirst({ where: { tenantId: t, code: "R08" } });
const varName = "ORG_NAME";
const var0 = await prisma.policyVariable.findFirst({ where: { tenantId: t, key: varName } });
if (!doc0 || !var0) throw new Error("Erwartetes Dokument R08 / Variable ORG_NAME fehlt");
// 1) Kuratierten Zustand simulieren: Freigabe-Status, TISAX-Override, Einreicher,
// geänderter Titel (Inhalts-Diff) + nutzergepflegter Variablenwert.
await prisma.policyDocument.update({
where: { id: doc0.id },
data: { status: "ENTWURF", protectionOverride: "AL3", submittedBy: "tester", title: "MANUELL GEÄNDERTER TITEL" },
});
await prisma.policyVariable.update({ where: { id: var0.id }, data: { value: "Mustermann Spezial GmbH" } });
// 2) Entfernte Anforderung simulieren (nicht im Paket): muss deaktiviert, nicht gelöscht werden.
const fakeReqId = "ZZ-REMOVED-TEST-1";
await prisma.policyRequirement.deleteMany({ where: { tenantId: t, reqId: fakeReqId } });
await prisma.policyRequirement.create({
data: { tenantId: t, reqId: fakeReqId, policyCode: "R08", control: "0.0.0", obligation: "MUSS", requirement: "Test", implementation: "" },
});
// 3) Dry-Run: Vorschau ohne Schreibzugriff
const preview = await importPolicies(prisma, t, SEED_DIR, { dryRun: true });
console.log("\n— Dry-Run-Report —");
console.log(JSON.stringify(preview.report, null, 0));
ok(preview.report.documents.updated >= 1, "Dry-Run erkennt Titeländerung an R08 (updated ≥ 1)");
ok(preview.report.documents.archived === 0, "Dry-Run deaktiviert KEINE verwalteten Register (CRYPTO/HANDBUCH/…)");
ok(preview.report.requirements.archived === 1, "Dry-Run erkennt 1 zu deaktivierende Anforderung");
const stillDraftAfterDry = await prisma.policyDocument.findUnique({ where: { id: doc0.id } });
ok(stillDraftAfterDry?.title === "MANUELL GEÄNDERTER TITEL", "Dry-Run schreibt NICHT (Titel unverändert)");
// 4) Echter Re-Import
const run1 = await importPolicies(prisma, t, SEED_DIR, { dryRun: false, actorId: null });
console.log("\n— Re-Import-Report —");
console.log(JSON.stringify(run1.report, null, 0));
const docA = await prisma.policyDocument.findUnique({ where: { id: doc0.id } });
ok(docA?.status === "ENTWURF", "Freigabe-Status bleibt erhalten (ENTWURF)");
ok(docA?.protectionOverride === "AL3", "TISAX-Override bleibt erhalten (AL3)");
ok(docA?.submittedBy === "tester", "Einreicher (Freigabe-Workflow) bleibt erhalten");
ok(docA?.title === doc0.title, "Titel wurde aus dem Paket aktualisiert (Inhalt)");
ok(docA?.archivedAt === null, "Dokument bleibt aktiv");
const varA = await prisma.policyVariable.findUnique({ where: { id: var0.id } });
ok(varA?.value === "Mustermann Spezial GmbH", "Nutzergepflegter Variablenwert bleibt erhalten");
const fakeA = await prisma.policyRequirement.findFirst({ where: { tenantId: t, reqId: fakeReqId } });
ok(!!fakeA && fakeA.archivedAt !== null, "Entfernte Anforderung ist deaktiviert (archivedAt gesetzt), NICHT gelöscht");
const auditA = await prisma.auditLog.findFirst({ where: { tenantId: t, entity: "policy_package" }, orderBy: { createdAt: "desc" } });
ok(!!auditA, "Änderungsreport im Audit-Log protokolliert");
// 5) Idempotenz: zweiter Lauf ohne externe Änderung → keine Diffs
const run2 = await importPolicies(prisma, t, SEED_DIR, { dryRun: false });
const r = run2.report;
const noChanges =
r.documents.added + r.documents.updated + r.documents.archived + r.documents.reactivated === 0 &&
r.requirements.added + r.requirements.updated + r.requirements.archived + r.requirements.reactivated === 0 &&
r.variables.added + r.variables.updated === 0 && r.baseline.added + r.baseline.updated === 0 &&
r.evidence.added + r.evidence.updated === 0;
console.log("\n— Idempotenz-Report —");
console.log(JSON.stringify(r, null, 0));
ok(noChanges, "Zweiter Lauf ist idempotent (keine added/updated/archived/reactivated)");
// Aufräumen: Testartefakt entfernen, kuratierten Zustand zurücksetzen
await prisma.policyRequirement.deleteMany({ where: { tenantId: t, reqId: fakeReqId } });
await prisma.policyDocument.update({
where: { id: doc0.id },
data: { status: doc0.status, protectionOverride: doc0.protectionOverride, submittedBy: doc0.submittedBy, title: doc0.title, archivedAt: null },
});
await prisma.policyVariable.update({ where: { id: var0.id }, data: { value: var0.value } });
console.log("\n✓ aufgeräumt (Testartefakte entfernt, R08/ORG_NAME zurückgesetzt)");
}
main().then(() => process.exit(0)).catch((e) => { console.error(e); process.exit(1); });
-78
View File
@@ -1,78 +0,0 @@
// AP4 — Managementklauseln (9.1 Kennzahlen, 9.3 Managementbewertung, 10.2 CAPA).
// Prüft Datenmodell, Relationen und Kern-Invarianten der DoD gegen die lokale DB
// (Wegwerf-Mandant, dbForTenant → RLS-Pfad).
//
// Lauf: npx tsx scripts/test-review.ts
import "dotenv/config";
import { prisma, dbForTenant } from "../src/server/db";
let failures = 0;
const ok = (c: boolean, m: string) => { console.log(`${c ? "✓" : "✗ FEHLER"} ${m}`); if (!c) failures++; };
const SLUG = "ap4-test-review";
async function cleanup() {
const t = await prisma.tenant.findUnique({ where: { slug: SLUG }, select: { id: true } });
if (!t) return;
const id = t.id;
await prisma.correctiveAction.deleteMany({ where: { tenantId: id } });
await prisma.nonconformity.deleteMany({ where: { tenantId: id } });
await prisma.managementReviewDecision.deleteMany({ where: { tenantId: id } });
await prisma.managementReview.deleteMany({ where: { tenantId: id } });
await prisma.kpiValue.deleteMany({ where: { tenantId: id } });
await prisma.kpi.deleteMany({ where: { tenantId: id } });
await prisma.tenant.delete({ where: { id } });
}
async function main() {
await cleanup();
const tenant = await prisma.tenant.create({ data: { name: "AP4", slug: SLUG } });
const t = tenant.id;
const db = dbForTenant(t);
// ── 9.1 Kennzahl über zwei Perioden auswertbar ──────────────────────────────
const kpi = await db.kpi.create({ data: { tenantId: t, name: "Überfällige Maßnahmen", target: "< 5%", unit: "%", cadence: "quartalsweise" }, select: { id: true } });
for (const [period, value] of [["2026-Q1", "8%"], ["2026-Q2", "4%"]]) {
await db.kpiValue.upsert({
where: { tenantId_kpiId_period: { tenantId: t, kpiId: kpi.id, period } },
update: { value }, create: { tenantId: t, kpiId: kpi.id, period, value },
});
}
// Upsert derselben Periode → Update statt Duplikat (Unique).
await db.kpiValue.upsert({ where: { tenantId_kpiId_period: { tenantId: t, kpiId: kpi.id, period: "2026-Q2" } }, update: { value: "3%" }, create: { tenantId: t, kpiId: kpi.id, period: "2026-Q2", value: "3%" } });
const vals = await db.kpiValue.findMany({ where: { kpiId: kpi.id }, orderBy: { period: "asc" } });
ok(vals.length === 2, `Kennzahl über zwei Perioden auswertbar (${vals.length} Werte, keine Dubletten)`);
ok(vals[1].value === "3%", "Messwert je Periode ist aktualisierbar (Upsert)");
// ── 9.3 Managementbewertung entlang 9.3.2 mit Beschlüssen ───────────────────
const review = await db.managementReview.create({ data: { tenantId: t, reviewDate: new Date("2026-06-01"), inputs: "Kennzahlen, Auditergebnisse, Vorjahresmaßnahmen …", results: "Ressourcen genehmigt" }, select: { id: true } });
await db.managementReviewDecision.create({ data: { tenantId: t, reviewId: review.id, decision: "Zusätzliche Awareness-Schulung", dueDate: new Date("2026-09-30") } });
const d2 = await db.managementReviewDecision.create({ data: { tenantId: t, reviewId: review.id, decision: "Backup-Konzept prüfen" }, select: { id: true } });
await db.managementReviewDecision.update({ where: { id: d2.id }, data: { status: "erledigt" } });
const withDec = await db.managementReview.findUnique({ where: { id: review.id }, include: { decisions: true } });
ok(withDec?.decisions.length === 2, "Managementbewertung mit Beschlüssen (Verantwortlicher/Termin) protokollierbar");
ok(withDec?.decisions.filter((d) => d.status === "erledigt").length === 1, "Beschluss-Status nachverfolgbar (1 erledigt)");
await db.managementReview.update({ where: { id: review.id }, data: { status: "abgeschlossen" } });
ok((await db.managementReview.findUnique({ where: { id: review.id } }))?.status === "abgeschlossen", "Managementbewertung abschließbar");
// ── 10.2 Nichtkonformität + Korrekturmaßnahme inkl. Wirksamkeit ──────────────
const nc = await db.nonconformity.create({ data: { tenantId: t, refNo: "NC-2026-0001", source: "Internes Audit", description: "Zugriffsrechte nicht rezertifiziert", immediateCorrection: "Sofort-Review" }, select: { id: true } });
const nc2 = await db.nonconformity.create({ data: { tenantId: t, refNo: "NC-2026-0002", source: "Vorfall", description: "Test" }, select: { refNo: true } });
ok(nc2.refNo === "NC-2026-0002", "fortlaufende NC-Kennung (NC-2026-0002)");
const ca = await db.correctiveAction.create({ data: { tenantId: t, nonconformityId: nc.id, action: "Rezertifizierungs-Prozess etablieren", rootCause: "Kein definierter Turnus" }, select: { id: true } });
// Wirksamkeit dokumentieren + Maßnahme umgesetzt.
await db.correctiveAction.update({ where: { id: ca.id }, data: { status: "umgesetzt", effectivenessCheck: "Rezertifizierung im Folgequartal vollständig", effectivenessConfirmedAt: new Date() } });
const caAfter = await db.correctiveAction.findUnique({ where: { id: ca.id } });
ok(!!caAfter?.effectivenessConfirmedAt && !!caAfter?.effectivenessCheck, "Korrekturmaßnahme mit dokumentierter Wirksamkeitsprüfung");
// Nichtkonformität schließen.
await db.nonconformity.update({ where: { id: nc.id }, data: { status: "abgeschlossen", closedAt: new Date() } });
const ncAfter = await db.nonconformity.findUnique({ where: { id: nc.id } });
ok(ncAfter?.status === "abgeschlossen" && !!ncAfter?.closedAt, "Maßnahmenfall inkl. Wirksamkeitsprüfung abschließbar");
await cleanup();
console.log("\n✓ aufgeräumt (Wegwerf-Mandant entfernt)");
}
main()
.then(() => { console.log(failures === 0 ? "\nAP4-Managementklauseln 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); });
+78 -72
View File
@@ -1,27 +1,32 @@
// Korrektheitsnachweis für F-04: Row Level Security scharfschalten. // Korrektheitsnachweis für F-04: Row Level Security scharfschalten.
// //
// Beweist, dass die scharfe RLS (FORCE + WITH CHECK + Kontext, Rolle isms_app) // Beweist, dass die scharfe RLS (FORCE + WITH CHECK + Kontext, Rolle craftvia_app)
// die Mandantentrennung erzwingt, OHNE den lokalen Owner-Betrieb zu brechen. // die Mandantentrennung erzwingt, OHNE den lokalen Owner-Betrieb zu brechen.
// Fünf Nachweise (jeweils Assertion; am Ende "OK" oder Exit-Code != 0): // Nachweise (jeweils Assertion; am Ende "OK" oder Exit-Code != 0):
// (0) Baseline: die RLS-Funktion enable_tenant_rls() existiert und alle Tabellen der
// TENANT_MODELS tragen FORCE RLS + Policy tenant_isolation.
// (1) Owner-Betrieb bleibt heil: Owner (Superuser/BYPASSRLS) sieht ohne // (1) Owner-Betrieb bleibt heil: Owner (Superuser/BYPASSRLS) sieht ohne
// app.tenant_id weiterhin ALLE Zeilen über Mandanten hinweg. // app.tenant_id weiterhin ALLE Zeilen über Mandanten hinweg.
// (2) RLS greift für isms_app: mit Kontext=A nur A-Zeilen, keine von B. // (2) RLS greift für craftvia_app: mit Kontext=A nur A-Zeilen, keine von B.
// (3) Ohne Kontext = null Zeilen (fail-closed). // (3) Ohne Kontext = null Zeilen (fail-closed).
// (4) WITH CHECK wirkt: INSERT mit eigenem Mandanten gelingt, mit fremdem // (4) WITH CHECK wirkt: INSERT mit eigenem Mandanten gelingt, mit fremdem
// Mandanten wird abgelehnt. // Mandanten wird abgelehnt.
// (5) dbForTenant end-to-end mit RLS_ENFORCED=true (dynamischer Import). // (5) dbForTenant end-to-end mit RLS_ENFORCED=true (dynamischer Import).
// //
// Voraussetzung (einmalig lokal): // Fixture: Fundament-Modell `Role`.
// docker exec isms-tool-postgres-1 psql -U isms -d isms \ //
// -c "ALTER ROLE isms_app WITH LOGIN PASSWORD 'isms_app_local';" // Voraussetzung (einmalig lokal, nach `prisma migrate deploy`):
// npx prisma migrate deploy // docker compose exec postgres psql -U craftvia -d craftvia \
// -c "ALTER ROLE craftvia_app WITH LOGIN PASSWORD 'craftvia_app_local';"
// Ohne Login-Recht der Rolle wird der Test mit klarer Meldung übersprungen (Exit 0),
// es sei denn RLS_TEST_REQUIRED=true.
// //
// Lauf: npx tsx scripts/test-rls-enforcement.ts // Lauf: npx tsx scripts/test-rls-enforcement.ts
// Nutzt die lokale Postgres-DB (Container isms-tool-postgres-1); .env im Worktree.
import "dotenv/config"; import "dotenv/config";
import { PrismaClient } from "@prisma/client"; import { PrismaClient } from "@prisma/client";
import { PrismaPg } from "@prisma/adapter-pg"; import { PrismaPg } from "@prisma/adapter-pg";
import { buildTenantTopology, TENANT_MODELS } from "../src/server/backup/topology";
let failures = 0; let failures = 0;
const ok = (cond: boolean, msg: string) => { const ok = (cond: boolean, msg: string) => {
@@ -39,7 +44,7 @@ async function expectThrow(fn: () => Promise<unknown>, msg: string) {
const RLS_URL = const RLS_URL =
process.env.RLS_DATABASE_URL ?? process.env.RLS_DATABASE_URL ??
"postgresql://isms_app:isms_app_local@localhost:5432/isms?schema=public"; "postgresql://craftvia_app:craftvia_app_local@localhost:5432/craftvia?schema=public";
const SLUG_A = "zz-rls-test-a"; const SLUG_A = "zz-rls-test-a";
const SLUG_B = "zz-rls-test-b"; const SLUG_B = "zz-rls-test-b";
@@ -50,7 +55,7 @@ const SLUG_B = "zz-rls-test-b";
const owner = new PrismaClient({ const owner = new PrismaClient({
adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }), adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }),
}); });
// Client der eingeschränkten App-Rolle isms_app (unterliegt der scharfen RLS). // Client der eingeschränkten App-Rolle craftvia_app (unterliegt der scharfen RLS).
const app = new PrismaClient({ const app = new PrismaClient({
adapter: new PrismaPg({ connectionString: RLS_URL }), adapter: new PrismaPg({ connectionString: RLS_URL }),
}); });
@@ -62,114 +67,115 @@ async function cleanup() {
}); });
const ids = tenants.map((t) => t.id); const ids = tenants.map((t) => t.id);
if (ids.length) { if (ids.length) {
await owner.risk.deleteMany({ where: { tenantId: { in: ids } } }); await owner.role.deleteMany({ where: { tenantId: { in: ids } } });
await owner.tenant.deleteMany({ where: { id: { in: ids } } }); await owner.tenant.deleteMany({ where: { id: { in: ids } } });
} }
} }
async function main() { async function main() {
// ── (0) Baseline-Struktur ─────────────────────────────────────────────────
const fn = await owner.$queryRawUnsafe<{ n: bigint }[]>(
"SELECT count(*)::bigint AS n FROM pg_proc WHERE proname = 'enable_tenant_rls'",
);
ok(Number(fn[0]?.n ?? 0) === 1, "(0a) SQL-Funktion enable_tenant_rls(text) existiert");
const topo = buildTenantTopology();
const tables = TENANT_MODELS.map((m) => topo.nodes.get(m)!.table);
const rls = await owner.$queryRawUnsafe<{ relname: string; relrowsecurity: boolean; relforcerowsecurity: boolean; policies: bigint }[]>(
`SELECT c.relname, c.relrowsecurity, c.relforcerowsecurity,
(SELECT count(*) FROM pg_policies p WHERE p.tablename = c.relname AND p.policyname = 'tenant_isolation')::bigint AS policies
FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE n.nspname = 'public' AND c.relkind = 'r'`,
);
const byTable = new Map(rls.map((r) => [r.relname, r]));
const missing = tables.filter((t) => {
const r = byTable.get(t);
return !r || !r.relrowsecurity || !r.relforcerowsecurity || Number(r.policies) !== 1;
});
ok(missing.length === 0, `(0b) alle ${tables.length} Tenant-Tabellen haben ENABLE+FORCE RLS + tenant_isolation${missing.length ? ` — fehlt: ${missing.join(", ")}` : ""}`);
// Login-Fähigkeit der App-Rolle prüfen (lokal ggf. noch nicht eingerichtet).
try {
await app.$queryRawUnsafe("SELECT 1");
} catch (e) {
const msg = e instanceof Error ? e.message : String(e);
if (process.env.RLS_TEST_REQUIRED === "true") throw e;
console.log(`\n⚠ ÜBERSPRUNGEN (1)-(5): Verbindung als craftvia_app nicht möglich (${msg.split("\n")[0]}).`);
console.log(" Einmalig: ALTER ROLE craftvia_app WITH LOGIN PASSWORD 'craftvia_app_local';");
if (failures > 0) process.exit(1);
return;
}
await cleanup(); await cleanup();
// Setup: zwei Test-Mandanten mit je einem Risk (über den Owner angelegt). // Setup: zwei Test-Mandanten mit je einer Rolle (über den Owner angelegt).
const tenantA = await owner.tenant.create({ const tenantA = await owner.tenant.create({ data: { name: "ZZ RLS Test A", slug: SLUG_A } });
data: { name: "ZZ RLS Test A", slug: SLUG_A }, const tenantB = await owner.tenant.create({ data: { name: "ZZ RLS Test B", slug: SLUG_B } });
}); const roleA = await owner.role.create({ data: { tenantId: tenantA.id, key: "zz-rls", name: "Rolle A" } });
const tenantB = await owner.tenant.create({ const roleB = await owner.role.create({ data: { tenantId: tenantB.id, key: "zz-rls", name: "Rolle B" } });
data: { name: "ZZ RLS Test B", slug: SLUG_B },
});
const riskA = await owner.risk.create({
data: { tenantId: tenantA.id, refNo: 9001, title: "Risiko A" },
});
const riskB = await owner.risk.create({
data: { tenantId: tenantB.id, refNo: 9001, title: "Risiko B" },
});
// ── (1) Owner-Betrieb bleibt heil (kritisch für lokal + devB) ────────────── // ── (1) Owner-Betrieb bleibt heil ────────────────────────────────────────
// Owner ist Superuser/BYPASSRLS → sieht trotz FORCE alle Mandanten, OHNE dass const ownerCount = await owner.role.count({ where: { tenantId: { in: [tenantA.id, tenantB.id] } } });
// app.tenant_id gesetzt ist. ok(ownerCount === 2, `(1) Owner sieht ohne Kontext beide Mandanten (${ownerCount}/2) — FORCE bricht Owner-Betrieb nicht`);
const ownerCount = await owner.risk.count({ const ownerTotal = await owner.role.count();
where: { tenantId: { in: [tenantA.id, tenantB.id] } },
});
ok(
ownerCount === 2,
`(1) Owner sieht ohne Kontext beide Mandanten (${ownerCount}/2) — FORCE bricht Owner-Betrieb nicht`,
);
const ownerTotal = await owner.risk.count();
ok(ownerTotal > 0, `(1b) Owner sieht global Zeilen (${ownerTotal} > 0)`); ok(ownerTotal > 0, `(1b) Owner sieht global Zeilen (${ownerTotal} > 0)`);
// ── (2) RLS greift für isms_app: mit Kontext=A nur A-Zeilen ──────────────── // ── (2) RLS greift für craftvia_app: mit Kontext=A nur A-Zeilen ─────────────
const seenA = await app.$transaction(async (tx) => { const seenA = await app.$transaction(async (tx) => {
await tx.$executeRaw`SELECT set_config('app.tenant_id', ${tenantA.id}, true)`; await tx.$executeRaw`SELECT set_config('app.tenant_id', ${tenantA.id}, true)`;
return tx.risk.findMany({ select: { id: true, tenantId: true } }); return tx.role.findMany({ select: { id: true, tenantId: true } });
}); });
ok( ok(
seenA.length > 0 && seenA.length > 0 &&
seenA.every((r) => r.tenantId === tenantA.id) && seenA.every((r) => r.tenantId === tenantA.id) &&
seenA.some((r) => r.id === riskA.id) && seenA.some((r) => r.id === roleA.id) &&
!seenA.some((r) => r.id === riskB.id), !seenA.some((r) => r.id === roleB.id),
`(2) isms_app mit Kontext=A sieht nur A-Zeilen (${seenA.length}), keine von B`, `(2) craftvia_app mit Kontext=A sieht nur A-Zeilen (${seenA.length}), keine von B`,
); );
// ── (3) Ohne Kontext = null Zeilen (fail-closed) ─────────────────────────── // ── (3) Ohne Kontext = null Zeilen (fail-closed) ───────────────────────────
const seenNone = await app.risk.count(); const seenNone = await app.role.count();
ok(seenNone === 0, `(3) isms_app ohne Kontext sieht 0 Zeilen (${seenNone})`); ok(seenNone === 0, `(3) craftvia_app ohne Kontext sieht 0 Zeilen (${seenNone})`);
// ── (4) WITH CHECK wirkt: eigener Mandant erlaubt, fremder abgelehnt ──────── // ── (4) WITH CHECK wirkt: eigener Mandant erlaubt, fremder abgelehnt ────────
const inserted = await app.$transaction(async (tx) => { const inserted = await app.$transaction(async (tx) => {
await tx.$executeRaw`SELECT set_config('app.tenant_id', ${tenantA.id}, true)`; await tx.$executeRaw`SELECT set_config('app.tenant_id', ${tenantA.id}, true)`;
return tx.risk.create({ return tx.role.create({ data: { tenantId: tenantA.id, key: "zz-rls-insert", name: "Rolle A insert" } });
data: { tenantId: tenantA.id, refNo: 9002, title: "Risiko A insert" },
}); });
}); ok(inserted.tenantId === tenantA.id, "(4a) INSERT mit eigenem Mandanten (A) unter Kontext=A gelingt");
ok(
inserted.tenantId === tenantA.id,
"(4a) INSERT mit eigenem Mandanten (A) unter Kontext=A gelingt",
);
await expectThrow( await expectThrow(
() => () =>
app.$transaction(async (tx) => { app.$transaction(async (tx) => {
await tx.$executeRaw`SELECT set_config('app.tenant_id', ${tenantA.id}, true)`; await tx.$executeRaw`SELECT set_config('app.tenant_id', ${tenantA.id}, true)`;
// Fremder Mandant B unter Kontext A → WITH-CHECK-Policy lehnt ab. return tx.role.create({ data: { tenantId: tenantB.id, key: "zz-rls-foreign", name: "Fremd-Insert" } });
return tx.risk.create({
data: { tenantId: tenantB.id, refNo: 9003, title: "Fremd-Insert" },
});
}), }),
"(4b) INSERT mit fremdem Mandanten (B) unter Kontext=A wird von WITH CHECK abgelehnt", "(4b) INSERT mit fremdem Mandanten (B) unter Kontext=A wird von WITH CHECK abgelehnt",
); );
// ── (5) dbForTenant end-to-end mit RLS_ENFORCED=true ─────────────────────── // ── (5) dbForTenant end-to-end mit RLS_ENFORCED=true ───────────────────────
// Flag + URL VOR dem Import von db.ts setzen (dynamischer Import).
process.env.RLS_ENFORCED = "true"; process.env.RLS_ENFORCED = "true";
process.env.RLS_DATABASE_URL = RLS_URL; process.env.RLS_DATABASE_URL = RLS_URL;
const db = await import("../src/server/db"); const db = await import("../src/server/db");
const e2eA = await db.dbForTenant(tenantA.id).risk.findMany({ const e2eA = await db.dbForTenant(tenantA.id).role.findMany({ select: { id: true, tenantId: true } });
select: { id: true, tenantId: true },
});
ok( ok(
e2eA.length > 0 && e2eA.length > 0 && e2eA.every((r) => r.tenantId === tenantA.id) && !e2eA.some((r) => r.id === roleB.id),
e2eA.every((r) => r.tenantId === tenantA.id) && `(5a) dbForTenant(A).role.findMany() liefert nur A (${e2eA.length})`,
!e2eA.some((r) => r.id === riskB.id),
`(5a) dbForTenant(A).risk.findMany() liefert nur A (${e2eA.length})`,
);
const foreign = await db.dbForTenant(tenantA.id).risk.findFirst({
where: { id: riskB.id },
});
ok(
foreign === null,
"(5b) dbForTenant(A).risk.findFirst({ id: B-Risk }) → null (kein Fremdzugriff)",
); );
const foreign = await db.dbForTenant(tenantA.id).role.findFirst({ where: { id: roleB.id } });
ok(foreign === null, "(5b) dbForTenant(A).role.findFirst({ id: B-Rolle }) → null (kein Fremdzugriff)");
await cleanup(); await cleanup();
}
main()
.then(() => {
if (failures > 0) { if (failures > 0) {
console.error(`\n${failures} Nachweis(e) fehlgeschlagen.`); console.error(`\n${failures} Nachweis(e) fehlgeschlagen.`);
process.exit(1); process.exit(1);
} }
console.log("\nOK — alle F-04-Nachweise (1)-(5) erfüllt."); console.log("\nOK — alle F-04-Nachweise erfüllt.");
} })
main()
.catch((e) => { .catch((e) => {
console.error(e); console.error(e);
process.exit(1); process.exit(1);
-102
View File
@@ -1,102 +0,0 @@
// Akzeptanztest der Regel-Engine (Story F3/B2). Deterministische Assertions gegen
// C2 §5 (Q-FEAT-01…10) und §8 (Aufgaben-Trigger). Kein DB-Zugriff, reine Logik.
// Lauf: npx tsx scripts/test-rules.ts (Exit 1 bei Fehler).
import { evaluateRules, makeContext } from "../src/lib/rules/engine";
import { ALL_RULES, FEATURE_RULES, Q } from "../src/lib/rules/feature-rules";
import { triggerById } from "../src/lib/task-triggers";
import type { Answer } from "../src/lib/rules/dsl";
let failures = 0;
const ok = (cond: boolean, msg: string) => {
console.log(`${cond ? "✓" : "✗ FEHLER"} ${msg}`);
if (!cond) failures++;
};
const evalAnswers = (answers: Record<string, Answer>, pruefziele?: string[]) =>
evaluateRules(ALL_RULES, makeContext({ answers, ...(pruefziele ? { pruefziele } : {}) }));
// — Q-FEAT-02 Cloud —
{
const on = evalAnswers({ [Q.CLOUD]: true });
ok(on.flags.FLAG_CLOUD_USED === true, "Q-FEAT-02=ja → FLAG_CLOUD_USED");
ok(["5.3.2", "5.3.4", "6.1.3"].every((c) => on.controls.includes(c)), "Q-FEAT-02=ja → Controls 5.3.2/5.3.4/6.1.3");
ok(on.risks.includes("R-CLOUD"), "Q-FEAT-02=ja → Risiko R-CLOUD");
ok(on.matched.includes("feat02-cloud"), "Q-FEAT-02=ja → Regel feat02-cloud ausgelöst");
const off = evalAnswers({ [Q.CLOUD]: false });
ok(off.flags.FLAG_CLOUD_USED !== true, "Q-FEAT-02=nein → kein FLAG_CLOUD_USED");
ok(!off.matched.includes("feat02-cloud"), "Q-FEAT-02=nein → feat02-cloud NICHT ausgelöst");
ok(!off.controls.includes("5.3.2"), "Q-FEAT-02=nein → Control 5.3.2 nicht im Scope");
}
// — A2-1: Schutzbedarf kommt zentral aus dem Assessment-Level (Seed), NICHT aus dem Fragebogen —
{
// Ohne AL-Seed setzt keine Fragebogen-Antwort HIGH/VERY_HIGH.
const noSeed = evalAnswers({ [Q.CLOUD]: true, [Q.EXTERNAL_IT]: true });
ok(noSeed.flags.FLAG_HIGH_PROTECTION !== true && noSeed.flags.FLAG_VERY_HIGH_PROTECTION !== true, "Fragebogen setzt HIGH/VERY_HIGH nicht");
ok(noSeed.flags.FLAG_ELEVATED_PROTECTION === false, "ohne AL-Seed → ELEVATED aus");
// AL2-Seed → HIGH an, VERY_HIGH aus; AL3-Seed → beide an; ELEVATED jeweils abgeleitet.
const al2 = evaluateRules(ALL_RULES, makeContext({ flags: { FLAG_HIGH_PROTECTION: true, FLAG_VERY_HIGH_PROTECTION: false } }));
ok(al2.flags.FLAG_HIGH_PROTECTION === true && al2.flags.FLAG_VERY_HIGH_PROTECTION !== true, "AL2-Seed → HIGH an, VERY_HIGH aus");
ok(al2.flags.FLAG_ELEVATED_PROTECTION === true, "AL2-Seed → ELEVATED abgeleitet an");
const al3 = evaluateRules(ALL_RULES, makeContext({ flags: { FLAG_HIGH_PROTECTION: true, FLAG_VERY_HIGH_PROTECTION: true } }));
ok(al3.flags.FLAG_VERY_HIGH_PROTECTION === true && al3.flags.FLAG_ELEVATED_PROTECTION === true, "AL3-Seed → VERY_HIGH + ELEVATED an");
}
// — Q-FEAT-09 externe IT (Control + Risiko + Aufgabe) —
{
const r = evalAnswers({ [Q.EXTERNAL_IT]: true });
ok(r.flags.FLAG_EXTERNAL_IT === true, "Q-FEAT-09=ja → FLAG_EXTERNAL_IT");
ok(r.controls.includes("6.1.1") && r.controls.includes("6.1.3"), "Q-FEAT-09=ja → Controls 6.1.1/6.1.3");
ok(r.risks.includes("R-SUP"), "Q-FEAT-09=ja → Risiko R-SUP");
ok(r.tasks.includes("external_it_yes"), "Q-FEAT-09=ja → Aufgabe external_it_yes");
}
// — C2 §8 Aufgaben-Trigger —
{
const cloud = evalAnswers({ [Q.CLOUD]: true });
ok(cloud.tasks.includes("cloud_ai_without_approval"), "Cloud=ja → Aufgabe cloud_ai_without_approval");
const ai = evalAnswers({ [Q.AI]: true });
ok(ai.tasks.includes("cloud_ai_without_approval"), "KI=ja → Aufgabe cloud_ai_without_approval");
const neither = evalAnswers({ [Q.CLOUD]: false, [Q.AI]: false });
ok(!neither.tasks.includes("cloud_ai_without_approval"), "Cloud/KI=nein → keine Freigabeverfahren-Aufgabe");
const isb = evalAnswers({ [Q.ISB_BENANNT]: false });
ok(isb.tasks.includes("isb_not_named"), "ISB nicht benannt → Aufgabe isb_not_named");
const restore = evalAnswers({ [Q.RESTORE_GETESTET]: false });
ok(restore.tasks.includes("no_restore_test"), "kein Restore-Test → Aufgabe no_restore_test");
}
// — Determinismus: gleiche Eingabe → gleiches Ergebnis —
{
const a = evalAnswers({ [Q.CLOUD]: true, [Q.EXTERNAL_IT]: true });
const b = evalAnswers({ [Q.CLOUD]: true, [Q.EXTERNAL_IT]: true });
ok(JSON.stringify(a) === JSON.stringify(b), "Determinismus: identische Auswertung bei gleicher Eingabe");
}
// — Integrität: alle referenzierten Trigger-IDs existieren im Katalog (B1) —
{
const taskIds = new Set(ALL_RULES.flatMap((r) => r.effect.tasks ?? []));
const allExist = [...taskIds].every((id) => triggerById(id) !== null);
ok(allExist, `alle Aufgaben-Trigger-IDs existieren in task-triggers.ts (${[...taskIds].join(", ")})`);
}
// — Abdeckung: jede Q-FEAT-02…10 hat eine Regel (Q-FEAT-01/Schutzbedarf ist zentral, A2-1) —
{
const covered = new Set(FEATURE_RULES.map((r) => r.source));
const missing = Array.from({ length: 9 }, (_, i) => `C2 §5 Q-FEAT-${String(i + 2).padStart(2, "0")}`).filter((s) => !covered.has(s));
ok(missing.length === 0, `alle Q-FEAT-02…10 durch Regeln abgedeckt${missing.length ? " — fehlt: " + missing.join(", ") : ""}`);
}
// — Follow-up: Prüfziele kommen aus WizardScope (Scoping), nicht aus dem Fragebogen —
{
const ds = evalAnswers({}, ["informationssicherheit", "datenschutz"]);
ok(ds.flags.FLAG_PERSONAL_DATA === true, "Prüfziel Datenschutz → FLAG_PERSONAL_DATA");
ok(ds.controls.includes("7.1.2") && ds.risks.includes("R-DSGVO"), "Prüfziel Datenschutz → Control 7.1.2 + Risiko R-DSGVO");
const proto = evalAnswers({}, ["informationssicherheit", "prototypenschutz"]);
ok(proto.flags.FLAG_PROTOTYPE_PROTECTION === true, "Prüfziel Prototypenschutz → FLAG_PROTOTYPE_PROTECTION");
const isOnly = evalAnswers({}, ["informationssicherheit"]);
ok(isOnly.flags.FLAG_PERSONAL_DATA !== true && isOnly.flags.FLAG_PROTOTYPE_PROTECTION !== true, "nur Informationssicherheit → Datenschutz/Prototyp-Flags aus");
}
console.log(failures === 0 ? "\nOK — alle Regel-Tests grün" : `\nPRUEFEN — ${failures} Fehler`);
process.exit(failures === 0 ? 0 : 1);
-72
View File
@@ -1,72 +0,0 @@
// Akzeptanztest des Scope-Filters (Story A2-2). Repräsentative C1-Zeilen + Scope-
// Dynamik. Reine Logik, kein DB-Zugriff. Lauf: npx tsx scripts/test-scope-filter.ts
import { C1_ROWS, activeRequirements, isRequirementActive, scopeSummary, type Pruefziel } from "../src/lib/scope-filter";
let failures = 0;
const ok = (cond: boolean, msg: string) => {
console.log(`${cond ? "✓" : "✗ FEHLER"} ${msg}`);
if (!cond) failures++;
};
const row = (id: string) => {
const r = C1_ROWS.find((x) => x.id === id);
if (!r) throw new Error(`C1-Zeile ${id} nicht gefunden`);
return r;
};
// Flag-Sets: HIGH ist AL2-Baseline, VERY_HIGH nur AL3 (A2-1).
const AL2 = { FLAG_HIGH_PROTECTION: true, FLAG_VERY_HIGH_PROTECTION: false, FLAG_ELEVATED_PROTECTION: true };
const AL3 = { FLAG_HIGH_PROTECTION: true, FLAG_VERY_HIGH_PROTECTION: true, FLAG_ELEVATED_PROTECTION: true };
const SHOULD = { FLAG_INCLUDE_SHOULD: true };
const IS: Pruefziel[] = ["informationssicherheit"];
// — Grunddaten —
ok(C1_ROWS.length === 412, `C1-Datengrundlage: 412 Anforderungen (${C1_ROWS.length})`);
// — MUSS (immer im Scope) —
ok(isRequirementActive(row("1.1.1-M1"), { pruefziele: IS, flags: {} }), "1.1.1-M1 (MUSS) → im Scope ohne Flags");
// — SOLL nur bei FLAG_INCLUDE_SHOULD —
ok(!isRequirementActive(row("1.1.1-S1"), { pruefziele: IS, flags: { ...AL2 } }), "1.1.1-S1 (SOLL) → NICHT ohne FLAG_INCLUDE_SHOULD");
ok(isRequirementActive(row("1.1.1-S1"), { pruefziele: IS, flags: { ...AL2, ...SHOULD } }), "1.1.1-S1 (SOLL) → im Scope mit FLAG_INCLUDE_SHOULD");
// — HOCH nur bei FLAG_HIGH_PROTECTION —
ok(!isRequirementActive(row("1.2.2-H1"), { pruefziele: IS, flags: { FLAG_HIGH_PROTECTION: false } }), "1.2.2-H1 (HOCH) → NICHT ohne FLAG_HIGH_PROTECTION");
ok(isRequirementActive(row("1.2.2-H1"), { pruefziele: IS, flags: { ...AL2 } }), "1.2.2-H1 (HOCH) → im Scope bei AL2 (HIGH-Baseline)");
// — SEHR HOCH nur bei FLAG_VERY_HIGH_PROTECTION (AL3) —
ok(!isRequirementActive(row("1.3.4-V1"), { pruefziele: IS, flags: { ...AL2 } }), "1.3.4-V1 (SEHR HOCH) → NICHT bei AL2");
ok(isRequirementActive(row("1.3.4-V1"), { pruefziele: IS, flags: { ...AL3 } }), "1.3.4-V1 (SEHR HOCH) → im Scope bei AL3");
// — Kapitel 8.x nur bei Prüfziel Prototypenschutz —
ok(!isRequirementActive(row("8.1.1-M1"), { pruefziele: IS, flags: { ...AL3, ...SHOULD } }), "8.1.1-M1 → NICHT ohne Prüfziel Prototypenschutz");
ok(isRequirementActive(row("8.1.1-M1"), { pruefziele: [...IS, "prototypenschutz"], flags: {} }), "8.1.1-M1 → im Scope bei Prüfziel Prototypenschutz");
// — Kapitel 9.x nur bei Prüfziel Datenschutz —
ok(!isRequirementActive(row("9.1.1-M1"), { pruefziele: IS, flags: { ...AL3, ...SHOULD } }), "9.1.1-M1 → NICHT ohne Prüfziel Datenschutz");
ok(isRequirementActive(row("9.1.1-M1"), { pruefziele: [...IS, "datenschutz"], flags: {} }), "9.1.1-M1 → im Scope bei Prüfziel Datenschutz");
// — Scope-Dynamik (Kennzahlen) —
const al2 = scopeSummary({ pruefziele: IS, flags: { ...AL2, ...SHOULD } });
const al3 = scopeSummary({ pruefziele: IS, flags: { ...AL3, ...SHOULD } });
ok(al3.total > al2.total, `AL3 hat mehr Anforderungen als AL2 (${al3.total} > ${al2.total}) — SEHR HOCH kommt hinzu`);
ok(al3.total - al2.total === al3.byType["SEHR HOCH"], "AL3−AL2 == Anzahl SEHR HOCH (nur SEHR HOCH unterscheidet sich)");
const noShould = scopeSummary({ pruefziele: IS, flags: { ...AL2 } });
ok(al2.total - noShould.total === al2.byType.SOLL, "FLAG_INCLUDE_SHOULD schaltet genau die SOLL-Anforderungen");
const withProto = scopeSummary({ pruefziele: [...IS, "prototypenschutz"], flags: { ...AL3, ...SHOULD } });
ok(withProto.total > al3.total, `Prüfziel Prototypenschutz erhöht den Scope (${withProto.total} > ${al3.total})`);
ok((withProto.byPruefziel["prototypenschutz"] ?? 0) > 0 && !al3.byPruefziel["prototypenschutz"], "Prototyp-Anforderungen nur bei aktivem Prüfziel");
// — Volles Prüfziel-/Flag-Set = alle 412 —
const all = scopeSummary({ pruefziele: ["informationssicherheit", "prototypenschutz", "datenschutz"], flags: { ...AL3, ...SHOULD } });
ok(all.total === 412, `alle Prüfziele + AL3 + SOLL → alle 412 Anforderungen im Scope (${all.total})`);
// — Nur MUSS (keine Flags, nur IS) —
const mussOnly = activeRequirements({ pruefziele: IS, flags: {} });
ok(mussOnly.every((r) => r.type === "MUSS"), "ohne Flags/Zusatz-Prüfziele → nur MUSS (IS)");
console.log(failures === 0 ? "\nOK — alle Scope-Filter-Tests grün" : `\nPRUEFEN — ${failures} Fehler`);
process.exit(failures === 0 ? 0 : 1);
-67
View File
@@ -1,67 +0,0 @@
// 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); });
+50 -35
View File
@@ -6,8 +6,9 @@
// Fall), noch mit `include`, ohne `select` oder über einen Compound-Unique-Key. // Fall), noch mit `include`, ohne `select` oder über einen Compound-Unique-Key.
// Der legitime Eigenzugriff muss unverändert funktionieren. // Der legitime Eigenzugriff muss unverändert funktionieren.
// //
// Lauf: npx tsx scripts/test-tenant-isolation.ts // Fixture: Fundament-Modell `Role` (tenantId + Compound-Unique `tenantId_key`).
// Nutzt die lokale Postgres-DB (Container isms-tool-postgres-1); .env liegt im Worktree. //
// Lauf: npx tsx scripts/test-tenant-isolation.ts (lokale Postgres-DB aus .env)
import "dotenv/config"; import "dotenv/config";
import { prisma, dbForTenant } from "../src/server/db"; import { prisma, dbForTenant } from "../src/server/db";
@@ -36,6 +37,7 @@ async function expectNull(fn: () => Promise<unknown>, msg: string) {
const SLUG_A = "zz-sec-test-a"; const SLUG_A = "zz-sec-test-a";
const SLUG_B = "zz-sec-test-b"; const SLUG_B = "zz-sec-test-b";
const KEY = "zz-isolation-role";
async function cleanup() { async function cleanup() {
const tenants = await prisma.tenant.findMany({ const tenants = await prisma.tenant.findMany({
@@ -44,7 +46,8 @@ async function cleanup() {
}); });
const ids = tenants.map((t) => t.id); const ids = tenants.map((t) => t.id);
if (ids.length) { if (ids.length) {
await prisma.risk.deleteMany({ where: { tenantId: { in: ids } } }); await prisma.auditLog.deleteMany({ where: { tenantId: { in: ids } } });
await prisma.role.deleteMany({ where: { tenantId: { in: ids } } });
await prisma.tenant.deleteMany({ where: { id: { in: ids } } }); await prisma.tenant.deleteMany({ where: { id: { in: ids } } });
} }
} }
@@ -53,57 +56,53 @@ async function main() {
// Idempotenz: eventuelle Reste eines früheren Laufs entfernen. // Idempotenz: eventuelle Reste eines früheren Laufs entfernen.
await cleanup(); await cleanup();
// Zwei Test-Mandanten mit je einem Risiko anlegen (roher Client = ohne Guard). // Zwei Test-Mandanten mit je einer Rolle anlegen (roher Client = ohne Guard).
const tenantA = await prisma.tenant.create({ data: { name: "SEC-Test A", slug: SLUG_A } }); const tenantA = await prisma.tenant.create({ data: { name: "SEC-Test A", slug: SLUG_A } });
const tenantB = await prisma.tenant.create({ data: { name: "SEC-Test B", slug: SLUG_B } }); const tenantB = await prisma.tenant.create({ data: { name: "SEC-Test B", slug: SLUG_B } });
const riskA = await prisma.risk.create({ const roleA = await prisma.role.create({ data: { tenantId: tenantA.id, key: KEY, name: "Rolle A (eigen)" } });
data: { tenantId: tenantA.id, refNo: 900001, title: "Risiko A (eigen)", likelihood: 3, impact: 3, score: 9 }, const roleB = await prisma.role.create({ data: { tenantId: tenantB.id, key: KEY, name: "GEHEIM-B (fremd)" } });
});
const riskB = await prisma.risk.create({
data: { tenantId: tenantB.id, refNo: 900001, title: "GEHEIM-B (fremd)", likelihood: 4, impact: 4, score: 16 },
});
const dbA = dbForTenant(tenantA.id); const dbA = dbForTenant(tenantA.id);
console.log("\n— Fremdzugriff (Mandant A liest Risiko von B) muss scheitern —"); console.log("\n— Fremdzugriff (Mandant A liest Rolle von B) muss scheitern —");
// (1) Der ursprüngliche Exploit: select-Projektion OHNE tenantId. // (1) Der ursprüngliche Exploit: select-Projektion OHNE tenantId.
await expectNull( await expectNull(
() => dbA.risk.findUnique({ where: { id: riskB.id }, select: { refNo: true, title: true } }), () => dbA.role.findUnique({ where: { id: roleB.id }, select: { key: true, name: true } }),
"findUnique + select OHNE tenantId → null (F-02-Kernfall)" "findUnique + select OHNE tenantId → null (F-02-Kernfall)"
); );
// (2) select MIT tenantId. // (2) select MIT tenantId.
await expectNull( await expectNull(
() => dbA.risk.findUnique({ where: { id: riskB.id }, select: { title: true, tenantId: true } }), () => dbA.role.findUnique({ where: { id: roleB.id }, select: { name: true, tenantId: true } }),
"findUnique + select MIT tenantId → null" "findUnique + select MIT tenantId → null"
); );
// (3) include (tenantId wäre ohnehin enthalten). // (3) include.
await expectNull( await expectNull(
() => dbA.risk.findUnique({ where: { id: riskB.id }, include: { riskMeasures: true } }), () => dbA.role.findUnique({ where: { id: roleB.id }, include: { rolePermissions: true } }),
"findUnique + include → null" "findUnique + include → null"
); );
// (4) ohne select/include. // (4) ohne select/include.
await expectNull( await expectNull(
() => dbA.risk.findUnique({ where: { id: riskB.id } }), () => dbA.role.findUnique({ where: { id: roleB.id } }),
"findUnique ohne Projektion → null" "findUnique ohne Projektion → null"
); );
// (5) findUniqueOrThrow → muss werfen statt fremden Datensatz zu liefern. // (5) findUniqueOrThrow → muss werfen statt fremden Datensatz zu liefern.
await expectThrow( await expectThrow(
() => dbA.risk.findUniqueOrThrow({ where: { id: riskB.id }, select: { title: true } }), () => dbA.role.findUniqueOrThrow({ where: { id: roleB.id }, select: { name: true } }),
"findUniqueOrThrow + select OHNE tenantId → Throw" "findUniqueOrThrow + select OHNE tenantId → Throw"
); );
// (6) Compound-Unique-Key (tenantId_refNo) mit fremdem tenantId, select ohne tenantId. // (6) Compound-Unique-Key (tenantId_key) mit fremdem tenantId, select ohne tenantId.
await expectThrow( await expectThrow(
() => () =>
dbA.risk.findUnique({ dbA.role.findUnique({
where: { tenantId_refNo: { tenantId: tenantB.id, refNo: riskB.refNo } }, where: { tenantId_key: { tenantId: tenantB.id, key: roleB.key } },
select: { title: true }, select: { name: true },
}), }),
"findUnique über Compound-Key (fremd) + select → Throw (fail-closed)" "findUnique über Compound-Key (fremd) + select → Throw (fail-closed)"
); );
@@ -111,34 +110,50 @@ async function main() {
// (7) Compound-Unique-Key mit fremdem tenantId, ohne select. // (7) Compound-Unique-Key mit fremdem tenantId, ohne select.
await expectThrow( await expectThrow(
() => () =>
dbA.risk.findUnique({ dbA.role.findUnique({
where: { tenantId_refNo: { tenantId: tenantB.id, refNo: riskB.refNo } }, where: { tenantId_key: { tenantId: tenantB.id, key: roleB.key } },
}), }),
"findUnique über Compound-Key (fremd) ohne select → Throw" "findUnique über Compound-Key (fremd) ohne select → Throw"
); );
console.log("\n— Legitimer Eigenzugriff (Mandant A liest eigenes Risiko A) muss funktionieren —"); // (7b) Mutation auf fremden Datensatz → Ownership-Vorprüfung wirft.
await expectThrow(
() => dbA.role.update({ where: { id: roleB.id }, data: { name: "gekapert" } }),
"update auf fremde Rolle → Throw (Ownership-Vorprüfung)"
);
const bAfter = await prisma.role.findUnique({ where: { id: roleB.id } });
ok(bAfter?.name === roleB.name, "fremde Rolle bleibt unverändert");
// (7c) findMany liefert nie fremde Zeilen.
const listA = await dbA.role.findMany({ where: { key: KEY } });
ok(listA.length === 1 && listA[0].id === roleA.id, "findMany(key) liefert nur die eigene Rolle");
console.log("\n— Legitimer Eigenzugriff (Mandant A liest eigene Rolle A) muss funktionieren —");
// (8) skalarer Key + select: Treffer, und tenantId darf NICHT auftauchen (kein Injektions-Leck). // (8) skalarer Key + select: Treffer, und tenantId darf NICHT auftauchen (kein Injektions-Leck).
const own1 = await dbA.risk.findUnique({ where: { id: riskA.id }, select: { title: true } }); const own1 = await dbA.role.findUnique({ where: { id: roleA.id }, select: { name: true } });
ok(own1?.title === riskA.title, "Eigenzugriff findUnique + select → Treffer"); ok(own1?.name === roleA.name, "Eigenzugriff findUnique + select → Treffer");
ok(own1 !== null && !("tenantId" in (own1 as object)), "Eigenzugriff select {title} → Rückgabe OHNE tenantId"); ok(own1 !== null && !("tenantId" in (own1 as object)), "Eigenzugriff select {name} → Rückgabe OHNE tenantId");
// (9) Compound-Key (eigen) + select: Treffer, injiziertes tenantId wieder entfernt. // (9) Compound-Key (eigen) + select: Treffer, injiziertes tenantId wieder entfernt.
const own2 = await dbA.risk.findUnique({ const own2 = await dbA.role.findUnique({
where: { tenantId_refNo: { tenantId: tenantA.id, refNo: riskA.refNo } }, where: { tenantId_key: { tenantId: tenantA.id, key: roleA.key } },
select: { title: true }, select: { name: true },
}); });
ok(own2?.title === riskA.title, "Eigenzugriff über Compound-Key + select → Treffer"); ok(own2?.name === roleA.name, "Eigenzugriff über Compound-Key + select → Treffer");
ok(own2 !== null && !("tenantId" in (own2 as object)), "Compound-Key select {title} → Rückgabe OHNE tenantId (Injektion bereinigt)"); ok(own2 !== null && !("tenantId" in (own2 as object)), "Compound-Key select {name} → Rückgabe OHNE tenantId (Injektion bereinigt)");
// (10) ohne Projektion: voller Datensatz inkl. tenantId (Normalfall). // (10) ohne Projektion: voller Datensatz inkl. tenantId (Normalfall).
const own3 = await dbA.risk.findUnique({ where: { id: riskA.id } }); const own3 = await dbA.role.findUnique({ where: { id: roleA.id } });
ok(own3?.tenantId === tenantA.id, "Eigenzugriff ohne Projektion → voller Datensatz inkl. tenantId"); ok(own3?.tenantId === tenantA.id, "Eigenzugriff ohne Projektion → voller Datensatz inkl. tenantId");
// (11) findUniqueOrThrow auf eigenen Datensatz → kein Throw. // (11) findUniqueOrThrow auf eigenen Datensatz → kein Throw.
const own4 = await dbA.risk.findUniqueOrThrow({ where: { id: riskA.id }, select: { title: true } }); const own4 = await dbA.role.findUniqueOrThrow({ where: { id: roleA.id }, select: { name: true } });
ok(own4.title === riskA.title, "Eigenzugriff findUniqueOrThrow → Treffer"); ok(own4.name === roleA.name, "Eigenzugriff findUniqueOrThrow → Treffer");
// (12) create injiziert den eigenen Mandanten.
const created = await dbA.role.create({ data: { key: `${KEY}-2`, name: "Neu A", tenantId: tenantB.id } });
ok(created.tenantId === tenantA.id, "create über dbForTenant(A) setzt tenantId=A (auch bei fremdem Input)");
} }
main() main()
+12 -12
View File
@@ -3,7 +3,7 @@
// F-13: Ein `role:manage`-Inhaber (z. B. tenant-admin) konnte über `createRole`/ // F-13: Ein `role:manage`-Inhaber (z. B. tenant-admin) konnte über `createRole`/
// `updateRolePermissions` eine Rolle mit BELIEBIGEN Katalog-Rechten bauen und sie sich // `updateRolePermissions` eine Rolle mit BELIEBIGEN Katalog-Rechten bauen und sie sich
// über `setUserRoles` selbst zuweisen — obwohl der tenant-admin bewusst NICHT über // über `setUserRoles` selbst zuweisen — obwohl der tenant-admin bewusst NICHT über
// `policy:approve`/`risk:accept`/`soa:write` verfügt. Der Fix begrenzt vergebbare Rechte // `report:approve`/`work_order:release_billing`/`customer:merge` verfügt. Der Fix begrenzt vergebbare Rechte
// auf die eigene effektive Rechtemenge (autoritativ aus der DB) und unterbindet die // auf die eigene effektive Rechtemenge (autoritativ aus der DB) und unterbindet die
// Selbstzuweisung höher privilegierter Rollen. // Selbstzuweisung höher privilegierter Rollen.
// //
@@ -62,10 +62,10 @@ async function main() {
const tenant = await prisma.tenant.create({ data: { name: "F13 AuthZ Test", slug: SLUG } }); const tenant = await prisma.tenant.create({ data: { name: "F13 AuthZ Test", slug: SLUG } });
// tenant-admin-typische Rechte (bewusst OHNE policy:approve / risk:accept / soa:write). // tenant-admin-typische Rechte (bewusst OHNE report:approve / work_order:release_billing / customer:merge).
const adminPermKeys = ["tenant:manage", "user:read", "user:manage", "role:manage", "report:read"]; const adminPermKeys = ["tenant:manage", "user:read", "user:manage", "role:manage", "report:read"];
const adminPerms = await Promise.all(adminPermKeys.map(ensurePerm)); const adminPerms = await Promise.all(adminPermKeys.map(ensurePerm));
const escalationTargets = await Promise.all(["policy:approve", "risk:accept", "soa:write"].map(ensurePerm)); const escalationTargets = await Promise.all(["report:approve", "work_order:release_billing", "customer:merge"].map(ensurePerm));
const adminRole = await prisma.role.create({ const adminRole = await prisma.role.create({
data: { data: {
@@ -84,10 +84,10 @@ async function main() {
}, },
}); });
// Höher privilegierte Rolle (ISB-artig), die der Actor sich NICHT selbst geben darf. // Höher privilegierte Rolle (Backoffice-artig), die der Actor sich NICHT selbst geben darf.
const isbRole = await prisma.role.create({ const isbRole = await prisma.role.create({
data: { data: {
tenantId: tenant.id, key: "isb", name: "ISB / CISO", tenantId: tenant.id, key: "backoffice", name: "Backoffice",
rolePermissions: { create: escalationTargets.map((p) => ({ permissionId: p.id })) }, rolePermissions: { create: escalationTargets.map((p) => ({ permissionId: p.id })) },
}, },
}); });
@@ -96,29 +96,29 @@ async function main() {
// (1) Effektive Menge stimmt und enthält bewusst NICHT die kritischen Rechte. // (1) Effektive Menge stimmt und enthält bewusst NICHT die kritischen Rechte.
ok(effective.has("role:manage"), "(1) Actor hat role:manage"); ok(effective.has("role:manage"), "(1) Actor hat role:manage");
ok(!effective.has("policy:approve") && !effective.has("risk:accept") && !effective.has("soa:write"), ok(!effective.has("report:approve") && !effective.has("work_order:release_billing") && !effective.has("customer:merge"),
"(1) Actor hat bewusst KEIN policy:approve/risk:accept/soa:write"); "(1) Actor hat bewusst KEIN report:approve/work_order:release_billing/customer:merge");
// (2) createRole/updateRolePermissions: Delegation ist bewusst ERLAUBT (pragmatische // (2) createRole/updateRolePermissions: Delegation ist bewusst ERLAUBT (pragmatische
// F-13-Variante) — ein Admin darf Rollen mit Rechten oberhalb seines Niveaus für ANDERE // F-13-Variante) — ein Admin darf Rollen mit Rechten oberhalb seines Niveaus für ANDERE
// anlegen (z. B. ISB klonen+bearbeiten). Die Escalation-Abwehr sitzt in der Selbst- // anlegen (z. B. Backoffice klonen+bearbeiten). Die Escalation-Abwehr sitzt in der Selbst-
// zuweisung (4). Hier nur festhalten, dass die kritischen Rechte tatsächlich außerhalb // zuweisung (4). Hier nur festhalten, dass die kritischen Rechte tatsächlich außerhalb
// des eigenen Niveaus liegen (sonst wäre der Test bedeutungslos). // des eigenen Niveaus liegen (sonst wäre der Test bedeutungslos).
ok(excessOf(["policy:approve", "risk:accept"], effective).length === 2, ok(excessOf(["report:approve", "work_order:release_billing"], effective).length === 2,
"(2) policy:approve/risk:accept liegen außerhalb des Actor-Niveaus (Delegation dennoch erlaubt)"); "(2) report:approve/work_order:release_billing liegen außerhalb des Actor-Niveaus (Delegation dennoch erlaubt)");
// (3) Die für ANDERE delegierbaren Rechte sind nicht künstlich beschnitten. // (3) Die für ANDERE delegierbaren Rechte sind nicht künstlich beschnitten.
ok(excessOf(["user:read", "report:read"], effective).length === 0, ok(excessOf(["user:read", "report:read"], effective).length === 0,
"(3) Vergabe eigener Rechte (user:read/report:read) ist ohnehin zulässig"); "(3) Vergabe eigener Rechte (user:read/report:read) ist ohnehin zulässig");
// (4) Selbstzuweisung der höher privilegierten ISB-Rolle → blockiert (Kern-Abwehr). // (4) Selbstzuweisung der höher privilegierten Backoffice-Rolle → blockiert (Kern-Abwehr).
const isbPerms = new Set( const isbPerms = new Set(
(await prisma.rolePermission.findMany({ where: { roleId: isbRole.id }, select: { permission: { select: { key: true } } } })) (await prisma.rolePermission.findMany({ where: { roleId: isbRole.id }, select: { permission: { select: { key: true } } } }))
.map((r) => r.permission.key), .map((r) => r.permission.key),
); );
const selfAssignExcess = [...isbPerms].filter((p) => !effective.has(p)); const selfAssignExcess = [...isbPerms].filter((p) => !effective.has(p));
ok(selfAssignExcess.length > 0, ok(selfAssignExcess.length > 0,
"(4) Selbstzuweisung der ISB-Rolle bringt Rechte oberhalb des eigenen Niveaus → blockiert"); "(4) Selbstzuweisung der Backoffice-Rolle bringt Rechte oberhalb des eigenen Niveaus → blockiert");
// (5) Selbstzuweisung der eigenen (bereits gehaltenen) Admin-Rolle → erlaubt (⊆ effektiv). // (5) Selbstzuweisung der eigenen (bereits gehaltenen) Admin-Rolle → erlaubt (⊆ effektiv).
const adminRolePerms = new Set( const adminRolePerms = new Set(
-44
View File
@@ -1,44 +0,0 @@
// Akzeptanztest des VDA-ISA-Export-Moduls (Story B7-2, C9 §3). Reine Logik.
// Lauf: npx tsx scripts/test-vda-isa.ts (Exit 1 bei Fehler).
import { pruefzielOfControl, sortForExport, toCatalogCsv, kennzahlen, type ExportControl } from "../src/lib/export/vda-isa";
let failures = 0;
const ok = (c: boolean, m: string) => { console.log(`${c ? "✓" : "✗ FEHLER"} ${m}`); if (!c) failures++; };
const mk = (control: string, reifegrad: number | null, bestaetigt: boolean): ExportControl => ({
control, frage: `Frage ${control}`, reifegrad, bestaetigt, umsetzung: "u", belege: "b", offenePunkte: 0, pruefziel: pruefzielOfControl(control),
});
// — Prüfziel-Zuordnung —
ok(pruefzielOfControl("4.1.2") === "informationssicherheit", "4.1.2 → Informationssicherheit");
ok(pruefzielOfControl("8.1.1") === "prototypenschutz", "8.1.1 → Prototypenschutz");
ok(pruefzielOfControl("9.2.1") === "datenschutz", "9.2.1 → Datenschutz");
// — Sortierung IS → Proto → DS —
{
const sorted = sortForExport([mk("9.1.1", 2, true), mk("8.1.1", 1, true), mk("1.1.1", 3, true), mk("2.1.1", 2, true)]);
ok(sorted.map((r) => r.control).join(",") === "1.1.1,2.1.1,8.1.1,9.1.1", "Reihenfolge IS→Proto→DS, dann numerisch");
}
// — CSV: BOM, Header, Status-Markierung, Semikolon —
{
const csv = toCatalogCsv([mk("1.1.1", 3, true), mk("1.1.2", null, false)]);
ok(csv.charCodeAt(0) === 0xfeff, "CSV beginnt mit UTF-8-BOM");
ok(csv.includes("Control-ID;Kontrollfrage/Ziel;Reifegrad;Status"), "Header vorhanden");
ok(/1\.1\.1;[^;]*;3;bestätigt/.test(csv), "bestätigtes Control mit Reifegrad 3");
ok(/1\.1\.2;[^;]*;na;unbestätigt/.test(csv), "unbestätigt → na + Markierung");
}
// — Kennzahlen: „unbestätigt zählt als 0" —
{
const k = kennzahlen([mk("1.1.1", 3, true), mk("1.1.2", 3, false), mk("8.1.1", 2, true)]);
ok(k.total === 3 && k.bestaetigt === 2, "total 3, bestätigt 2");
ok(Math.abs(k.gesamtAvg! - (3 + 0 + 2) / 3) < 1e-9, "Ø gesamt = (3+0+2)/3 (unbestätigt=0)");
const is = k.jePruefziel.find((p) => p.pruefziel === "informationssicherheit")!;
ok(is.controls === 2 && is.bestaetigt === 1 && Math.abs(is.avg! - 1.5) < 1e-9, "IS: 2 Controls, 1 bestätigt, Ø 1,5");
ok(k.jePruefziel.some((p) => p.pruefziel === "prototypenschutz"), "Prototyp-Prüfziel vorhanden");
}
console.log(failures === 0 ? "\nOK — alle VDA-ISA-Export-Tests grün" : `\nPRUEFEN — ${failures} Fehler`);
process.exit(failures === 0 ? 0 : 1);
@@ -1,47 +0,0 @@
# Central Evidence Register – ISMS {{ORG_NAME}}
| Document information | Value |
|-----------------------|------|
| Document type | Evidence register (applies to all policies) |
| Version | {{DOC_VERSION}} |
| Date | {{DOC_DATE}} |
| Status | {{DOC_STATUS}} |
| Responsible | {{ROLE_ISB}} |
## Purpose
This document consolidates **all evidence** for the ISMS in one place. The individual policies do not contain evidence lists but refer to this ({{LINK:NACHWEISREGISTER}}). Registers with ongoing records (asset inventory, risk register, supplier/NDA register, awareness evidence, record of processing activities) are maintained in **{{TOOL_NAME}}**; this register refers to them.
The fine-grained mapping of evidence ↔ individual MUST/SHOULD requirement is done via `mapping.json` (hidden anchors `REQ`/`IMPL`) and is not visible in reading mode. From this, the tool generates an evidence link per requirement.
## Evidence overview
| # | Policy | Evidence | Source | Responsible | Cycle |
|---|-----------|----------|--------|----------------|--------|
| 1 | {{LINK:L00}} | Approved, versioned IS policy incl. communication record | Doc | {{ROLE_MANAGEMENT}} | {{REVIEW_CYCLE}} |
| 2 | {{LINK:R01}} | ISMS scope, role/responsibility matrix, management review | Tool/Doc | {{ROLE_ISB}} | annually |
| 3 | {{LINK:R02}} | Asset inventory & classification matrix; list of approved hardware/software | Tool | {{ROLE_IT_LEAD}} | ongoing |
| 4 | {{LINK:R03}} | Risk register & treatment plan; internal/independent review reports | Tool/Doc | {{ROLE_ISB}} | ≤ annually |
| 5 | {{LINK:R04}} | Incident records; crisis/emergency plan; recovery tests | Tool/Doc | {{ROLE_ISB}} | ongoing |
| 6 | {{LINK:R05}} | Confidentiality obligations; training/awareness evidence | Tool | {{ROLE_HR_LEAD}} | upon joining / annually |
| 7 | {{LINK:R06}} | Rule & approvals for mobile working / mobile devices | Doc/Tool | {{ROLE_ISB}} | ongoing |
| 8 | {{LINK:R07}} | Access concept, zone plan, access logs | Doc | {{ROLE_IT_LEAD}} | ongoing |
| 9 | {{LINK:R08}} | Authorisation concept & recertification evidence | Tool | {{ROLE_IT_LEAD}} | ≤ annually |
| 10 | {{LINK:R09}} | Cryptography concept, key/certificate management | Doc | {{ROLE_IT_LEAD}} | ongoing |
| 11 | {{LINK:R10}} | Change/patch/vulnerability reports, malware status, logging, backup/recovery tests | Tool/rec. | {{ROLE_IT_LEAD}} | ongoing |
| 12 | {{LINK:R11}} | Security requirements procurement/development; deletion evidence | Doc/rec. | {{ROLE_IT_LEAD}} | ongoing |
| 13 | {{LINK:R12}} | Approval list for cloud/AI services, segregation/data-flow evidence | Tool/Doc | {{ROLE_ISB}} | ongoing |
| 14 | {{LINK:R13}} | Supplier register, NDAs, delineation of responsibilities | Tool | {{ROLE_ISB}} | ongoing |
| 15 | {{LINK:R14}} | Compliance/legal register; record of processing activities | Tool | {{ROLE_DPO}} | ≤ annually |
| 16 | {{LINK:VA-22}} | Metrics sheet with targets, owners and measured values per period | Tool | {{ROLE_ISB}} | {{MGMT_REVIEW_CYCLE}} |
| 17 | {{LINK:VA-22}} | Management review minutes with inputs, decisions and due dates | Tool/Doc | {{ROLE_MANAGEMENT}} | {{MGMT_REVIEW_CYCLE}} |
| 18 | {{LINK:VA-21}} | Action register: nonconformities, root cause analysis, effectiveness review | Tool | {{ROLE_ISB}} | ongoing |
| 19 | {{LINK:R03}} | Statement of Applicability with justification, origin and implementation status | Tool/Doc | {{ROLE_ISB}} | {{RISK_REVIEW_CYCLE}} |
| 20 | {{LINK:L00}} | Read receipts of staff per approved policy version | Tool | {{ROLE_ISB}} | {{POLICY_REVIEW_CYCLE}} |
> **Wizard note:** Rows with source `Tool` are not generated as a document but link to the record in {{TOOL_NAME}}. Conditional evidence is shown/hidden based on the feature flags (e.g. row 13 only with flag `FLAG_CLOUD_USED` / `FLAG_AI_USED`, row 10 only with flag `FLAG_CRYPTO_PKI`).
## Related documents
- ISA mapping matrix: {{LINK:ISA_MAPPING}}
- Information security policy: {{LINK:L00}}
@@ -1,135 +0,0 @@
# Statement of Applicability
| Document information | Value |
|-----------------------|------|
| Document type | Statement of Applicability |
| Scope | {{ISMS_SCOPE}} |
| Organisation | {{ORG_NAME}} |
| Responsible | {{ROLE_ISB}} |
| Approved by | {{ROLE_MANAGEMENT}} |
| Version | {{DOC_VERSION}} |
| Date | {{DOC_DATE}} |
| Status | {{DOC_STATUS}} |
## Purpose
For each control of Annex A of ISO/IEC 27001:2022 this statement records whether it is applicable, why it was included or excluded, what it derives from and how far it is implemented (ISO/IEC 27001:2022, 6.1.3 d). It is updated with every risk assessment ({{RISK_REVIEW_CYCLE}}) and approved by {{ROLE_MANAGEMENT}}. The procedure is set out in {{LINK:R03}}.
**Columns:** *Applicable* = yes/no · *Justification* = reason for inclusion or exclusion · *Origin* = risk ID, legal or contractual requirement · *Status* = implemented / partial / planned · *Evidence* = reference into the evidence register ({{LINK:NACHWEISREGISTER}}).
## A.5 Organisational controls (37 controls)
| Control | Title | Applicable | Justification | Origin | Status | Policy | Procedure | Evidence |
|---|---|:--:|---|---|---|---|---|---|
| A.5.1 | Policies for information security | yes | Determined as necessary by the risk treatment. | | | {{LINK:L00}} | - | |
| A.5.2 | Information security roles and responsibilities | yes | Determined as necessary by the risk treatment. | | | {{LINK:R01}} | - | |
| A.5.3 | Segregation of duties | yes | Determined as necessary by the risk treatment. | | | {{LINK:R01}} | - | |
| A.5.4 | Management responsibilities | yes | Determined as necessary by the risk treatment. | | | {{LINK:R01}} | - | |
| A.5.5 | Contact with authorities | yes | Determined as necessary by the risk treatment. | | | {{LINK:R01}} | - | |
| A.5.6 | Contact with special interest groups | yes | Determined as necessary by the risk treatment. | | | {{LINK:R01}} | - | |
| A.5.7 | Threat intelligence | yes | Determined as necessary by the risk treatment. | | | {{LINK:R10}} | - | |
| A.5.8 | Information security in project management | yes | Determined as necessary by the risk treatment. | | | {{LINK:R01}} | VA-19 | |
| A.5.9 | Inventory of information and other associated assets | yes | Determined as necessary by the risk treatment. | | | {{LINK:R02}} | VA-08 | |
| A.5.10 | Acceptable use of information and other associated assets | yes | Determined as necessary by the risk treatment. | | | {{LINK:R02}} | VA-08 | |
| A.5.11 | Return of assets | yes | Determined as necessary by the risk treatment. | | | {{LINK:R11}} | VA-08 | |
| A.5.12 | Classification of information | yes | Determined as necessary by the risk treatment. | | | {{LINK:R02}} | VA-08 | |
| A.5.13 | Labelling of information | yes | Determined as necessary by the risk treatment. | | | {{LINK:R02}} | VA-08 | |
| A.5.14 | Information transfer | yes | Determined as necessary by the risk treatment. | | | {{LINK:R09}} | - | |
| A.5.15 | Access control | yes | Determined as necessary by the risk treatment. | | | {{LINK:R08}} | VA-03 | |
| A.5.16 | Identity management | yes | Determined as necessary by the risk treatment. | | | {{LINK:R08}} | VA-03 | |
| A.5.17 | Authentication information | yes | Determined as necessary by the risk treatment. | | | {{LINK:R08}} | VA-03 | |
| A.5.18 | Access rights | yes | Determined as necessary by the risk treatment. | | | {{LINK:R08}} | VA-03 | |
| A.5.19 | Information security in supplier relationships | yes | Determined as necessary by the risk treatment. | | | {{LINK:R13}} | VA-10 | |
| A.5.20 | Addressing information security within supplier agreements | yes | Determined as necessary by the risk treatment. | | | {{LINK:R13}} | VA-10 | |
| A.5.21 | Managing information security in the ICT supply chain | yes | Determined as necessary by the risk treatment. | | | {{LINK:R13}} | VA-10 | |
| A.5.22 | Monitoring, review and change management of supplier services | yes | Determined as necessary by the risk treatment. | | | {{LINK:R13}} | VA-10 | |
| A.5.23 | Information security for use of cloud services | {{#if FLAG_CLOUD_USED}}yes{{/if}}{{#unless FLAG_CLOUD_USED}}no{{/unless}} | {{#if FLAG_CLOUD_USED}}Determined as necessary by the risk treatment.{{/if}}{{#unless FLAG_CLOUD_USED}}Not applicable - enter justification.{{/unless}} | | | {{LINK:R12}} | VA-11 | |
| A.5.24 | Information security incident management planning and preparation | yes | Determined as necessary by the risk treatment. | | | {{LINK:R04}} | VA-01 | |
| A.5.25 | Assessment and decision on information security events | yes | Determined as necessary by the risk treatment. | | | {{LINK:R04}} | VA-01 | |
| A.5.26 | Response to information security incidents | yes | Determined as necessary by the risk treatment. | | | {{LINK:R04}} | VA-01 | |
| A.5.27 | Learning from information security incidents | yes | Determined as necessary by the risk treatment. | | | {{LINK:R04}} | VA-01 | |
| A.5.28 | Collection of evidence | yes | Determined as necessary by the risk treatment. | | | {{LINK:R04}} | VA-01 | |
| A.5.29 | Information security during disruption | yes | Determined as necessary by the risk treatment. | | | {{LINK:R04}} | VA-02 | |
| A.5.30 | ICT readiness for business continuity | yes | Determined as necessary by the risk treatment. | | | {{LINK:R04}} | VA-02 | |
| A.5.31 | Legal, statutory, regulatory and contractual requirements | yes | Determined as necessary by the risk treatment. | | | {{LINK:R14}} | VA-18 | |
| A.5.32 | Intellectual property rights | yes | Determined as necessary by the risk treatment. | | | {{LINK:R14}} | VA-18 | |
| A.5.33 | Protection of records | yes | Determined as necessary by the risk treatment. | | | {{LINK:R14}} | VA-18 | |
| A.5.34 | Privacy and protection of PII | {{#if FLAG_PERSONAL_DATA}}yes{{/if}}{{#unless FLAG_PERSONAL_DATA}}no{{/unless}} | {{#if FLAG_PERSONAL_DATA}}Determined as necessary by the risk treatment.{{/if}}{{#unless FLAG_PERSONAL_DATA}}Not applicable - enter justification.{{/unless}} | | | {{LINK:R14}} | VA-18 | |
| A.5.35 | Independent review of information security | yes | Determined as necessary by the risk treatment. | | | {{LINK:R03}} | VA-15 | |
| A.5.36 | Compliance with policies, rules and standards for information security | yes | Determined as necessary by the risk treatment. | | | {{LINK:R03}} | VA-15 | |
| A.5.37 | Documented operating procedures | yes | Determined as necessary by the risk treatment. | | | {{LINK:R10}} | - | |
## A.6 People controls (8 controls)
| Control | Title | Applicable | Justification | Origin | Status | Policy | Procedure | Evidence |
|---|---|:--:|---|---|---|---|---|---|
| A.6.1 | Screening | yes | Determined as necessary by the risk treatment. | | | {{LINK:R05}} | VA-14 | |
| A.6.2 | Terms and conditions of employment | yes | Determined as necessary by the risk treatment. | | | {{LINK:R05}} | VA-14 | |
| A.6.3 | Information security awareness, education and training | yes | Determined as necessary by the risk treatment. | | | {{LINK:R05}} | VA-12 | |
| A.6.4 | Disciplinary process | yes | Determined as necessary by the risk treatment. | | | {{LINK:R05}} | - | |
| A.6.5 | Responsibilities after termination or change of employment | yes | Determined as necessary by the risk treatment. | | | {{LINK:R05}} | VA-14 | |
| A.6.6 | Confidentiality or non-disclosure agreements | yes | Determined as necessary by the risk treatment. | | | {{LINK:R05}} | VA-14 | |
| A.6.7 | Remote working | {{#if FLAG_MOBILE_WORK}}yes{{/if}}{{#unless FLAG_MOBILE_WORK}}no{{/unless}} | {{#if FLAG_MOBILE_WORK}}Determined as necessary by the risk treatment.{{/if}}{{#unless FLAG_MOBILE_WORK}}Not applicable - enter justification.{{/unless}} | | | {{LINK:R06}} | - | |
| A.6.8 | Information security event reporting | yes | Determined as necessary by the risk treatment. | | | {{LINK:R04}} | VA-01 | |
## A.7 Physical controls (14 controls)
| Control | Title | Applicable | Justification | Origin | Status | Policy | Procedure | Evidence |
|---|---|:--:|---|---|---|---|---|---|
| A.7.1 | Physical security perimeters | yes | Determined as necessary by the risk treatment. | | | {{LINK:R07}} | VA-17 | |
| A.7.2 | Physical entry | yes | Determined as necessary by the risk treatment. | | | {{LINK:R07}} | VA-17 | |
| A.7.3 | Securing offices, rooms and facilities | yes | Determined as necessary by the risk treatment. | | | {{LINK:R07}} | VA-17 | |
| A.7.4 | Physical security monitoring | yes | Determined as necessary by the risk treatment. | | | {{LINK:R07}} | VA-17 | |
| A.7.5 | Protecting against physical and environmental threats | yes | Determined as necessary by the risk treatment. | | | {{LINK:R07}} | VA-17 | |
| A.7.6 | Working in secure areas | yes | Determined as necessary by the risk treatment. | | | {{LINK:R07}} | VA-17 | |
| A.7.7 | Clear desk and clear screen | yes | Determined as necessary by the risk treatment. | | | {{LINK:R07}} | VA-17 | |
| A.7.8 | Equipment siting and protection | yes | Determined as necessary by the risk treatment. | | | {{LINK:R07}} | VA-17 | |
| A.7.9 | Security of assets off-premises | {{#if FLAG_MOBILE_DEVICES}}yes{{/if}}{{#unless FLAG_MOBILE_DEVICES}}no{{/unless}} | {{#if FLAG_MOBILE_DEVICES}}Determined as necessary by the risk treatment.{{/if}}{{#unless FLAG_MOBILE_DEVICES}}Not applicable - enter justification.{{/unless}} | | | {{LINK:R06}} | - | |
| A.7.10 | Storage media | yes | Determined as necessary by the risk treatment. | | | {{LINK:R06}} | VA-08 | |
| A.7.11 | Supporting utilities | yes | Determined as necessary by the risk treatment. | | | {{LINK:R07}} | VA-17 | |
| A.7.12 | Cabling security | yes | Determined as necessary by the risk treatment. | | | {{LINK:R07}} | VA-17 | |
| A.7.13 | Equipment maintenance | yes | Determined as necessary by the risk treatment. | | | {{LINK:R07}} | VA-04 | |
| A.7.14 | Secure disposal or re-use of equipment | yes | Determined as necessary by the risk treatment. | | | {{LINK:R11}} | VA-08 | |
## A.8 Technological controls (34 controls)
| Control | Title | Applicable | Justification | Origin | Status | Policy | Procedure | Evidence |
|---|---|:--:|---|---|---|---|---|---|
| A.8.1 | User endpoint devices | yes | Determined as necessary by the risk treatment. | | | {{LINK:R06}} | - | |
| A.8.2 | Privileged access rights | yes | Determined as necessary by the risk treatment. | | | {{LINK:R08}} | VA-03 | |
| A.8.3 | Information access restriction | yes | Determined as necessary by the risk treatment. | | | {{LINK:R08}} | VA-03 | |
| A.8.4 | Access to source code | {{#if FLAG_DEV_INHOUSE}}yes{{/if}}{{#unless FLAG_DEV_INHOUSE}}no{{/unless}} | {{#if FLAG_DEV_INHOUSE}}Determined as necessary by the risk treatment.{{/if}}{{#unless FLAG_DEV_INHOUSE}}Not applicable - enter justification.{{/unless}} | | | {{LINK:R11}} | VA-16 | |
| A.8.5 | Secure authentication | yes | Determined as necessary by the risk treatment. | | | {{LINK:R08}} | VA-03 | |
| A.8.6 | Capacity management | yes | Determined as necessary by the risk treatment. | | | {{LINK:R10}} | - | |
| A.8.7 | Protection against malware | yes | Determined as necessary by the risk treatment. | | | {{LINK:R10}} | - | |
| A.8.8 | Management of technical vulnerabilities | yes | Determined as necessary by the risk treatment. | | | {{LINK:R10}} | VA-06 | |
| A.8.9 | Configuration management | yes | Determined as necessary by the risk treatment. | | | {{LINK:R10}} | VA-04 | |
| A.8.10 | Information deletion | yes | Determined as necessary by the risk treatment. | | | {{LINK:R11}} | VA-08 | |
| A.8.11 | Data masking | {{#if FLAG_PERSONAL_DATA}}yes{{/if}}{{#unless FLAG_PERSONAL_DATA}}no{{/unless}} | {{#if FLAG_PERSONAL_DATA}}Determined as necessary by the risk treatment.{{/if}}{{#unless FLAG_PERSONAL_DATA}}Not applicable - enter justification.{{/unless}} | | | {{LINK:R02}} | - | |
| A.8.12 | Data leakage prevention | yes | Determined as necessary by the risk treatment. | | | {{LINK:R10}} | - | |
| A.8.13 | Information backup | yes | Determined as necessary by the risk treatment. | | | {{LINK:R10}} | VA-05 | |
| A.8.14 | Redundancy of information processing facilities | yes | Determined as necessary by the risk treatment. | | | {{LINK:R04}} | VA-02 | |
| A.8.15 | Logging | yes | Determined as necessary by the risk treatment. | | | {{LINK:R10}} | VA-13 | |
| A.8.16 | Monitoring activities | yes | Determined as necessary by the risk treatment. | | | {{LINK:R10}} | VA-13 | |
| A.8.17 | Clock synchronisation | yes | Determined as necessary by the risk treatment. | | | {{LINK:R10}} | VA-13 | |
| A.8.18 | Use of privileged utility programs | yes | Determined as necessary by the risk treatment. | | | {{LINK:R08}} | VA-03 | |
| A.8.19 | Installation of software on operational systems | yes | Determined as necessary by the risk treatment. | | | {{LINK:R02}} | VA-04 | |
| A.8.20 | Networks security | yes | Determined as necessary by the risk treatment. | | | {{LINK:R10}} | - | |
| A.8.21 | Security of network services | yes | Determined as necessary by the risk treatment. | | | {{LINK:R11}} | - | |
| A.8.22 | Segregation of networks | yes | Determined as necessary by the risk treatment. | | | {{LINK:R10}} | - | |
| A.8.23 | Web filtering | yes | Determined as necessary by the risk treatment. | | | {{LINK:R10}} | - | |
| A.8.24 | Use of cryptography | yes | Determined as necessary by the risk treatment. | | | {{LINK:R09}} | VA-07 | |
| A.8.25 | Secure development life cycle | {{#if FLAG_DEV_INHOUSE}}yes{{/if}}{{#unless FLAG_DEV_INHOUSE}}no{{/unless}} | {{#if FLAG_DEV_INHOUSE}}Determined as necessary by the risk treatment.{{/if}}{{#unless FLAG_DEV_INHOUSE}}Not applicable - enter justification.{{/unless}} | | | {{LINK:R11}} | VA-16 | |
| A.8.26 | Application security requirements | {{#if FLAG_DEV_INHOUSE}}yes{{/if}}{{#unless FLAG_DEV_INHOUSE}}no{{/unless}} | {{#if FLAG_DEV_INHOUSE}}Determined as necessary by the risk treatment.{{/if}}{{#unless FLAG_DEV_INHOUSE}}Not applicable - enter justification.{{/unless}} | | | {{LINK:R11}} | VA-16 | |
| A.8.27 | Secure system architecture and engineering principles | {{#if FLAG_DEV_INHOUSE}}yes{{/if}}{{#unless FLAG_DEV_INHOUSE}}no{{/unless}} | {{#if FLAG_DEV_INHOUSE}}Determined as necessary by the risk treatment.{{/if}}{{#unless FLAG_DEV_INHOUSE}}Not applicable - enter justification.{{/unless}} | | | {{LINK:R11}} | VA-16 | |
| A.8.28 | Secure coding | {{#if FLAG_DEV_INHOUSE}}yes{{/if}}{{#unless FLAG_DEV_INHOUSE}}no{{/unless}} | {{#if FLAG_DEV_INHOUSE}}Determined as necessary by the risk treatment.{{/if}}{{#unless FLAG_DEV_INHOUSE}}Not applicable - enter justification.{{/unless}} | | | {{LINK:R11}} | VA-16 | |
| A.8.29 | Security testing in development and acceptance | {{#if FLAG_DEV_INHOUSE}}yes{{/if}}{{#unless FLAG_DEV_INHOUSE}}no{{/unless}} | {{#if FLAG_DEV_INHOUSE}}Determined as necessary by the risk treatment.{{/if}}{{#unless FLAG_DEV_INHOUSE}}Not applicable - enter justification.{{/unless}} | | | {{LINK:R11}} | VA-16 | |
| A.8.30 | Outsourced development | {{#if FLAG_DEV_INHOUSE}}yes{{/if}}{{#unless FLAG_DEV_INHOUSE}}no{{/unless}} | {{#if FLAG_DEV_INHOUSE}}Determined as necessary by the risk treatment.{{/if}}{{#unless FLAG_DEV_INHOUSE}}Not applicable - enter justification.{{/unless}} | | | {{LINK:R11}} | VA-16 | |
| A.8.31 | Separation of development, test and production environments | {{#if FLAG_DEV_INHOUSE}}yes{{/if}}{{#unless FLAG_DEV_INHOUSE}}no{{/unless}} | {{#if FLAG_DEV_INHOUSE}}Determined as necessary by the risk treatment.{{/if}}{{#unless FLAG_DEV_INHOUSE}}Not applicable - enter justification.{{/unless}} | | | {{LINK:R10}} | VA-16 | |
| A.8.32 | Change management | yes | Determined as necessary by the risk treatment. | | | {{LINK:R10}} | VA-04 | |
| A.8.33 | Test information | {{#if FLAG_DEV_INHOUSE}}yes{{/if}}{{#unless FLAG_DEV_INHOUSE}}no{{/unless}} | {{#if FLAG_DEV_INHOUSE}}Determined as necessary by the risk treatment.{{/if}}{{#unless FLAG_DEV_INHOUSE}}Not applicable - enter justification.{{/unless}} | | | {{LINK:R11}} | VA-16 | |
| A.8.34 | Protection of information systems during audit testing | yes | Determined as necessary by the risk treatment. | | | {{LINK:R10}} | VA-15 | |
## Management system requirements (clauses 4-10)
The requirements of clauses 4 to 10 are not subject to the Statement of Applicability; they apply directly. Their allocation to the policies is held in `mapping-iso.json`.
@@ -1,113 +0,0 @@
# Technical Security Baseline
| Document information | Value |
|-----------------------|------|
| Document type | Requirements document (baseline) |
| Scope | {{ISMS_SCOPE}} |
| Organisation | {{ORG_NAME}} |
| Responsible | {{ROLE_IT_LEAD}} |
| Approved by | {{ROLE_ISB}} |
| Version | {{DOC_VERSION}} |
| Date | {{DOC_DATE}} |
| Status | {{DOC_STATUS}} |
## Purpose
This document defines the **concrete technical minimum parameters** of information security. It is the central point of maintenance for all measurable values (password lengths, deadlines, procedures). For exact values, the policies R01–R14 refer to the **baseline IDs** assigned here (e.g. `BL-IAM-01`) and repeat the key statement concretely in the respective implementation text.
Changes to parameters are made exclusively here and are approved by {{ROLE_ISB}}. The values are stored as adjustable defaults (variables); they correspond to the state of the art (guided, among others, by BSI IT-Grundschutz and current NIST recommendations).
## 1. Identity and access management
| ID | Parameter | Requirement |
|----|-----------|---------|
| BL-IAM-01 | Password requirements | Minimum length {{PW_MIN_LENGTH}} characters; {{PW_COMPLEXITY}}; check against known/compromised passwords; {{PW_ROTATION}} |
| BL-IAM-02 | Multi-factor authentication (MFA) | Mandatory for {{MFA_SCOPE}} |
| BL-IAM-03 | Session management | Automatic lock upon inactivity: {{SESSION_TIMEOUT}} |
| BL-IAM-04 | Account lockout | {{ACCOUNT_LOCKOUT}} |
| BL-IAM-05 | Recertification of authorisations | {{RECERT_FREQ}}; privileged rights additionally on an ad-hoc basis |
| BL-IAM-06 | Privileged/technical accounts | Separate management, individual assignment, enhanced logging; management via the central directory ({{TOOL_IAM}}) |
| BL-IAM-07 | IAM documentation location | Requests/approvals/blockings in {{TOOL_TICKET}}; account management in the central directory ({{TOOL_IAM}}) |
## 2. Cryptography and transmission
| ID | Parameter | Requirement |
|----|-----------|---------|
| BL-CRY-01 | Transport encryption | At least {{TLS_MIN}}; insecure protocols deactivated |
| BL-CRY-02 | Permissible algorithms/key lengths | {{CRYPTO_ALGO}} |
| BL-CRY-03 | Data media encryption | Full encryption of mobile devices and data media (AES-256) |
| BL-CRY-04 | Email/file exchange | Encryption of content requiring protection; secure exchange paths prescribed |
| BL-CRY-05 | Key management | Defined lifecycle (generation, distribution, storage, revocation, destruction){{#if FLAG_CRYPTO_PKI}}; PKI/certificate management established{{/if}} |
## 3. Operational security
| ID | Parameter | Requirement |
|----|-----------|---------|
| BL-OPS-01 | Patch SLA | Critical: {{PATCH_SLA_CRIT}}; high: {{PATCH_SLA_HIGH}}; standard: {{PATCH_SLA_STD}} |
| BL-OPS-02 | Vulnerability scanning | {{VULN_SCAN_FREQ}}; tracking in {{TOOL_TICKET}} |
| BL-OPS-03 | Malware protection | {{TECH_MALWARE}} on all endpoints/servers; signature/engine update {{MALWARE_UPDATE}} |
| BL-OPS-04 | Logging & retention | Central logging ({{TECH_SIEM}}); retention {{LOG_RETENTION}}; tamper-protected |
| BL-OPS-05 | Data backup | Scheme {{BACKUP_SCHEME}} via {{TECH_BACKUP}}; retention {{BACKUP_RETENTION}} |
| BL-OPS-06 | Recovery tests | {{BACKUP_TEST_FREQ}}; result documented |
| BL-OPS-07 | System hardening | Hardening requirements (e.g. CIS benchmarks) for standard systems |
| BL-OPS-08 | Technical review / penetration test | {{PENTEST_FREQ}} or risk-oriented |
| BL-OPS-09 | Change management | Request/assessment/test/approval/documentation in {{TOOL_TICKET}} |
{{#if FLAG_FW_ISO27001}}
| BL-OPS-10 | Time synchronisation | System clocks of all logging systems synchronised to {{NTP_SOURCES}}; deviations are monitored |
| BL-OPS-11 | Capacity management | Utilisation (compute, memory, bandwidth, licences) monitored {{CAPACITY_REVIEW_FREQ}}; thresholds raise an alert |
| BL-OPS-12 | Protection against data leakage | Measures against unauthorised outflow of protected information for {{DLP_SCOPE}} |
{{/if}}
## 4. Network security
| ID | Parameter | Requirement |
|----|-----------|---------|
| BL-NET-01 | Segmentation | Separation according to protection need; {{#if FLAG_OT_USED}}production/OT networks separated and specially secured; {{/if}}guest/external networks isolated |
| BL-NET-02 | Perimeter & remote access | Firewall with default deny; remote access only via {{TECH_VPN}} with MFA (BL-IAM-02) |
{{#if FLAG_FW_ISO27001}}
| BL-NET-03 | Web filtering | Access to external web content filtered (categories, known malicious sites); exceptions documented and time-limited |
{{/if}}
## 5. Endpoint and mobile use
| ID | Parameter | Requirement |
|----|-----------|---------|
| BL-EP-01 | Device management | Management via {{TECH_MDM}}; only approved devices |
| BL-EP-02 | Device encryption/remote wipe | Full encryption (BL-CRY-03); blocking/wiping upon loss via {{TECH_MDM}} |
| BL-EP-03 | Removable media | Only encrypted and approved; use controlled |
## 6. Physical security
| ID | Parameter | Requirement |
|----|-----------|---------|
| BL-PHY-01 | Security zones | Defined zones; access on a needs-oriented basis, documented, revoked when no longer needed |
| BL-PHY-02 | Access logging | Logging for areas requiring protection; visitors registered and escorted |
{{#if FLAG_FW_ISO27001}}
| BL-PHY-03 | Environmental protection and utilities | Early fire detection, protection against water, temperature/humidity monitoring in technical rooms; uninterruptible power for critical systems, tested regularly |
| BL-PHY-04 | Clear desk and screen lock | Protected documents and media locked away when unattended; automatic screen lock after {{SESSION_TIMEOUT}} |
{{/if}}
## 7. Personnel and suppliers
| ID | Parameter | Requirement |
|----|-----------|---------|
| BL-HR-01 | Awareness/training | Upon joining and thereafter at least {{REVIEW_CYCLE}}; evidence in {{TOOL_NAME}} |
| BL-SUP-01 | Supplier risk classes | Classification according to protection need and access; verification of compliance (evidence/TISAX) |
| BL-DEL-01 | Secure deletion | Deletion/destruction appropriate to the protection need (e.g. according to recognised standards); deletion evidence |
## 8. Governance and projects
| ID | Parameter | Requirement |
|----|-----------|---------|
| BL-GOV-01 | Audit/review cycle | Internal review {{REVIEW_CYCLE}}; independent review/assessment at least every 3 years or after fundamental changes |
| BL-PROJ-01 | Project classification criteria | Documented catalogue of criteria for the IS classification of projects (triggers/thresholds for ISO involvement) |
{{#if FLAG_FW_ISO27001}}
| BL-GOV-02 | Management review | Top management reviews the ISMS {{MGMT_REVIEW_CYCLE}} against a fixed agenda; decisions with owner and due date |
| BL-GOV-03 | Document control | Review cycle of the policy and thematic policies {{POLICY_REVIEW_CYCLE}}; four-eyes approval; retention of superseded versions {{RECORDS_RETENTION}} |
{{/if}}
## Change history
| Version | Date | Author | Change |
|---------|-------|-------|----------|
| {{DOC_VERSION}} | {{DOC_DATE}} | {{ROLE_IT_LEAD}} | Creation |
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,49 +0,0 @@
# Policy Data Protection
| Document information | Value |
|-----------------------|------|
| Document type | Policy |
| Scope | {{ISMS_SCOPE}} |
| Organisation | {{ORG_NAME}} |
| Responsible | {{ROLE_DPO}} |
| Approved by | {{ROLE_MANAGEMENT}} |
| Version | {{DOC_VERSION}} |
| Date | {{DOC_DATE}} |
| Status | {{DOC_STATUS}} |
## 1. Purpose
This policy governs the protection of personal data (assessment objective data protection, VDA ISA chapter 9). It elaborates the information security policy ({{LINK:L00}}) and complements the policy Compliance and Data Protection ({{LINK:R14}}).
## 2. Scope
This policy applies within the defined ISMS scope ({{ISMS_SCOPE_DESCRIPTION}}), insofar as personal data is processed.
## 3. Requirements and implementation
> Structure per section: **Requirement** (1:1 from VDA ISA, chapter 9) and **Implementation at {{ORG_NAME}}**.
{{#if FLAG_PERSONAL_DATA}}
### 3.1 Data protection organisation (ISA 9.1.1)
**Requirement**
<!-- REQ 9.1.1-M1 -->
- **[MUST]** Responsibilities for data protection are appointed and the data protection organisation is documented.
**Implementation at {{ORG_NAME}}**
The role {{ROLE_DPO}} is appointed and integrated into the ISMS organisation. Tasks, reporting paths and escalation are documented.
### 3.2 Lawfulness and record of processing activities (ISA 9.2.1)
**Requirement**
<!-- REQ 9.2.1-M1 -->
- **[MUST]** Processing of personal data is lawful, purpose-bound and recorded in a record of processing activities.
**Implementation at {{ORG_NAME}}**
A record of processing activities is maintained and updated regularly. For each processing activity, the legal basis, purpose and deletion periods are documented.
{{/if}}
@@ -1,175 +0,0 @@
# Information Security Policy
| Document information | Value |
|-----------------------|------|
| Document type | Policy |
| Scope | {{ISMS_SCOPE}} |
| Organisation | {{ORG_NAME}} |
| Responsible | {{ROLE_ISB}} |
| Approved by | {{ROLE_MANAGEMENT}} |
| Version | {{DOC_VERSION}} |
| Date | {{DOC_DATE}} |
| Status | {{DOC_STATUS}} |
## 1. Purpose
<!-- REQ 1.1.1-M1 -->
This information security policy describes the fundamental requirements, objectives and responsibilities of {{ORG_NAME}} for protecting information, IT systems, business processes and supporting assets. The information security requirements are defined, documented and aligned with the objectives of {{ORG_NAME}}.
The objective is to ensure an appropriate level of information security and to meet the requirements of VDA ISA 2027 in the area of information security.
## 2. Scope
This policy applies to the defined ISMS scope:
{{ISMS_SCOPE_DESCRIPTION}}
It applies to:
- all employees within the scope,
- managers,
- external service providers, insofar as they have access to the organisation's information, systems or processes,
- relevant IT systems, information, applications, sites and business processes within the ISMS scope.
## 3. Information security objectives
<!-- REQ 1.1.1-M3 -->
The policy states the objectives and the importance of information security. Through the ISMS, the organisation pursues in particular the following objectives:
- protection of confidential information against unauthorised access,
- ensuring the integrity of information and systems,
- ensuring the availability of business-critical information, systems and services,
- compliance with legal, regulatory and contractual requirements,
- appropriate protection of customer information, personal data, trade secrets and other information requiring protection,
- structured identification, assessment and treatment of information security risks,
- continual improvement of information security.
## 4. Information security principles
Information security is based on the following principles:
### 4.1 Risk orientation
Information security measures are planned, implemented, reviewed and improved in a risk-oriented manner. Risks are assessed and tracked in the ISMS tool in use ({{TOOL_NAME}}) (see {{LINK:R03}}).
### 4.2 Appropriateness
Protective measures must be appropriate to the protection needs of the information, systems and processes. Confidentiality, integrity and availability are taken into account.
### 4.3 Responsibility
Information security is a shared responsibility of all employees. {{ROLE_MANAGEMENT}} holds overall responsibility for the ISMS.
### 4.4 Traceability
Decisions, assessments, approvals and material measures relating to information security must be documented in a traceable manner.
### 4.5 Continual improvement
The ISMS is reviewed regularly and adjusted where necessary. Findings from audits, incidents, risks, changes and management reviews feed into the improvement.
## 5. Information security requirements
<!-- REQ 1.1.1-S1 -->
{{#if FLAG_INCLUDE_SHOULD}}The information security requirements are based on the strategy of {{ORG_NAME}}; legal and contractual requirements are taken into account. {{/if}}The organisation determines and documents information security requirements on the basis of:
- legal and regulatory requirements,
- contractual requirements, in particular from customers and partners,
- requirements from the VDA ISA,
- internal business requirements,
- results of risk analyses,
- protection needs of information, processes and IT systems,
- requirements from projects, changes and external IT services.
The relevant requirements in each case are taken into account in the ISMS and implemented through suitable policies, processes, technical measures and evidence.
## 6. Roles and responsibilities
The organisation defines roles and responsibilities for information security. These include at least:
| Role | Fundamental responsibility |
|-------|------------------------------|
| {{ROLE_MANAGEMENT}} | Overall responsibility, approval of the information security policy, provision of appropriate resources |
| {{ROLE_ISB}} | Steering, maintenance and further development of the ISMS |
| Managers | Implementation of the requirements within their respective area of responsibility |
| {{ROLE_IT_LEAD}} | Implementation of technical and organisational security measures in the IT area |
| Asset owners / process owners | Assessment and maintenance of relevant information, processes and assets in the ISMS tool |
| Employees | Compliance with the policies and reporting of security events |
| External service providers | Compliance with contractually agreed security requirements |
The specific assignment of roles and responsibilities is maintained in the ISMS tool ({{TOOL_NAME}}) or in a supplementary role matrix (see also {{LINK:R01}}).
## 7. Binding nature
<!-- REQ 1.1.1-M2 -->
<!-- REQ 1.1.1-S2 -->
This policy is approved by {{ROLE_MANAGEMENT}} and is binding for all affected persons within the scope. {{#if FLAG_INCLUDE_SHOULD}}Violations of information security requirements may lead to organisational, employment-law or contractual measures. {{/if}}All employees are obliged to:
- comply with the applicable information security policies,
- handle information requiring protection appropriately,
- report security events or suspected cases without delay,
- use only approved systems, applications and services,
- report identified vulnerabilities or risks to the responsible body.
## 8. Publication and communication
<!-- REQ 1.1.1-M4 -->
<!-- REQ 1.1.1-M5 -->
The information security policy is made known to the relevant persons in a suitable form; employees and affected external partners are informed about relevant changes. This can be done via:
- publication in the ISMS tool ({{TOOL_NAME}}),
- internal wiki or document management system,
- onboarding process,
- awareness training (see {{LINK:R05}}),
- direct communication to the affected target groups.
## 9. Review and update
<!-- REQ 1.1.1-S4 -->
This policy is reviewed regularly, but at least:
- {{REVIEW_CYCLE}},
- upon material changes to the ISMS scope,
- upon material organisational or technical changes,
- in the event of relevant security incidents,
- upon new or changed regulatory, legal or contractual requirements.
Changes are documented and approved by {{ROLE_MANAGEMENT}}.
## 10. Evidence
The evidence for the implementation of this policy is not maintained in this document but centrally in the evidence register ({{LINK:NACHWEISREGISTER}}) and in the associated entries of the ISMS tool ({{TOOL_NAME}}).
## 11. Related documents
<!-- REQ 1.1.1-S3 -->
Further topic-specific security policies (R01–R14) are established and coordinated with one another.
- Technical security baseline: {{LINK:BASELINE}}
- ISA mapping matrix: {{LINK:ISA_MAPPING}}
- Evidence register: {{LINK:NACHWEISREGISTER}}
- ISMS organisation and roles: {{LINK:R01}}
- All thematic policies: {{LINK:R01}} … {{LINK:R14}}
<!-- Das Mapping der Anforderungen (REQ/IMPL) zu VDA-ISA-Controls ist in mapping.json hinterlegt und wird vom Tool über die Hidden-Anker aufgelöst. Im Lesemodus nicht sichtbar. -->
<!-- FW:ISO-SECTION-START -->
{{#if FLAG_FW_ISO27001}}
## Annex A — Information security policy, objectives and communication
*Requirement reference:* ISO/IEC 27001 5.2, 6.2, 7.4, A.5.1
**Requirement**
<!-- REQ 5.2-1 -->
- **[ISO 5.2]** An information security policy is established that fits the organisation, sets objectives, commits to meeting requirements and to continual improvement, and is communicated and available.
<!-- REQ 6.2-1 -->
- **[ISO 6.2]** Information security objectives are established for relevant functions and levels, and their achievement is planned.
<!-- REQ 7.4-1 -->
- **[ISO 7.4]** The internal and external communications relevant to the ISMS are determined.
<!-- REQ A.5.1-1 -->
- **[ISO A.5.1]** The information security policy and topic-specific policies are defined, approved by management, published, communicated, acknowledged and reviewed at planned intervals.
**Implementation at {{ORG_NAME}}**
<!-- IMPL ISO-LEITLINIE -->
This policy is approved by {{ROLE_MANAGEMENT}}, published in {{TOOL_NAME}} and made known to all staff and relevant third parties; acknowledgement is recorded per version. It is reviewed at least {{POLICY_REVIEW_CYCLE}} and upon significant change (BL-GOV-03). The thematic policies and the procedures elaborate it and follow the same approval and review cycle. The information security objectives are stated in measurable terms and held in {{TOOL_NAME}} with target value, responsible role and due date; their achievement is evaluated {{MGMT_REVIEW_CYCLE}}. For internal and external communication on information security it is defined what is communicated, when, with whom and by whom; the central point of contact is {{ROLE_ISB}}.
{{/if}}
<!-- FW:ISO-SECTION-END -->
@@ -1,60 +0,0 @@
# Policy Prototype Protection
| Document information | Value |
|-----------------------|------|
| Document type | Policy |
| Scope | {{ISMS_SCOPE}} |
| Organisation | {{ORG_NAME}} |
| Responsible | {{ROLE_ISB}} |
| Approved by | {{ROLE_MANAGEMENT}} |
| Version | {{DOC_VERSION}} |
| Date | {{DOC_DATE}} |
| Status | {{DOC_STATUS}} |
## 1. Purpose
This policy governs the protection of prototypes and development objects requiring protection (assessment objective prototype protection, VDA ISA chapter 8). It elaborates the information security policy ({{LINK:L00}}) and is operationalised by the procedure {{LINK:VA-20}}.
## 2. Scope
This policy applies within the defined ISMS scope ({{ISMS_SCOPE_DESCRIPTION}}), insofar as prototypes or development objects requiring protection are processed, stored or transported.
## 3. Requirements and implementation
> Structure per section: **Requirement** (1:1 from VDA ISA, chapter 8) and **Implementation at {{ORG_NAME}}**.
{{#if FLAG_PROTOTYPE_PROTECTION}}
### 3.1 Physical security and perimeter (ISA 8.1.1)
**Requirement**
<!-- REQ 8.1.1-M1 -->
- **[MUST]** Areas in which prototypes are processed or stored are protected by defined security zones and an effective perimeter.
**Implementation at {{ORG_NAME}}**
Prototype areas are designated as a dedicated security zone with access control, perimeter protection and logging. Access is limited to authorised persons.
### 3.2 Confidentiality and classification (ISA 8.2.1)
**Requirement**
<!-- REQ 8.2.1-M1 -->
- **[MUST]** Confidentiality obligations exist for prototypes; the associated information is classified and labelled accordingly.
**Implementation at {{ORG_NAME}}**
All persons involved with prototypes (internal and external) sign confidentiality agreements. Prototypes and associated documents are classified as confidential or higher in accordance with the classification scheme.
### 3.3 Transport and storage (ISA 8.3.1)
**Requirement**
<!-- REQ 8.3.1-M1 -->
- **[MUST]** Transport and storage of prototypes are carried out according to documented protection requirements that ensure confidentiality and integrity.
**Implementation at {{ORG_NAME}}**
Transport and storage follow the procedure {{LINK:VA-20}}: secured containers, logged handovers, access and visual protection as well as traceability.
{{/if}}
@@ -1,294 +0,0 @@
# Policy ISMS Organisation and Roles
| Document information | Value |
|-----------------------|------|
| Document type | Policy |
| Scope | {{ISMS_SCOPE}} |
| Organisation | {{ORG_NAME}} |
| Responsible | {{ROLE_ISB}} |
| Approved by | {{ROLE_MANAGEMENT}} |
| Version | {{DOC_VERSION}} |
| Date | {{DOC_DATE}} |
| Status | {{DOC_STATUS}} |
## 1. Purpose
This policy governs the structure, steering and responsibilities of the ISMS of {{ORG_NAME}} as well as the consideration of information security in projects. It elaborates the information security policy ({{LINK:L00}}) and serves to meet the requirements of VDA ISA 2027.
## 2. Scope
This policy applies within the defined ISMS scope ({{ISMS_SCOPE_DESCRIPTION}}).
## 3. Requirements and implementation
> Structure per section: **Requirement** (1:1 from VDA ISA; [MUST]/[SHOULD] and — where the protection need applies — [HIGH]/[VERY HIGH]) and **Implementation at {{ORG_NAME}}** (consolidated, to be adjusted where necessary).
### 3.1 Steering of information security
<!-- FW:REF-START ORIG:(ISA 1.2.1) -->
*Requirement reference:* {{#if FLAG_FW_TISAX}}VDA ISA 1.2.1{{/if}}{{#if FLAG_FW_ISO27001}}{{#if FLAG_FW_TISAX}} · {{/if}}ISO/IEC 27001 5.1, A.5.4{{/if}}
<!-- FW:REF-END -->
**Requirement**
<!-- FW:TISAX-REQ-START -->
{{#if FLAG_FW_TISAX}}
{{#if FLAG_FW_ISO27001}}*Requirements per VDA ISA 2027:*{{/if}}
<!-- REQ 1.2.1-M1 -->
- **[MUST]** The scope of the ISMS (the organisation governed by the ISMS) is defined.
<!-- REQ 1.2.1-M2 -->
- **[MUST]** The organisation's requirements for the ISMS are determined.
<!-- REQ 1.2.1-M3 -->
- **[MUST]** The organisation's management has commissioned and approved the ISMS.
<!-- REQ 1.2.1-M4 -->
- **[MUST]** The ISMS provides management with suitable means for monitoring and steering (e.g. management review).
<!-- REQ 1.2.1-M5 -->
- **[MUST]** The applicable controls are determined (e.g. ISO 27001 statement of applicability or a completed ISA catalogue).
<!-- REQ 1.2.1-M6 -->
- **[MUST]** The effectiveness of the ISMS is reviewed regularly by management.
{{/if}}
<!-- FW:TISAX-REQ-END -->
<!-- FW:ISO-REQ-START -->
{{#if FLAG_FW_ISO27001}}
{{#if FLAG_FW_TISAX}}*Requirements per ISO/IEC 27001:*{{/if}}
<!-- REQ 5.1-1 -->
- **[ISO 5.1]** Top management demonstrates leadership and commitment with respect to the ISMS.
<!-- REQ A.5.4-1 -->
- **[ISO A.5.4]** Management requires all personnel to apply information security in accordance with the established requirements.
{{/if}}
<!-- FW:ISO-REQ-END -->
**Implementation at {{ORG_NAME}}**
<!-- IMPL 1.2.1 -->
The ISMS scope, the requirements and the applicable controls (statement of applicability / ISA catalogue) are documented in the ISMS tool ({{TOOL_NAME}}). {{ROLE_MANAGEMENT}} has commissioned and approved the ISMS by management decision, provides resources and reviews its effectiveness at least {{REVIEW_CYCLE}} in a documented management review; operational steering rests with {{ROLE_ISB}}.
### 3.2 Organisation of information security
<!-- FW:REF-START ORIG:(ISA 1.2.2) -->
*Requirement reference:* {{#if FLAG_FW_TISAX}}VDA ISA 1.2.2{{/if}}{{#if FLAG_FW_ISO27001}}{{#if FLAG_FW_TISAX}} · {{/if}}ISO/IEC 27001 5.3, 7.1, A.5.2, A.5.3{{/if}}
<!-- FW:REF-END -->
**Requirement**
<!-- FW:TISAX-REQ-START -->
{{#if FLAG_FW_TISAX}}
{{#if FLAG_FW_ISO27001}}*Requirements per VDA ISA 2027:*{{/if}}
<!-- REQ 1.2.2-M1 -->
- **[MUST]** Responsibilities for information security are defined, documented and assigned.
<!-- REQ 1.2.2-M2 -->
- **[MUST]** The responsible employees are defined, qualified and enabled for their task.
<!-- REQ 1.2.2-M3 -->
- **[MUST]** The necessary resources are available.
<!-- REQ 1.2.2-M4 -->
- **[MUST]** The points of contact are known within the organisation and to relevant business partners.
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.2.2-S1 -->
- **[SHOULD]** An appropriate information security structure within the organisation is defined and documented.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.2.2-S2 -->
- **[SHOULD]** Security-relevant roles that are not part of the ISMS but are relevant to information security are taken into account.
{{/if}}
{{#if FLAG_HIGH_PROTECTION}}
<!-- REQ 1.2.2-H1 -->
- **[HIGH]** An appropriate organisational separation of responsibilities is established to avoid conflicts of interest (segregation of duties). (C, I, A)
{{/if}}
{{/if}}
<!-- FW:TISAX-REQ-END -->
<!-- FW:ISO-REQ-START -->
{{#if FLAG_FW_ISO27001}}
{{#if FLAG_FW_TISAX}}*Requirements per ISO/IEC 27001:*{{/if}}
<!-- REQ 5.3-1 -->
- **[ISO 5.3]** Responsibilities and authorities for security-relevant roles are assigned and communicated.
<!-- REQ 7.1-1 -->
- **[ISO 7.1]** The resources needed for the ISMS are determined and provided.
<!-- REQ A.5.2-1 -->
- **[ISO A.5.2]** Information security roles and responsibilities are defined and allocated.
<!-- REQ A.5.3-1 -->
- **[ISO A.5.3]** Conflicting duties and areas of responsibility are segregated to reduce unauthorised or unintentional modification and misuse.
{{/if}}
<!-- FW:ISO-REQ-END -->
**Implementation at {{ORG_NAME}}**
<!-- IMPL 1.2.2 -->
Responsibilities are documented in the role/responsibility matrix and in the ISMS tool ({{TOOL_NAME}}) and made known to the role holders as well as to relevant business partners. The role {{ROLE_ISB}} is appointed, qualified, equipped with resources and authority, and reports directly to {{ROLE_MANAGEMENT}}.
{{#if FLAG_ELEVATED_PROTECTION}}
<!-- IMPL 1.2.2-elev -->
Where the protection need is high, an organisational segregation of duties (e.g. implementation vs. control) is established; unavoidable dual roles are safeguarded by compensating controls (four-eyes principle).
{{/if}}
### 3.3 Information security in projects
<!-- FW:REF-START ORIG:(ISA 1.2.3) -->
*Requirement reference:* {{#if FLAG_FW_TISAX}}VDA ISA 1.2.3{{/if}}{{#if FLAG_FW_ISO27001}}{{#if FLAG_FW_TISAX}} · {{/if}}ISO/IEC 27001 A.5.8{{/if}}
<!-- FW:REF-END -->
**Requirement**
<!-- FW:TISAX-REQ-START -->
{{#if FLAG_FW_TISAX}}
{{#if FLAG_FW_ISO27001}}*Requirements per VDA ISA 2027:*{{/if}}
<!-- REQ 1.2.3-M1 -->
- **[MUST]** Projects are classified taking information security requirements into account.
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.2.3-S1 -->
- **[SHOULD]** Procedures and criteria for classifying projects are documented.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.2.3-S2 -->
- **[SHOULD]** A risk assessment following the defined procedure is carried out in an early project phase and repeated upon project changes.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.2.3-S3 -->
- **[SHOULD]** Measures are derived for identified information security risks and taken into account in the project.
{{/if}}
{{#if FLAG_HIGH_PROTECTION}}
<!-- REQ 1.2.3-H1 -->
- **[HIGH]** The derived measures are reviewed regularly during the project and reassessed when the assessment criteria change. (C, I, A)
{{/if}}
{{/if}}
<!-- FW:TISAX-REQ-END -->
<!-- FW:ISO-REQ-START -->
{{#if FLAG_FW_ISO27001}}
{{#if FLAG_FW_TISAX}}*Requirements per ISO/IEC 27001:*{{/if}}
<!-- REQ A.5.8-1 -->
- **[ISO A.5.8]** Information security is integrated into project management.
{{/if}}
<!-- FW:ISO-REQ-END -->
**Implementation at {{ORG_NAME}}**
<!-- IMPL 1.2.3 -->
At the outset, projects are classified with regard to their information security needs on the basis of the documented catalogue of criteria (BL-PROJ-01); classification, risk assessment and derived measures are maintained in the project register ({{LINK:REG-PROJECTS}}). In an early project phase and upon changes, a risk assessment is carried out following the procedure Information Security in Projects ({{LINK:VA-19}}); measures are tracked as tasks in {{TOOL_TICKET}} and reviewed before project completion. The project management is responsible; where the protection need is elevated, {{ROLE_ISB}} is involved.
{{#if FLAG_ELEVATED_PROTECTION}}
<!-- IMPL 1.2.3-elev -->
Where the protection need is high, the derived measures are reviewed continuously over the course of the project and reassessed when the assessment criteria change.
{{/if}}
## 4. Binding nature
This policy is binding for all affected roles within the scope. Compliance is monitored by {{ROLE_ISB}}.
## 5. Roles and responsibilities
| Role | Responsibility in this policy |
|-------|-------------------------------------|
| {{ROLE_MANAGEMENT}} | Commissioning, overall responsibility, management review |
| {{ROLE_ISB}} | Operational steering of the ISMS |
| {{ROLE_IT_LEAD}} | Technical implementation |
## 6. Review and update
This policy is reviewed at least {{REVIEW_CYCLE}} and on an ad-hoc basis by {{ROLE_ISB}} and approved by {{ROLE_MANAGEMENT}}.
## 7. Evidence
The evidence is not maintained in this document but centrally in the evidence register ({{LINK:NACHWEISREGISTER}}) and in the associated entries of the ISMS tool ({{TOOL_NAME}}).
## 8. Related documents
- Technical security baseline: {{LINK:BASELINE}}
- ISA mapping matrix: {{LINK:ISA_MAPPING}}
- Evidence register: {{LINK:NACHWEISREGISTER}}
- Further: {{LINK:L00}}, {{LINK:R03}}, {{LINK:R13}}
<!-- Anforderungen 1:1 aus VDA ISA 2027; Mapping (REQ/IMPL) in mapping.json ueber Hidden-Anker. Im Lesemodus nicht sichtbar. -->
<!-- FW:ISO-SECTION-START -->
{{#if FLAG_FW_ISO27001}}
### 3.4 Context, interested parties and scope of the ISMS
*Requirement reference:* ISO/IEC 27001 4.1, 4.2, 4.3, 4.4
**Requirement**
<!-- REQ 4.1-1 -->
- **[ISO 4.1]** Internal and external issues that affect the ability to achieve the ISMS objectives are determined and kept up to date.
<!-- REQ 4.2-1 -->
- **[ISO 4.2]** The interested parties relevant to the ISMS and their information security requirements are determined.
<!-- REQ 4.3-1 -->
- **[ISO 4.3]** The scope of the ISMS is determined considering the issues, requirements and interfaces, and maintained as documented information.
<!-- REQ 4.4-1 -->
- **[ISO 4.4]** An ISMS is established, implemented, maintained and continually improved.
**Implementation at {{ORG_NAME}}**
<!-- IMPL ISO-MS-KONTEXT -->
Internal and external issues as well as the relevant interested parties and their requirements are maintained in {{TOOL_NAME}} as a context and stakeholder analysis and updated at least {{POLICY_REVIEW_CYCLE}} and upon significant change. The scope of the ISMS ({{ISMS_SCOPE}}) is documented information and names sites, processes, organisational units and IT services as well as interfaces and dependencies on third parties; exclusions are justified. The ISMS is operated according to the PDCA cycle and continually improved. Responsible: {{ROLE_ISB}}; approval: {{ROLE_MANAGEMENT}}.
{{/if}}
<!-- FW:ISO-SECTION-END -->
<!-- FW:ISO-SECTION-START -->
{{#if FLAG_FW_ISO27001}}
### 3.5 Planning of changes to the ISMS
*Requirement reference:* ISO/IEC 27001 6.3
**Requirement**
<!-- REQ 6.3-1 -->
- **[ISO 6.3]** Changes to the ISMS are carried out in a planned manner.
**Implementation at {{ORG_NAME}}**
<!-- IMPL ISO-MS-CHANGE -->
Changes to the ISMS — scope, organisation, roles, key processes or systems — are planned, assessed before implementation and documented in {{TOOL_NAME}}. The assessment covers the purpose and potential consequences of the change, effects on risks and controls, the resources required and the assignment of responsibilities. Approval is given by {{ROLE_MANAGEMENT}}; technical changes additionally run through change management (BL-OPS-09, see {{LINK:VA-04}}).
{{/if}}
<!-- FW:ISO-SECTION-END -->
<!-- FW:ISO-SECTION-START -->
{{#if FLAG_FW_ISO27001}}
### 3.6 Control of documented information
*Requirement reference:* ISO/IEC 27001 7.5.1, 7.5.2, 7.5.3
**Requirement**
<!-- REQ 7.5.1-1 -->
- **[ISO 7.5.1]** The ISMS includes the documented information required by the standard and that determined as necessary.
<!-- REQ 7.5.2-1 -->
- **[ISO 7.5.2]** When creating and updating documented information, identification, format and medium as well as review and approval are ensured.
<!-- REQ 7.5.3-1 -->
- **[ISO 7.5.3]** Documented information is controlled: availability, protection, distribution, access, retention and change control.
**Implementation at {{ORG_NAME}}**
<!-- IMPL ISO-MS-DOKU -->
The documented information of the ISMS is maintained in {{TOOL_NAME}}. Every document carries a title, a unique identifier, version, date, status, responsible role and approver; creation and modification pass through review and four-eyes approval (BL-GOV-03). Control ensures availability to the authorised roles, protection against unauthorised modification, managed distribution, version control with a change history and retention of superseded versions ({{RECORDS_RETENTION}}). Documents of external origin are identified and controlled in the same way. Review cycle: {{POLICY_REVIEW_CYCLE}}.
{{/if}}
<!-- FW:ISO-SECTION-END -->
<!-- FW:ISO-SECTION-START -->
{{#if FLAG_FW_ISO27001}}
### 3.7 Contact with authorities and interest groups
*Requirement reference:* ISO/IEC 27001 A.5.5, A.5.6
**Requirement**
<!-- REQ A.5.5-1 -->
- **[ISO A.5.5]** Appropriate contacts with relevant authorities are established and maintained.
<!-- REQ A.5.6-1 -->
- **[ISO A.5.6]** Appropriate contacts with special interest groups, professional forums and security associations are maintained.
**Implementation at {{ORG_NAME}}**
<!-- IMPL ISO-KONTAKTE -->
{{ROLE_ISB}} maintains a contact list of the relevant authorities and reporting bodies ({{AUTHORITY_CONTACTS}}) with responsibility, availability and reporting channel; it is checked for currency {{POLICY_REVIEW_CYCLE}} and is available in an emergency without IT access. Reporting obligations and deadlines are held in the incident procedure ({{LINK:VA-01}}). In addition, professional contacts with interest groups, forums and security associations are maintained; the resulting insights feed into the evaluation of threat intelligence.
{{/if}}
<!-- FW:ISO-SECTION-END -->
@@ -1,259 +0,0 @@
# Policy Asset and Classification Policy
| Document information | Value |
|-----------------------|------|
| Document type | Policy |
| Scope | {{ISMS_SCOPE}} |
| Organisation | {{ORG_NAME}} |
| Responsible | {{ROLE_IT_LEAD}} |
| Approved by | {{ROLE_MANAGEMENT}} |
| Version | {{DOC_VERSION}} |
| Date | {{DOC_DATE}} |
| Status | {{DOC_STATUS}} |
## 1. Purpose
This policy governs the identification, classification and protected handling of information assets as well as the approval of hardware and software. It elaborates the information security policy ({{LINK:L00}}) and serves to meet the requirements of VDA ISA 2027.
## 2. Scope
This policy applies within the defined ISMS scope ({{ISMS_SCOPE_DESCRIPTION}}).
## 3. Requirements and implementation
> Structure per section: **Requirement** (1:1 from VDA ISA; [MUST]/[SHOULD] and — where the protection need applies — [HIGH]/[VERY HIGH]) and **Implementation at {{ORG_NAME}}** (consolidated, to be adjusted where necessary).
### 3.1 Identification of information assets
<!-- FW:REF-START ORIG:(ISA 1.3.1) -->
*Requirement reference:* {{#if FLAG_FW_TISAX}}VDA ISA 1.3.1{{/if}}{{#if FLAG_FW_ISO27001}}{{#if FLAG_FW_TISAX}} · {{/if}}ISO/IEC 27001 A.5.9{{/if}}
<!-- FW:REF-END -->
**Requirement**
<!-- FW:TISAX-REQ-START -->
{{#if FLAG_FW_TISAX}}
{{#if FLAG_FW_ISO27001}}*Requirements per VDA ISA 2027:*{{/if}}
<!-- REQ 1.3.1-M1 -->
- **[MUST]** The organisation's information assets and other security-relevant assets are identified and recorded.
<!-- REQ 1.3.1-M2 -->
- **[MUST]** The supporting assets that process the information assets are identified and recorded.
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.3.1-S1 -->
- **[SHOULD]** A catalogue of the relevant information assets exists; the relevant aspects are taken into account.
{{/if}}
{{/if}}
<!-- FW:TISAX-REQ-END -->
<!-- FW:ISO-REQ-START -->
{{#if FLAG_FW_ISO27001}}
{{#if FLAG_FW_TISAX}}*Requirements per ISO/IEC 27001:*{{/if}}
<!-- REQ A.5.9-1 -->
- **[ISO A.5.9]** An inventory of information and associated assets, including owners, is established and maintained.
{{/if}}
<!-- FW:ISO-REQ-END -->
**Implementation at {{ORG_NAME}}**
<!-- IMPL 1.3.1 -->
Information assets and supporting assets are recorded in the ISMS tool ({{TOOL_NAME}}) in the asset inventory with attributes (owner, location, protection need) and maintained as a catalogue (see {{LINK:VA-08}}); additions and removals are triggered via {{TOOL_TICKET}}.
### 3.2 Classification of information assets
<!-- FW:REF-START ORIG:(ISA 1.3.2) -->
*Requirement reference:* {{#if FLAG_FW_TISAX}}VDA ISA 1.3.2{{/if}}{{#if FLAG_FW_ISO27001}}{{#if FLAG_FW_TISAX}} · {{/if}}ISO/IEC 27001 A.5.12, A.5.13{{/if}}
<!-- FW:REF-END -->
**Requirement**
<!-- FW:TISAX-REQ-START -->
{{#if FLAG_FW_TISAX}}
{{#if FLAG_FW_ISO27001}}*Requirements per VDA ISA 2027:*{{/if}}
<!-- REQ 1.3.2-M1 -->
- **[MUST]** A consistent scheme for classifying information assets with regard to the protection goal of confidentiality is in place.
<!-- REQ 1.3.2-M2 -->
- **[MUST]** The identified information assets are assessed according to the defined criteria and assigned to the classification scheme.
<!-- REQ 1.3.2-M3 -->
- **[MUST]** Requirements for handling supporting assets (e.g. labelling, use, transport, storage, return, deletion/destruction) depending on the classification are in place and implemented.
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.3.2-S1 -->
- **[SHOULD]** The protection goals of integrity and availability are taken into account.
{{/if}}
{{/if}}
<!-- FW:TISAX-REQ-END -->
<!-- FW:ISO-REQ-START -->
{{#if FLAG_FW_ISO27001}}
{{#if FLAG_FW_TISAX}}*Requirements per ISO/IEC 27001:*{{/if}}
<!-- REQ A.5.12-1 -->
- **[ISO A.5.12]** Information is classified according to its protection needs (confidentiality, integrity, availability).
<!-- REQ A.5.13-1 -->
- **[ISO A.5.13]** Procedures for labelling information in accordance with the classification scheme are developed and implemented.
{{/if}}
<!-- FW:ISO-REQ-END -->
**Implementation at {{ORG_NAME}}**
<!-- IMPL 1.3.2 -->
A consistent four-tier classification scheme (Public / Internal / Confidential / Strictly confidential) applies for confidentiality; the classification is carried out according to defined criteria by the asset owner in the ISMS tool and also takes integrity and availability into account. Handling requirements per protection class (labelling, storage, transport, transmission BL-CRY-01/04, deletion BL-DEL-01) are defined, implemented and made known (see {{LINK:VA-08}}).
### 3.3 Use of approved external IT services/hardware
<!-- FW:REF-START ORIG:(ISA 1.3.3) -->
*Requirement reference:* {{#if FLAG_FW_TISAX}}VDA ISA 1.3.3{{/if}}{{#if FLAG_FW_ISO27001}}{{#if FLAG_FW_TISAX}} · {{/if}}ISO/IEC 27001 A.5.10{{/if}}
<!-- FW:REF-END -->
**Requirement**
<!-- FW:TISAX-REQ-START -->
{{#if FLAG_FW_TISAX}}
{{#if FLAG_FW_ISO27001}}*Requirements per VDA ISA 2027:*{{/if}}
<!-- REQ 1.3.3-M1 -->
- **[MUST]** External IT services are not used without an explicit assessment and implementation of the information security requirements; the relevant aspects are taken into account.
<!-- REQ 1.3.3-M2 -->
- **[MUST]** The external IT services are aligned with the protection need of the information assets processed.
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.3.3-S1 -->
- **[SHOULD]** Requirements for procurement, commissioning and approval in connection with the use of external IT services are determined and met.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.3.3-S2 -->
- **[SHOULD]** A procedure for approval taking the protection need into account is established.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.3.3-S3 -->
- **[SHOULD]** External IT services and their approval are documented.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.3.3-S4 -->
- **[SHOULD]** It is regularly verified that only approved external IT services are used.
{{/if}}
{{/if}}
<!-- FW:TISAX-REQ-END -->
<!-- FW:ISO-REQ-START -->
{{#if FLAG_FW_ISO27001}}
{{#if FLAG_FW_TISAX}}*Requirements per ISO/IEC 27001:*{{/if}}
<!-- REQ A.5.10-1 -->
- **[ISO A.5.10]** Rules for the acceptable use and handling of information and assets are defined, documented and implemented.
{{/if}}
<!-- FW:ISO-REQ-END -->
**Implementation at {{ORG_NAME}}**
<!-- IMPL 1.3.3 -->
External IT services/components are assessed before use, aligned with the protection need and approved via a defined procedure; the approvals are maintained in the register of external IT/cloud/AI services ({{LINK:REG-EXT-SERVICES}}) (supplier {{LINK:VA-10}}, asset {{LINK:VA-08}}) and regularly checked for exclusive use of approved services.
### 3.4 Approval of software
<!-- FW:REF-START ORIG:(ISA 1.3.4) -->
*Requirement reference:* {{#if FLAG_FW_TISAX}}VDA ISA 1.3.4{{/if}}{{#if FLAG_FW_ISO27001}}{{#if FLAG_FW_TISAX}} · {{/if}}ISO/IEC 27001 A.8.19{{/if}}
<!-- FW:REF-END -->
**Requirement**
<!-- FW:TISAX-REQ-START -->
{{#if FLAG_FW_TISAX}}
{{#if FLAG_FW_ISO27001}}*Requirements per VDA ISA 2027:*{{/if}}
<!-- REQ 1.3.4-M1 -->
- **[MUST]** Software is approved before installation or use; the relevant aspects are taken into account.
<!-- REQ 1.3.4-M2 -->
- **[MUST]** The software approval also applies to special software such as maintenance tools.
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.3.4-S1 -->
- **[SHOULD]** The types of software to be managed (firmware, operating systems, applications, libraries, device drivers) are determined.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.3.4-S2 -->
- **[SHOULD]** Repositories of the managed software exist.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.3.4-S3 -->
- **[SHOULD]** The software repositories are protected against unauthorised manipulation.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.3.4-S4 -->
- **[SHOULD]** The approval of software is reviewed regularly.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.3.4-S5 -->
- **[SHOULD]** Software versions and patch levels are known.
{{/if}}
{{#if FLAG_VERY_HIGH_PROTECTION}}
<!-- REQ 1.3.4-V1 -->
- **[VERY HIGH]** Additional requirements for software use (e.g. the need to control/monitor use) are determined where present. (C, I, A)
{{/if}}
{{/if}}
<!-- FW:TISAX-REQ-END -->
<!-- FW:ISO-REQ-START -->
{{#if FLAG_FW_ISO27001}}
{{#if FLAG_FW_TISAX}}*Requirements per ISO/IEC 27001:*{{/if}}
<!-- REQ A.8.19-1 -->
- **[ISO A.8.19]** Procedures and measures for securely managing software installation on operational systems are implemented.
{{/if}}
<!-- FW:ISO-REQ-END -->
**Implementation at {{ORG_NAME}}**
<!-- IMPL 1.3.4 -->
Software (incl. special/maintenance software) is approved before use; approved software is maintained in the software whitelist register ({{LINK:REG-SW-WHITELIST}}) with version/patch level, source/supplier ({{LINK:VA-10}}) and approval status and is linked to the asset inventory ({{LINK:VA-08}}); procurement/approval runs via {{TOOL_TICKET}}. Managed software types are determined, repositories protected against manipulation, and approvals are reviewed regularly.
{{#if FLAG_ELEVATED_PROTECTION}}
<!-- IMPL 1.3.4-elev -->
{{#if FLAG_VERY_HIGH_PROTECTION}}Where the protection need is very high, additional control/monitoring requirements for software use are determined and implemented.{{/if}}
{{/if}}
## 4. Binding nature
This policy is binding for all affected roles within the scope. Compliance is monitored by {{ROLE_IT_LEAD}}.
## 5. Roles and responsibilities
| Role | Responsibility in this policy |
|-------|-------------------------------------|
| {{ROLE_IT_LEAD}} | Asset inventory, approval of hardware/software |
| {{ROLE_ISB}} | Classification scheme |
| Asset owner | Maintenance of individual assets |
## 6. Review and update
This policy is reviewed at least {{REVIEW_CYCLE}} and on an ad-hoc basis by {{ROLE_IT_LEAD}} and approved by {{ROLE_MANAGEMENT}}.
## 7. Evidence
The evidence is not maintained in this document but centrally in the evidence register ({{LINK:NACHWEISREGISTER}}) and in the associated entries of the ISMS tool ({{TOOL_NAME}}).
## 8. Related documents
- Associated procedures: {{LINK:VA-08}}
- Technical security baseline: {{LINK:BASELINE}}
- ISA mapping matrix: {{LINK:ISA_MAPPING}}
- Evidence register: {{LINK:NACHWEISREGISTER}}
- Further: {{LINK:R01}}, {{LINK:R08}}, {{LINK:R11}}
<!-- Anforderungen 1:1 aus VDA ISA 2027; Mapping (REQ/IMPL) in mapping.json ueber Hidden-Anker. Im Lesemodus nicht sichtbar. -->
<!-- FW:ISO-SECTION-START -->
{{#if FLAG_FW_ISO27001}}
### 3.5 Data masking and pseudonymisation
*Requirement reference:* ISO/IEC 27001 A.8.11
**Requirement**
<!-- REQ A.8.11-1 -->
- **[ISO A.8.11]** Data masking is applied in accordance with the access control and privacy requirements.
**Implementation at {{ORG_NAME}}**
<!-- IMPL ISO-MASKIERUNG -->
Where the full information content is not required for the purpose, data is masked, pseudonymised or anonymised. This applies in particular to test, training and development environments ({{LINK:R11}}), to analyses and to displays with a restricted need for access. Extent and method follow the classification and the data protection requirements ({{LINK:R14}}); whether the link to a person may be restored, and how that is safeguarded, is governed explicitly.
{{/if}}
<!-- FW:ISO-SECTION-END -->
@@ -1,289 +0,0 @@
# Policy Risk Management and Audit Policy
| Document information | Value |
|-----------------------|------|
| Document type | Policy |
| Scope | {{ISMS_SCOPE}} |
| Organisation | {{ORG_NAME}} |
| Responsible | {{ROLE_ISB}} |
| Approved by | {{ROLE_MANAGEMENT}} |
| Version | {{DOC_VERSION}} |
| Date | {{DOC_DATE}} |
| Status | {{DOC_STATUS}} |
## 1. Purpose
This policy governs the identification, assessment and treatment of information security risks as well as internal and independent reviews. It elaborates the information security policy ({{LINK:L00}}) and serves to meet the requirements of VDA ISA 2027.
## 2. Scope
This policy applies within the defined ISMS scope ({{ISMS_SCOPE_DESCRIPTION}}).
## 3. Requirements and implementation
> Structure per section: **Requirement** (1:1 from VDA ISA; [MUST]/[SHOULD] and — where the protection need applies — [HIGH]/[VERY HIGH]) and **Implementation at {{ORG_NAME}}** (consolidated, to be adjusted where necessary).
### 3.1 Risk management
<!-- FW:REF-START ORIG:(ISA 1.4.1) -->
*Requirement reference:* {{#if FLAG_FW_TISAX}}VDA ISA 1.4.1{{/if}}{{#if FLAG_FW_ISO27001}}{{#if FLAG_FW_TISAX}} · {{/if}}ISO/IEC 27001 6.1.1, 6.1.2, 8.2, 8.3{{/if}}
<!-- FW:REF-END -->
**Requirement**
<!-- FW:TISAX-REQ-START -->
{{#if FLAG_FW_TISAX}}
{{#if FLAG_FW_ISO27001}}*Requirements per VDA ISA 2027:*{{/if}}
<!-- REQ 1.4.1-M1 -->
- **[MUST]** Risk assessments are carried out regularly and on an ad-hoc basis.
<!-- REQ 1.4.1-M2 -->
- **[MUST]** Information security risks are assessed appropriately (e.g. likelihood of occurrence and potential extent of damage).
<!-- REQ 1.4.1-M3 -->
- **[MUST]** Information security risks are documented.
<!-- REQ 1.4.1-M4 -->
- **[MUST]** A responsible person (risk owner) is assigned to each information security risk and is responsible for its assessment and treatment.
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.4.1-S1 -->
- **[SHOULD]** A procedure for the identification, assessment and treatment of security risks is in place.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.4.1-S2 -->
- **[SHOULD]** Criteria for the assessment and treatment of security risks exist.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.4.1-S3 -->
- **[SHOULD]** Risk treatment measures and their responsible persons are defined and documented; a measures plan or implementation overview is tracked.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.4.1-S4 -->
- **[SHOULD]** Upon changes in the environment (e.g. organisational structure, location, regulations), a reassessment is carried out promptly.
{{/if}}
{{/if}}
<!-- FW:TISAX-REQ-END -->
<!-- FW:ISO-REQ-START -->
{{#if FLAG_FW_ISO27001}}
{{#if FLAG_FW_TISAX}}*Requirements per ISO/IEC 27001:*{{/if}}
<!-- REQ 6.1.1-1 -->
- **[ISO 6.1.1]** When planning the ISMS, risks and opportunities that need to be addressed are determined.
<!-- REQ 6.1.2-1 -->
- **[ISO 6.1.2]** A risk assessment process with defined criteria is established and applied so that it is repeatable and produces comparable results.
<!-- REQ 8.2-1 -->
- **[ISO 8.2]** Risk assessments are performed at planned intervals and upon significant change, and are documented.
<!-- REQ 8.3-1 -->
- **[ISO 8.3]** The risk treatment plan is implemented and the results are documented.
{{/if}}
<!-- FW:ISO-REQ-END -->
**Implementation at {{ORG_NAME}}**
<!-- IMPL 1.4.1 -->
The documented risk management procedure (see {{LINK:VA-09}}) with assessment and acceptance criteria is implemented in the ISMS tool ({{TOOL_NAME}}): risks are identified regularly ({{REVIEW_CYCLE}}) and on an ad-hoc basis, assessed (likelihood × impact) and documented; for each risk, a risk owner, treatment option and measures with deadlines are recorded and tracked. Residual risks are accepted by {{ROLE_MANAGEMENT}} in a documented manner.
### 3.2 Verification of compliance in IS operations
<!-- FW:REF-START ORIG:(ISA 1.5.1) -->
*Requirement reference:* {{#if FLAG_FW_TISAX}}VDA ISA 1.5.1{{/if}}{{#if FLAG_FW_ISO27001}}{{#if FLAG_FW_TISAX}} · {{/if}}ISO/IEC 27001 A.5.36{{/if}}
<!-- FW:REF-END -->
**Requirement**
<!-- FW:TISAX-REQ-START -->
{{#if FLAG_FW_TISAX}}
{{#if FLAG_FW_ISO27001}}*Requirements per VDA ISA 2027:*{{/if}}
<!-- REQ 1.5.1-M1 -->
- **[MUST]** Compliance with the policies is reviewed organisation-wide.
<!-- REQ 1.5.1-M2 -->
- **[MUST]** Information security policies and procedures are reviewed regularly.
<!-- REQ 1.5.1-M3 -->
- **[MUST]** Measures to correct possible deviations are initiated and tracked.
<!-- REQ 1.5.1-M4 -->
- **[MUST]** Compliance with information security requirements (e.g. technical specifications) is reviewed regularly.
<!-- REQ 1.5.1-M5 -->
- **[MUST]** The results of the reviews carried out are recorded and retained.
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.5.1-S1 -->
- **[SHOULD]** A plan for the content and framework conditions (schedule, scope, controls) of the reviews to be carried out is in place.
{{/if}}
{{/if}}
<!-- FW:TISAX-REQ-END -->
<!-- FW:ISO-REQ-START -->
{{#if FLAG_FW_ISO27001}}
{{#if FLAG_FW_TISAX}}*Requirements per ISO/IEC 27001:*{{/if}}
<!-- REQ A.5.36-1 -->
- **[ISO A.5.36]** Compliance with the information security policy, topic-specific policies, rules and standards is reviewed regularly.
{{/if}}
<!-- FW:ISO-REQ-END -->
**Implementation at {{ORG_NAME}}**
<!-- IMPL 1.5.1 -->
Compliance with policies, procedures and technical requirements is reviewed organisation-wide and regularly according to the audit programme ({{LINK:REG-AUDIT-PLAN}}) and the audit/compliance review procedure ({{LINK:VA-15}}) through internal audits and controls (cycle BL-GOV-01); results are recorded and retained, deviations are tracked as measures in {{TOOL_NAME}}; responsible: {{ROLE_ISB}}.
### 3.3 Independent review of the ISMS
<!-- FW:REF-START ORIG:(ISA 1.5.2) -->
*Requirement reference:* {{#if FLAG_FW_TISAX}}VDA ISA 1.5.2{{/if}}{{#if FLAG_FW_ISO27001}}{{#if FLAG_FW_TISAX}} · {{/if}}ISO/IEC 27001 9.2, A.5.35{{/if}}
<!-- FW:REF-END -->
**Requirement**
<!-- FW:TISAX-REQ-START -->
{{#if FLAG_FW_TISAX}}
{{#if FLAG_FW_ISO27001}}*Requirements per VDA ISA 2027:*{{/if}}
<!-- REQ 1.5.2-M1 -->
- **[MUST]** Information security reviews are carried out by an independent and competent body regularly and after fundamental changes.
<!-- REQ 1.5.2-M2 -->
- **[MUST]** Measures to correct possible deviations are initiated and tracked.
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.5.2-S1 -->
- **[SHOULD]** The results of the reviews carried out are documented and reported to the organisation's management.
{{/if}}
{{/if}}
<!-- FW:TISAX-REQ-END -->
<!-- FW:ISO-REQ-START -->
{{#if FLAG_FW_ISO27001}}
{{#if FLAG_FW_TISAX}}*Requirements per ISO/IEC 27001:*{{/if}}
<!-- REQ 9.2-1 -->
- **[ISO 9.2]** Internal audits are conducted at planned intervals to verify conformity and effective implementation of the ISMS.
<!-- REQ A.5.35-1 -->
- **[ISO A.5.35]** The organisation's approach to managing information security is reviewed independently at planned intervals.
{{/if}}
<!-- FW:ISO-REQ-END -->
**Implementation at {{ORG_NAME}}**
<!-- IMPL 1.5.2 -->
The ISMS is reviewed regularly and after fundamental changes by an independent, competent body (internal audit or external auditing, e.g. TISAX); results are documented, reported to {{ROLE_MANAGEMENT}} and deviations tracked as measures.
## 4. Binding nature
This policy is binding for all affected roles within the scope. Compliance is monitored by {{ROLE_ISB}}.
## 5. Roles and responsibilities
| Role | Responsibility in this policy |
|-------|-------------------------------------|
| {{ROLE_ISB}} | Risk management, audits |
| {{ROLE_MANAGEMENT}} | Risk acceptance |
## 6. Review and update
This policy is reviewed at least {{REVIEW_CYCLE}} and on an ad-hoc basis by {{ROLE_ISB}} and approved by {{ROLE_MANAGEMENT}}.
## 7. Evidence
The evidence is not maintained in this document but centrally in the evidence register ({{LINK:NACHWEISREGISTER}}) and in the associated entries of the ISMS tool ({{TOOL_NAME}}).
## 8. Related documents
- Associated procedures: {{LINK:VA-09}}
- Technical security baseline: {{LINK:BASELINE}}
- ISA mapping matrix: {{LINK:ISA_MAPPING}}
- Evidence register: {{LINK:NACHWEISREGISTER}}
- Further: {{LINK:R01}}, {{LINK:R04}}
<!-- Anforderungen 1:1 aus VDA ISA 2027; Mapping (REQ/IMPL) in mapping.json ueber Hidden-Anker. Im Lesemodus nicht sichtbar. -->
<!-- FW:ISO-SECTION-START -->
{{#if FLAG_FW_ISO27001}}
### 3.4 Statement of Applicability (SoA)
*Requirement reference:* ISO/IEC 27001 6.1.3
**Requirement**
<!-- REQ 6.1.3-1 -->
- **[ISO 6.1.3]** A risk treatment process is defined; necessary controls are determined and compared against Annex A in a Statement of Applicability.
**Implementation at {{ORG_NAME}}**
<!-- IMPL ISO-SOA -->
The risk treatment determines which controls are necessary; the result is compared against Annex A to identify controls that may have been overlooked. The Statement of Applicability is maintained in {{TOOL_NAME}} and states for each control: applicability, justification for inclusion, origin (risk ID, legal or contractual requirement), implementation status, responsible role, reference to policy and procedure as well as evidence; where a control is excluded, the justification is documented. The risk treatment plan and the acceptance of residual risks are approved by the respective risk owners; the SoA is approved by {{ROLE_MANAGEMENT}} and updated with every risk assessment ({{RISK_REVIEW_CYCLE}}).
{{/if}}
<!-- FW:ISO-SECTION-END -->
<!-- FW:ISO-SECTION-START -->
{{#if FLAG_FW_ISO27001}}
### 3.5 Operational planning and control
*Requirement reference:* ISO/IEC 27001 8.1
**Requirement**
<!-- REQ 8.1-1 -->
- **[ISO 8.1]** The processes needed to meet the requirements are planned, implemented and controlled; planned changes are controlled.
**Implementation at {{ORG_NAME}}**
<!-- IMPL ISO-MS-BETRIEB -->
The processes required to meet the information security requirements are laid down in the procedures and controlled in {{TOOL_NAME}}; for each process the trigger, responsible role, deadlines and evidence are defined. Planned changes are controlled and their consequences assessed; unintended changes are reviewed and corrected where necessary. Outsourced processes are determined and monitored through supplier management ({{LINK:R13}}). Evidence of execution as planned is kept in the evidence register ({{LINK:NACHWEISREGISTER}}).
{{/if}}
<!-- FW:ISO-SECTION-END -->
<!-- FW:ISO-SECTION-START -->
{{#if FLAG_FW_ISO27001}}
### 3.6 Monitoring, measurement, analysis and evaluation
*Requirement reference:* ISO/IEC 27001 9.1
**Requirement**
<!-- REQ 9.1-1 -->
- **[ISO 9.1]** The information security performance and the effectiveness of the ISMS are monitored, measured, analysed and evaluated.
**Implementation at {{ORG_NAME}}**
<!-- IMPL ISO-MS-MESSUNG -->
For the evaluation of information security performance and the effectiveness of the ISMS it is defined what is measured (metrics sheet in {{TOOL_NAME}}), by which method and data source, at which interval, who measures, when the results are analysed and who analyses them. The metrics cover at least incident handling, vulnerability and patch remediation, recertification of access rights, restore tests, awareness participation and open actions; each metric has a target value and a responsible role. Results and trends feed into the management review {{MGMT_REVIEW_CYCLE}}; a deviation from the target value triggers an action.
{{/if}}
<!-- FW:ISO-SECTION-END -->
<!-- FW:ISO-SECTION-START -->
{{#if FLAG_FW_ISO27001}}
### 3.7 Management review
*Requirement reference:* ISO/IEC 27001 9.3
**Requirement**
<!-- REQ 9.3-1 -->
- **[ISO 9.3]** Top management reviews the ISMS at planned intervals.
**Implementation at {{ORG_NAME}}**
<!-- IMPL ISO-MS-MGMTREVIEW -->
{{ROLE_MANAGEMENT}} reviews the ISMS at least {{MGMT_REVIEW_CYCLE}} against a fixed agenda (BL-GOV-02). Inputs are at least: status of actions from previous reviews; changes in relevant internal and external issues and in the requirements of interested parties; feedback on information security performance (nonconformities and corrective actions, monitoring and measurement results, audit results, achievement of the information security objectives); feedback from interested parties; results of the risk assessment and status of the risk treatment plan; opportunities for improvement. Outputs are decisions on opportunities for improvement and on any need to change the ISMS, each with a responsible role and a due date. The minutes are retained in {{TOOL_NAME}}.
{{/if}}
<!-- FW:ISO-SECTION-END -->
<!-- FW:ISO-SECTION-START -->
{{#if FLAG_FW_ISO27001}}
### 3.8 Nonconformity, corrective action and continual improvement
*Requirement reference:* ISO/IEC 27001 10.1, 10.2
**Requirement**
<!-- REQ 10.1-1 -->
- **[ISO 10.1]** The suitability, adequacy and effectiveness of the ISMS are continually improved.
<!-- REQ 10.2-1 -->
- **[ISO 10.2]** In the event of nonconformity, corrections are made and corrective actions are taken to eliminate the causes.
**Implementation at {{ORG_NAME}}**
<!-- IMPL ISO-MS-CAPA -->
Nonconformities arising from audits, controls, incidents, deviations of metrics and reports are recorded in {{TOOL_NAME}}. For each case the immediate correction and the handling of the consequences are decided, the cause is analysed and it is evaluated whether similar nonconformities exist or could occur elsewhere. Necessary corrective actions are implemented with a responsible role and a due date; their effectiveness is evaluated after the defined effectiveness interval and, where necessary, risks, controls and documents are adjusted. The nature of the nonconformity, the actions taken and the result of the effectiveness review are retained. The suitability, adequacy and effectiveness of the ISMS are continually improved; evidence is provided through metrics and the management review.
{{/if}}
<!-- FW:ISO-SECTION-END -->
@@ -1,383 +0,0 @@
# Policy Incident, Emergency and Continuity Policy
| Document information | Value |
|-----------------------|------|
| Document type | Policy |
| Scope | {{ISMS_SCOPE}} |
| Organisation | {{ORG_NAME}} |
| Responsible | {{ROLE_ISB}} |
| Approved by | {{ROLE_MANAGEMENT}} |
| Version | {{DOC_VERSION}} |
| Date | {{DOC_DATE}} |
| Status | {{DOC_STATUS}} |
## 1. Purpose
This policy governs the reporting and handling of security events, crisis management as well as emergency and continuity planning. It elaborates the information security policy ({{LINK:L00}}) and serves to meet the requirements of VDA ISA 2027.
## 2. Scope
This policy applies within the defined ISMS scope ({{ISMS_SCOPE_DESCRIPTION}}).
## 3. Requirements and implementation
> Structure per section: **Requirement** (1:1 from VDA ISA; [MUST]/[SHOULD] and — where the protection need applies — [HIGH]/[VERY HIGH]) and **Implementation at {{ORG_NAME}}** (consolidated, to be adjusted where necessary).
### 3.1 Reporting of security events
<!-- FW:REF-START ORIG:(ISA 1.6.1) -->
*Requirement reference:* {{#if FLAG_FW_TISAX}}VDA ISA 1.6.1{{/if}}{{#if FLAG_FW_ISO27001}}{{#if FLAG_FW_TISAX}} · {{/if}}ISO/IEC 27001 A.5.24, A.6.8{{/if}}
<!-- FW:REF-END -->
**Requirement**
<!-- FW:TISAX-REQ-START -->
{{#if FLAG_FW_TISAX}}
{{#if FLAG_FW_ISO27001}}*Requirements per VDA ISA 2027:*{{/if}}
<!-- REQ 1.6.1-M1 -->
- **[MUST]** A definition of a reportable security event or observation exists and is known to employees and relevant stakeholders.
<!-- REQ 1.6.1-M2 -->
- **[MUST]** Appropriate, risk-oriented mechanisms for reporting security events are defined, implemented and known to all relevant reporters.
<!-- REQ 1.6.1-M3 -->
- **[MUST]** Appropriate channels for communicating with reporters exist.
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.6.1-S1 -->
- **[SHOULD]** A common point of contact for event reporting exists.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.6.1-S2 -->
- **[SHOULD]** Different reporting channels depending on the perceived severity (real-time for serious events/emergencies as well as asynchronous mechanisms such as tickets or email) are available.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.6.1-S3 -->
- **[SHOULD]** Employees are obliged and trained to report relevant events.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.6.1-S4 -->
- **[SHOULD]** Security events can also be reported by external parties; the relevant aspects are taken into account.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.6.1-S5 -->
- **[SHOULD]** The mechanism and the information on how incidents are reported are accessible to all relevant reporters.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.6.1-S6 -->
- **[SHOULD]** A feedback procedure to the reporters is established.
{{/if}}
{{#if FLAG_VERY_HIGH_PROTECTION}}
<!-- REQ 1.6.1-V1 -->
- **[VERY HIGH]** Tests and exercises of event and observation reporting are carried out regularly. (C, I, A)
{{/if}}
{{/if}}
<!-- FW:TISAX-REQ-END -->
<!-- FW:ISO-REQ-START -->
{{#if FLAG_FW_ISO27001}}
{{#if FLAG_FW_TISAX}}*Requirements per ISO/IEC 27001:*{{/if}}
<!-- REQ A.5.24-1 -->
- **[ISO A.5.24]** The management of information security incidents is planned and prepared (roles, processes, responsibilities).
<!-- REQ A.6.8-1 -->
- **[ISO A.6.8]** A mechanism for the timely reporting of observed or suspected information security events is provided.
{{/if}}
<!-- FW:ISO-REQ-END -->
**Implementation at {{ORG_NAME}}**
<!-- IMPL 1.6.1 -->
A known definition of reportable events and a low-threshold reporting path (report button/form in {{TOOL_TICKET}} or the ISMS tool, email to {{ROLE_ISB}}, real-time channel for serious cases) are available to all employees and external parties; the reporting path is known via onboarding/awareness (BL-HR-01), and a feedback procedure is established (see {{LINK:VA-01}}).
{{#if FLAG_ELEVATED_PROTECTION}}
<!-- IMPL 1.6.1-elev -->
{{#if FLAG_VERY_HIGH_PROTECTION}}Where the protection need is very high, tests and exercises of event reporting are carried out regularly.{{/if}}
{{/if}}
### 3.2 Handling of security events
<!-- FW:REF-START ORIG:(ISA 1.6.2) -->
*Requirement reference:* {{#if FLAG_FW_TISAX}}VDA ISA 1.6.2{{/if}}{{#if FLAG_FW_ISO27001}}{{#if FLAG_FW_TISAX}} · {{/if}}ISO/IEC 27001 A.5.25, A.5.26, A.5.27, A.5.28{{/if}}
<!-- FW:REF-END -->
**Requirement**
<!-- FW:TISAX-REQ-START -->
{{#if FLAG_FW_TISAX}}
{{#if FLAG_FW_ISO27001}}*Requirements per VDA ISA 2027:*{{/if}}
<!-- REQ 1.6.2-M1 -->
- **[MUST]** Reported events are processed without undue delay.
<!-- REQ 1.6.2-M2 -->
- **[MUST]** An appropriate response to reported security events is ensured.
<!-- REQ 1.6.2-M3 -->
- **[MUST]** Lessons learned feed into continual improvement.
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.6.2-S1 -->
- **[SHOULD]** During processing, reported events are categorised (e.g. personnel, physical, cyber), qualified (e.g. not security-relevant, observation, improvement suggestion, vulnerability, incident) and prioritised (e.g. low, medium, high, critical).
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.6.2-S2 -->
- **[SHOULD]** Responsibilities for handling events per category are defined and assigned.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.6.2-S3 -->
- **[SHOULD]** A strategy for reporting potentially criminally relevant aspects to the competent authorities, where necessary, exists. (C, I, A)
{{/if}}
{{#if FLAG_HIGH_PROTECTION}}
<!-- REQ 1.6.2-H1 -->
- **[HIGH]** Maximum response times per class, category and severity are defined. (C, I, A)
{{/if}}
{{#if FLAG_HIGH_PROTECTION}}
<!-- REQ 1.6.2-H2 -->
- **[HIGH]** Events not processed in line with their priority are escalated; the relevant aspects are taken into account. (C, I, A)
{{/if}}
{{#if FLAG_HIGH_PROTECTION}}
<!-- REQ 1.6.2-H3 -->
- **[HIGH]** Legal, regulatory and contractual reporting obligations and the associated contact information are known. (C, I, A)
{{/if}}
{{#if FLAG_HIGH_PROTECTION}}
<!-- REQ 1.6.2-H4 -->
- **[HIGH]** A communication strategy for security-relevant events exists; the relevant aspects are taken into account. (C, I, A)
{{/if}}
{{#if FLAG_HIGH_PROTECTION}}
<!-- REQ 1.6.2-H5 -->
- **[HIGH]** Procedures for responding to security incidents at suppliers are established; the relevant aspects are taken into account. (C, I, A)
{{/if}}
{{#if FLAG_VERY_HIGH_PROTECTION}}
<!-- REQ 1.6.2-V1 -->
- **[VERY HIGH]** The handling of events of different categories and priorities is tested regularly; the relevant aspects are taken into account. (A)
{{/if}}
{{/if}}
<!-- FW:TISAX-REQ-END -->
<!-- FW:ISO-REQ-START -->
{{#if FLAG_FW_ISO27001}}
{{#if FLAG_FW_TISAX}}*Requirements per ISO/IEC 27001:*{{/if}}
<!-- REQ A.5.25-1 -->
- **[ISO A.5.25]** Information security events are assessed and a decision is taken whether they are to be categorised as incidents.
<!-- REQ A.5.26-1 -->
- **[ISO A.5.26]** Information security incidents are responded to in accordance with documented procedures.
<!-- REQ A.5.27-1 -->
- **[ISO A.5.27]** Knowledge gained from information security incidents is used to strengthen the controls.
<!-- REQ A.5.28-1 -->
- **[ISO A.5.28]** Procedures for the identification, collection, acquisition and preservation of evidence relating to incidents are established and implemented.
{{/if}}
<!-- FW:ISO-REQ-END -->
**Implementation at {{ORG_NAME}}**
<!-- IMPL 1.6.2 -->
Events are categorised, qualified, prioritised, handled and documented without delay following a defined incident procedure (see {{LINK:VA-01}}) in {{TOOL_TICKET}}; responsibilities and escalation paths are assigned ({{ROLE_ISB}} coordinates, {{ROLE_IT_LEAD}} implements). Lessons learned feed into improvement; a strategy for reporting to authorities/law enforcement exists.
{{#if FLAG_ELEVATED_PROTECTION}}
<!-- IMPL 1.6.2-elev -->
Where the protection need is high, maximum response times per severity are defined, escalations for events not processed in line with their priority are regulated, reporting obligations and contacts are known, and a communication strategy as well as a procedure for supplier incidents are established. {{#if FLAG_VERY_HIGH_PROTECTION}}Where the protection need is very high, event handling is tested regularly.{{/if}}
{{/if}}
### 3.3 Crisis management
<!-- FW:REF-START ORIG:(ISA 1.6.3) -->
*Requirement reference:* {{#if FLAG_FW_TISAX}}VDA ISA 1.6.3{{/if}}{{#if FLAG_FW_ISO27001}}{{#if FLAG_FW_TISAX}} · {{/if}}ISO/IEC 27001 A.5.29{{/if}}
<!-- FW:REF-END -->
**Requirement**
<!-- FW:TISAX-REQ-START -->
{{#if FLAG_FW_TISAX}}
{{#if FLAG_FW_ISO27001}}*Requirements per VDA ISA 2027:*{{/if}}
<!-- REQ 1.6.3-M1 -->
- **[MUST]** An appropriate plan for responding to and managing crisis situations exists and the necessary resources are available.
<!-- REQ 1.6.3-M2 -->
- **[MUST]** Responsibilities and authorities for crisis management are defined, documented and assigned.
<!-- REQ 1.6.3-M3 -->
- **[MUST]** The responsible employees are defined and qualified for their task.
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.6.3-S1 -->
- **[SHOULD]** Methods for detecting crisis situations are established; general indicators and specific foreseeable crises are identified.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.6.3-S2 -->
- **[SHOULD]** A procedure for triggering and/or escalating crisis management is in place.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.6.3-S3 -->
- **[SHOULD]** Strategic objectives and their priority in crisis situations are defined and known to relevant personnel.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.6.3-S4 -->
- **[SHOULD]** A crisis team is defined and approved.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.6.3-S5 -->
- **[SHOULD]** Crisis policies and procedures are defined and approved.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 1.6.3-S6 -->
- **[SHOULD]** The crisis planning is reviewed and updated regularly.
{{/if}}
{{#if FLAG_HIGH_PROTECTION}}
<!-- REQ 1.6.3-H1 -->
- **[HIGH]** Relevant different potential crisis scenarios are identified.
{{/if}}
{{#if FLAG_HIGH_PROTECTION}}
<!-- REQ 1.6.3-H2 -->
- **[HIGH]** The resources and information necessary for crisis management (e.g. communication infrastructure, availability of contact and risk information) are identified; appropriate measures to ensure availability or fallback planning are in place. (A)
{{/if}}
{{#if FLAG_HIGH_PROTECTION}}
<!-- REQ 1.6.3-H3 -->
- **[HIGH]** A communication strategy for crisis situations exists. (A)
{{/if}}
{{#if FLAG_HIGH_PROTECTION}}
<!-- REQ 1.6.3-H4 -->
- **[HIGH]** The efficiency, feasibility and appropriateness of the crisis planning are assessed regularly. (A)
{{/if}}
{{#if FLAG_HIGH_PROTECTION}}
<!-- REQ 1.6.3-H5 -->
- **[HIGH]** Sample-based tests of the crisis planning are carried out (e.g. simulation, tabletop exercises with key personnel). (A)
{{/if}}
{{#if FLAG_VERY_HIGH_PROTECTION}}
<!-- REQ 1.6.3-V1 -->
- **[VERY HIGH]** Crisis exercises and simulations involving all relevant persons, including decision-makers, are carried out regularly. (A)
{{/if}}
{{/if}}
<!-- FW:TISAX-REQ-END -->
<!-- FW:ISO-REQ-START -->
{{#if FLAG_FW_ISO27001}}
{{#if FLAG_FW_TISAX}}*Requirements per ISO/IEC 27001:*{{/if}}
<!-- REQ A.5.29-1 -->
- **[ISO A.5.29]** The maintenance of information security during disruption is planned and implemented.
{{/if}}
<!-- FW:ISO-REQ-END -->
**Implementation at {{ORG_NAME}}**
<!-- IMPL 1.6.3 -->
A crisis management with a plan, a defined and approved crisis team, roles, triggering/escalation, communication and decision paths as well as strategic objectives is established (triggering/recovery see {{LINK:VA-02}}); the necessary resources are available and the responsible persons are qualified. Detection methods are in place, the crisis planning is reviewed and updated regularly; the crisis team is convened by {{ROLE_MANAGEMENT}}.
{{#if FLAG_ELEVATED_PROTECTION}}
<!-- IMPL 1.6.3-elev -->
Where the protection need is high, relevant crisis scenarios are identified, the necessary resources/information and a communication strategy are ensured, and the planning is assessed regularly and tested on a sample basis (tabletop). {{#if FLAG_VERY_HIGH_PROTECTION}}Where the protection need is very high, crisis exercises involving all relevant persons including decision-makers are carried out regularly.{{/if}}
{{/if}}
### 3.4 Continuity planning for IT services
<!-- FW:REF-START ORIG:(ISA 5.2.8) -->
*Requirement reference:* {{#if FLAG_FW_TISAX}}VDA ISA 5.2.8{{/if}}{{#if FLAG_FW_ISO27001}}{{#if FLAG_FW_TISAX}} · {{/if}}ISO/IEC 27001 A.5.30, A.8.14{{/if}}
<!-- FW:REF-END -->
**Requirement**
<!-- FW:TISAX-REQ-START -->
{{#if FLAG_FW_TISAX}}
{{#if FLAG_FW_ISO27001}}*Requirements per VDA ISA 2027:*{{/if}}
<!-- REQ 5.2.8-M1 -->
- **[MUST]** Critical IT services are identified and the business impact is taken into account.
<!-- REQ 5.2.8-M2 -->
- **[MUST]** Requirements and responsibilities for the continuity and recovery of these IT services are known to relevant stakeholders and fulfilled.
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 5.2.8-S1 -->
- **[SHOULD]** Critical IT systems are identified; the relevant aspects are taken into account.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 5.2.8-S2 -->
- **[SHOULD]** A continuity plan exists and is reviewed and updated regularly.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 5.2.8-S3 -->
- **[SHOULD]** The continuity planning covers at least (D)DoS attacks, successful ransomware attacks and other sabotage, system failure scenarios as well as natural disasters affecting critical IT systems.
{{/if}}
{{#if FLAG_HIGH_PROTECTION}}
<!-- REQ 5.2.8-H1 -->
- **[HIGH]** The continuity planning contains predefined time frames (recovery time objective) for the resumption of operations. (A)
{{/if}}
{{#if FLAG_HIGH_PROTECTION}}
<!-- REQ 5.2.8-H2 -->
- **[HIGH]** Appropriate SLAs with external service providers in line with the continuity planning are in place. (A)
{{/if}}
{{#if FLAG_HIGH_PROTECTION}}
<!-- REQ 5.2.8-H3 -->
- **[HIGH]** The continuity plans include the coordination of contractually agreed communication with business partners. (A)
{{/if}}
{{#if FLAG_HIGH_PROTECTION}}
<!-- REQ 5.2.8-H4 -->
- **[HIGH]** The continuity planning is tested regularly, incl. full recovery to a known state and adherence to defined target times. (A)
{{/if}}
{{#if FLAG_HIGH_PROTECTION}}
<!-- REQ 5.2.8-H5 -->
- **[HIGH]** A backup and recovery strategy for critical IT services and information is defined and implemented. (C, I, A)
{{/if}}
{{#if FLAG_HIGH_PROTECTION}}
<!-- REQ 5.2.8-H6 -->
- **[HIGH]** Backups of critical IT services and information are sufficiently protected against unauthorised modification/deletion by malware. (I, A)
{{/if}}
{{#if FLAG_HIGH_PROTECTION}}
<!-- REQ 5.2.8-H7 -->
- **[HIGH]** Backups of critical IT services and information are sufficiently protected against unauthorised access by malware or operators. (C, I)
{{/if}}
{{#if FLAG_VERY_HIGH_PROTECTION}}
<!-- REQ 5.2.8-V1 -->
- **[VERY HIGH]** The continuity planning is coordinated with the continuity plans of relevant external service providers. (A)
{{/if}}
{{#if FLAG_VERY_HIGH_PROTECTION}}
<!-- REQ 5.2.8-V2 -->
- **[VERY HIGH]** The continuation of essential core and business functions with minimal or no loss of operational continuity is possible; the relevant aspects are taken into account.
{{/if}}
{{#if FLAG_VERY_HIGH_PROTECTION}}
<!-- REQ 5.2.8-V3 -->
- **[VERY HIGH]** The continuity planning is tested regularly. Test scenarios, results and lessons learned are recorded. (I, A)
{{/if}}
{{/if}}
<!-- FW:TISAX-REQ-END -->
<!-- FW:ISO-REQ-START -->
{{#if FLAG_FW_ISO27001}}
{{#if FLAG_FW_TISAX}}*Requirements per ISO/IEC 27001:*{{/if}}
<!-- REQ A.5.30-1 -->
- **[ISO A.5.30]** ICT readiness is planned, implemented and tested on the basis of the business continuity objectives and requirements.
<!-- REQ A.8.14-1 -->
- **[ISO A.8.14]** Information processing facilities are implemented with sufficient redundancy to meet the availability requirements.
{{/if}}
<!-- FW:ISO-REQ-END -->
**Implementation at {{ORG_NAME}}**
<!-- IMPL 5.2.8 -->
Critical IT services are recorded with their business impact in the register of critical IT services ({{LINK:REG-CRIT-SERVICES}}) (incl. BIA classification, RTO/RPO, recovery sequence); requirements and responsibilities for continuity/recovery are known and fulfilled. A continuity plan (incl. (D)DoS, ransomware, failure, natural disasters) exists, is reviewed regularly and implemented via the IT emergency procedure (see {{LINK:VA-02}}).
{{#if FLAG_ELEVATED_PROTECTION}}
<!-- IMPL 5.2.8-elev -->
Where the protection need is high, RTO/RPO, SLAs with service providers, partner communication, regular full tests as well as a protected backup/recovery strategy (immutable/isolated) are established. {{#if FLAG_VERY_HIGH_PROTECTION}}Where the protection need is very high, the planning is coordinated with external service providers, the continuation of essential functions is ensured, and tests incl. lessons learned are recorded.{{/if}}
{{/if}}
## 4. Binding nature
This policy is binding for all affected roles within the scope. Compliance is monitored by {{ROLE_ISB}}.
## 5. Roles and responsibilities
| Role | Responsibility in this policy |
|-------|-------------------------------------|
| {{ROLE_ISB}} | Incident coordination |
| {{ROLE_IT_LEAD}} | Emergency/recovery planning |
| {{ROLE_MANAGEMENT}} | Crisis team |
## 6. Review and update
This policy is reviewed at least {{REVIEW_CYCLE}} and on an ad-hoc basis by {{ROLE_ISB}} and approved by {{ROLE_MANAGEMENT}}.
## 7. Evidence
The evidence is not maintained in this document but centrally in the evidence register ({{LINK:NACHWEISREGISTER}}) and in the associated entries of the ISMS tool ({{TOOL_NAME}}).
## 8. Related documents
- Associated procedures: {{LINK:VA-01}}, {{LINK:VA-02}}
- Technical security baseline: {{LINK:BASELINE}}
- ISA mapping matrix: {{LINK:ISA_MAPPING}}
- Evidence register: {{LINK:NACHWEISREGISTER}}
- Further: {{LINK:R03}}, {{LINK:R10}}
<!-- Anforderungen 1:1 aus VDA ISA 2027; Mapping (REQ/IMPL) in mapping.json ueber Hidden-Anker. Im Lesemodus nicht sichtbar. -->
@@ -1,221 +0,0 @@
# Policy Personnel Security and Awareness
| Document information | Value |
|-----------------------|------|
| Document type | Policy |
| Scope | {{ISMS_SCOPE}} |
| Organisation | {{ORG_NAME}} |
| Responsible | {{ROLE_HR_LEAD}} |
| Approved by | {{ROLE_MANAGEMENT}} |
| Version | {{DOC_VERSION}} |
| Date | {{DOC_DATE}} |
| Status | {{DOC_STATUS}} |
## 1. Purpose
This policy governs the suitability, contractual commitment as well as training and awareness of personnel. It elaborates the information security policy ({{LINK:L00}}) and serves to meet the requirements of VDA ISA 2027.
## 2. Scope
This policy applies within the defined ISMS scope ({{ISMS_SCOPE_DESCRIPTION}}).
## 3. Requirements and implementation
> Structure per section: **Requirement** (1:1 from VDA ISA; [MUST]/[SHOULD] and — where the protection need applies — [HIGH]/[VERY HIGH]) and **Implementation at {{ORG_NAME}}** (consolidated, to be adjusted where necessary).
### 3.1 Qualification for sensitive activities
<!-- FW:REF-START ORIG:(ISA 2.1.1) -->
*Requirement reference:* {{#if FLAG_FW_TISAX}}VDA ISA 2.1.1{{/if}}{{#if FLAG_FW_ISO27001}}{{#if FLAG_FW_TISAX}} · {{/if}}ISO/IEC 27001 7.2, A.6.1{{/if}}
<!-- FW:REF-END -->
**Requirement**
<!-- FW:TISAX-REQ-START -->
{{#if FLAG_FW_TISAX}}
{{#if FLAG_FW_ISO27001}}*Requirements per VDA ISA 2027:*{{/if}}
<!-- REQ 2.1.1-M1 -->
- **[MUST]** Sensitive work areas and activities are determined.
<!-- REQ 2.1.1-M2 -->
- **[MUST]** The requirements for employees with regard to their job profiles are determined and met.
<!-- REQ 2.1.1-M3 -->
- **[MUST]** The identity of potential employees is verified (e.g. checking of identity documents).
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 2.1.1-S1 -->
- **[SHOULD]** The personal suitability of potential employees is checked using simple methods (e.g. job interview).
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 2.1.1-S2 -->
- **[SHOULD]** An extended suitability check depending on the work area and the activity is carried out (e.g. assessment centre, checking of references, certificates and criminal record certificates).
{{/if}}
{{/if}}
<!-- FW:TISAX-REQ-END -->
<!-- FW:ISO-REQ-START -->
{{#if FLAG_FW_ISO27001}}
{{#if FLAG_FW_TISAX}}*Requirements per ISO/IEC 27001:*{{/if}}
<!-- REQ 7.2-1 -->
- **[ISO 7.2]** The necessary competence is determined and ensured; corresponding evidence is retained.
<!-- REQ A.6.1-1 -->
- **[ISO A.6.1]** Background verification of candidates is carried out appropriately to the business requirements and in accordance with the law.
{{/if}}
<!-- FW:ISO-REQ-END -->
**Implementation at {{ORG_NAME}}**
<!-- IMPL 2.1.1 -->
Sensitive work areas and activities are determined in the register of sensitive activities ({{LINK:REG-SENS-ROLES}}) and recorded with the required depth of checking; requirements for positions are documented in job descriptions and are met. Identity verification as well as the personal and — for sensitive roles — extended suitability check (interview, references, criminal record certificate within the legally permissible scope) are carried out following the suitability and verification procedure ({{LINK:VA-14}}); responsible: {{ROLE_HR_LEAD}}; evidence in the personnel file.
### 3.2 Contractual commitment of personnel
<!-- FW:REF-START ORIG:(ISA 2.1.2) -->
*Requirement reference:* {{#if FLAG_FW_TISAX}}VDA ISA 2.1.2{{/if}}{{#if FLAG_FW_ISO27001}}{{#if FLAG_FW_TISAX}} · {{/if}}ISO/IEC 27001 A.6.2, A.6.5, A.6.6{{/if}}
<!-- FW:REF-END -->
**Requirement**
<!-- FW:TISAX-REQ-START -->
{{#if FLAG_FW_TISAX}}
{{#if FLAG_FW_ISO27001}}*Requirements per VDA ISA 2027:*{{/if}}
<!-- REQ 2.1.2-M1 -->
- **[MUST]** A confidentiality obligation is in force.
<!-- REQ 2.1.2-M2 -->
- **[MUST]** An obligation to comply with the information security policies is in force.
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 2.1.2-S1 -->
- **[SHOULD]** A confidentiality obligation going beyond the employment contract is in force.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 2.1.2-S2 -->
- **[SHOULD]** Information security aspects are taken into account in the employees' employment contracts.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 2.1.2-S3 -->
- **[SHOULD]** A procedure for dealing with violations of these obligations is described.
{{/if}}
{{/if}}
<!-- FW:TISAX-REQ-END -->
<!-- FW:ISO-REQ-START -->
{{#if FLAG_FW_ISO27001}}
{{#if FLAG_FW_TISAX}}*Requirements per ISO/IEC 27001:*{{/if}}
<!-- REQ A.6.2-1 -->
- **[ISO A.6.2]** The employment agreements state the responsibilities for information security.
<!-- REQ A.6.5-1 -->
- **[ISO A.6.5]** Continuing information security responsibilities after termination or change of employment are defined and enforced.
<!-- REQ A.6.6-1 -->
- **[ISO A.6.6]** Confidentiality or non-disclosure agreements are identified, documented and reviewed regularly.
{{/if}}
<!-- FW:ISO-REQ-END -->
**Implementation at {{ORG_NAME}}**
<!-- IMPL 2.1.2 -->
All employees are contractually obliged upon joining to confidentiality and to compliance with the information security policies ({{ROLE_HR_LEAD}}); information security aspects are part of the employment contracts, and confidentiality continues to apply after termination. A documented procedure for dealing with violations (see {{LINK:VA-14}}) is established; the evidence is kept in the personnel file.
### 3.3 Awareness and training
<!-- FW:REF-START ORIG:(ISA 2.1.3) -->
*Requirement reference:* {{#if FLAG_FW_TISAX}}VDA ISA 2.1.3{{/if}}{{#if FLAG_FW_ISO27001}}{{#if FLAG_FW_TISAX}} · {{/if}}ISO/IEC 27001 7.3, A.6.3{{/if}}
<!-- FW:REF-END -->
**Requirement**
<!-- FW:TISAX-REQ-START -->
{{#if FLAG_FW_TISAX}}
{{#if FLAG_FW_ISO27001}}*Requirements per VDA ISA 2027:*{{/if}}
<!-- REQ 2.1.3-M1 -->
- **[MUST]** Employees are trained and made aware.
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 2.1.3-S1 -->
- **[SHOULD]** A concept for the awareness and training of employees is created.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 2.1.3-S2 -->
- **[SHOULD]** Target groups for training and awareness measures (e.g. managers, administrators, employees with access to customer networks, production personnel) are identified and taken into account in the concept.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 2.1.3-S3 -->
- **[SHOULD]** The concept is approved by the responsible management.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 2.1.3-S4 -->
- **[SHOULD]** Training and awareness measures are carried out regularly and on an ad-hoc basis.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 2.1.3-S5 -->
- **[SHOULD]** Participation in training and awareness measures is documented.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 2.1.3-S6 -->
- **[SHOULD]** Points of contact for information security are known to the employees.
{{/if}}
{{/if}}
<!-- FW:TISAX-REQ-END -->
<!-- FW:ISO-REQ-START -->
{{#if FLAG_FW_ISO27001}}
{{#if FLAG_FW_TISAX}}*Requirements per ISO/IEC 27001:*{{/if}}
<!-- REQ 7.3-1 -->
- **[ISO 7.3]** Persons under the organisation's control are aware of the policy, their contribution and the consequences of non-conformance.
<!-- REQ A.6.3-1 -->
- **[ISO A.6.3]** Personnel receive appropriate awareness, education and training as well as regular updates of the relevant policies.
{{/if}}
<!-- FW:ISO-REQ-END -->
**Implementation at {{ORG_NAME}}**
<!-- IMPL 2.1.3 -->
A role-specific training/awareness concept approved by management (BL-HR-01) is established; employees are trained upon joining and thereafter at least {{REVIEW_CYCLE}} and on an ad-hoc basis (process see {{LINK:VA-12}}). Target groups are identified, records of participation are kept in {{TOOL_NAME}}, points of contact for information security are known; the effectiveness is checked (e.g. phishing simulation).
## 4. Binding nature
This policy is binding for all affected roles within the scope. Compliance is monitored by {{ROLE_HR_LEAD}}.
## 5. Roles and responsibilities
| Role | Responsibility in this policy |
|-------|-------------------------------------|
| {{ROLE_HR_LEAD}} | Commitment, suitability |
| {{ROLE_ISB}} | Awareness/training |
## 6. Review and update
This policy is reviewed at least {{REVIEW_CYCLE}} and on an ad-hoc basis by {{ROLE_HR_LEAD}} and approved by {{ROLE_MANAGEMENT}}.
## 7. Evidence
The evidence is not maintained in this document but centrally in the evidence register ({{LINK:NACHWEISREGISTER}}) and in the associated entries of the ISMS tool ({{TOOL_NAME}}).
## 8. Related documents
- Associated procedures: {{LINK:VA-12}}
- Technical security baseline: {{LINK:BASELINE}}
- ISA mapping matrix: {{LINK:ISA_MAPPING}}
- Evidence register: {{LINK:NACHWEISREGISTER}}
- Further: {{LINK:R01}}, {{LINK:R06}}
<!-- Anforderungen 1:1 aus VDA ISA 2027; Mapping (REQ/IMPL) in mapping.json ueber Hidden-Anker. Im Lesemodus nicht sichtbar. -->
<!-- FW:ISO-SECTION-START -->
{{#if FLAG_FW_ISO27001}}
### 3.4 Handling of violations
*Requirement reference:* ISO/IEC 27001 A.6.4
**Requirement**
<!-- REQ A.6.4-1 -->
- **[ISO A.6.4]** A disciplinary process for information security violations is established and communicated.
**Implementation at {{ORG_NAME}}**
<!-- IMPL ISO-DISZIPLIN -->
A graduated, documented process applies to violations of the information security requirements and is communicated in advance. It takes into account the nature and severity of the violation, intent or negligence, repetition and the training status of the person concerned. The process is run by {{ROLE_HR_LEAD}} in coordination with {{ROLE_ISB}}; employment law requirements and co-determination rights are observed. Its application is documented confidentially.
{{/if}}
<!-- FW:ISO-SECTION-END -->
@@ -1,151 +0,0 @@
# Policy Mobile Working and Mobile Devices
| Document information | Value |
|-----------------------|------|
| Document type | Policy |
| Scope | {{ISMS_SCOPE}} |
| Organisation | {{ORG_NAME}} |
| Responsible | {{ROLE_ISB}} |
| Approved by | {{ROLE_MANAGEMENT}} |
| Version | {{DOC_VERSION}} |
| Date | {{DOC_DATE}} |
| Status | {{DOC_STATUS}} |
## 1. Purpose
This policy governs mobile working as well as the handling of mobile IT devices and data media. It elaborates the information security policy ({{LINK:L00}}) and serves to meet the requirements of VDA ISA 2027.
## 2. Scope
This policy applies within the defined ISMS scope ({{ISMS_SCOPE_DESCRIPTION}}).
## 3. Requirements and implementation
> Structure per section: **Requirement** (1:1 from VDA ISA; [MUST]/[SHOULD] and — where the protection need applies — [HIGH]/[VERY HIGH]) and **Implementation at {{ORG_NAME}}** (consolidated, to be adjusted where necessary).
{{#if FLAG_MOBILE_WORK}}
### 3.1 Mobile working
<!-- FW:REF-START ORIG:(ISA 2.1.4) -->
*Requirement reference:* {{#if FLAG_FW_TISAX}}VDA ISA 2.1.4{{/if}}{{#if FLAG_FW_ISO27001}}{{#if FLAG_FW_TISAX}} · {{/if}}ISO/IEC 27001 A.6.7{{/if}}
<!-- FW:REF-END -->
**Requirement**
<!-- FW:TISAX-REQ-START -->
{{#if FLAG_FW_TISAX}}
{{#if FLAG_FW_ISO27001}}*Requirements per VDA ISA 2027:*{{/if}}
<!-- REQ 2.1.4-M1 -->
- **[MUST]** The requirements for mobile working are determined and met; the relevant aspects are taken into account.
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 2.1.4-S1 -->
- **[SHOULD]** The relevant aspects of mobile working are taken into account.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 2.1.4-S2 -->
- **[SHOULD]** Awareness of employees.
{{/if}}
{{#if FLAG_HIGH_PROTECTION}}
<!-- REQ 2.1.4-H1 -->
- **[HIGH]** Protective measures against eavesdropping and being overlooked are implemented. (C)
{{/if}}
{{/if}}
<!-- FW:TISAX-REQ-END -->
<!-- FW:ISO-REQ-START -->
{{#if FLAG_FW_ISO27001}}
{{#if FLAG_FW_TISAX}}*Requirements per ISO/IEC 27001:*{{/if}}
<!-- REQ A.6.7-1 -->
- **[ISO A.6.7]** Security measures for working outside the organisation's premises are implemented.
{{/if}}
<!-- FW:ISO-REQ-END -->
**Implementation at {{ORG_NAME}}**
<!-- IMPL 2.1.4 -->
Mobile working is defined in this policy and the associated mobile working rule (stored in {{TOOL_NAME}}) and the requirements are met; access is exclusively via {{TECH_VPN}} with MFA (BL-IAM-02) and approved, encrypted devices (BL-CRY-03). Employees are made aware (BL-HR-01).
{{#if FLAG_ELEVATED_PROTECTION}}
<!-- IMPL 2.1.4-elev -->
Where the protection need is high, protective measures against eavesdropping and being overlooked are implemented (e.g. privacy screen, quiet environment, clean screen).
{{/if}}
{{/if}}
{{#if FLAG_MOBILE_DEVICES}}
### 3.2 Mobile IT devices and data media
<!-- FW:REF-START ORIG:(ISA 3.1.4) -->
*Requirement reference:* {{#if FLAG_FW_TISAX}}VDA ISA 3.1.4{{/if}}{{#if FLAG_FW_ISO27001}}{{#if FLAG_FW_TISAX}} · {{/if}}ISO/IEC 27001 A.7.9, A.7.10, A.8.1{{/if}}
<!-- FW:REF-END -->
**Requirement**
<!-- FW:TISAX-REQ-START -->
{{#if FLAG_FW_TISAX}}
{{#if FLAG_FW_ISO27001}}*Requirements per VDA ISA 2027:*{{/if}}
<!-- REQ 3.1.4-M1 -->
- **[MUST]** The requirements for mobile IT devices and mobile data media are determined and met; the relevant aspects are taken into account.
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 3.1.4-S1 -->
- **[SHOULD]** Registration of the IT devices.
{{/if}}
{{#if FLAG_HIGH_PROTECTION}}
<!-- REQ 3.1.4-H1 -->
- **[HIGH]** General encryption of mobile data media or of the information assets stored on them. Where technically not feasible, information is protected by equivalent measures. (C, I)
{{/if}}
{{/if}}
<!-- FW:TISAX-REQ-END -->
<!-- FW:ISO-REQ-START -->
{{#if FLAG_FW_ISO27001}}
{{#if FLAG_FW_TISAX}}*Requirements per ISO/IEC 27001:*{{/if}}
<!-- REQ A.7.9-1 -->
- **[ISO A.7.9]** Assets used outside the premises are protected.
<!-- REQ A.7.10-1 -->
- **[ISO A.7.10]** Storage media are protected throughout their life cycle (acquisition, use, transport, disposal) in accordance with the classification scheme.
<!-- REQ A.8.1-1 -->
- **[ISO A.8.1]** Information stored on, processed by or accessible via user endpoint devices is protected.
{{/if}}
<!-- FW:ISO-REQ-END -->
**Implementation at {{ORG_NAME}}**
<!-- IMPL 3.1.4 -->
The requirements for mobile devices and data media are determined and met: devices are registered and centrally managed via {{TECH_MDM}}, only approved devices are used; loss is reported via the reporting path (R04) and {{TOOL_TICKET}}, blocking/wiping upon loss via {{TECH_MDM}} (BL-EP-02).
{{#if FLAG_ELEVATED_PROTECTION}}
<!-- IMPL 3.1.4-elev -->
Where the protection need is high, mobile data media or the information stored on them are generally encrypted (BL-CRY-03); where not feasible, equivalent protective measures apply.
{{/if}}
{{/if}}
## 4. Binding nature
This policy is binding for all affected roles within the scope. Compliance is monitored by {{ROLE_ISB}}.
## 5. Roles and responsibilities
| Role | Responsibility in this policy |
|-------|-------------------------------------|
| {{ROLE_ISB}} | Security requirements |
| {{ROLE_IT_LEAD}} | Technical implementation |
## 6. Review and update
This policy is reviewed at least {{REVIEW_CYCLE}} and on an ad-hoc basis by {{ROLE_ISB}} and approved by {{ROLE_MANAGEMENT}}.
## 7. Evidence
The evidence is not maintained in this document but centrally in the evidence register ({{LINK:NACHWEISREGISTER}}) and in the associated entries of the ISMS tool ({{TOOL_NAME}}).
## 8. Related documents
- Technical security baseline: {{LINK:BASELINE}}
- ISA mapping matrix: {{LINK:ISA_MAPPING}}
- Evidence register: {{LINK:NACHWEISREGISTER}}
- Further: {{LINK:R05}}, {{LINK:R07}}, {{LINK:R08}}
<!-- Anforderungen 1:1 aus VDA ISA 2027; Mapping (REQ/IMPL) in mapping.json ueber Hidden-Anker. Im Lesemodus nicht sichtbar. -->
@@ -1,173 +0,0 @@
# Policy Physical Security
| Document information | Value |
|-----------------------|------|
| Document type | Policy |
| Scope | {{ISMS_SCOPE}} |
| Organisation | {{ORG_NAME}} |
| Responsible | {{ROLE_IT_LEAD}} |
| Approved by | {{ROLE_MANAGEMENT}} |
| Version | {{DOC_VERSION}} |
| Date | {{DOC_DATE}} |
| Status | {{DOC_STATUS}} |
## 1. Purpose
This policy governs physical protection through security zones, access protection and the handling of supporting utilities. It elaborates the information security policy ({{LINK:L00}}) and serves to meet the requirements of VDA ISA 2027.
## 2. Scope
This policy applies within the defined ISMS scope ({{ISMS_SCOPE_DESCRIPTION}}).
## 3. Requirements and implementation
> Structure per section: **Requirement** (1:1 from VDA ISA; [MUST]/[SHOULD] and — where the protection need applies — [HIGH]/[VERY HIGH]) and **Implementation at {{ORG_NAME}}** (consolidated, to be adjusted where necessary).
### 3.1 Security zones and access
<!-- FW:REF-START ORIG:(ISA 3.1.1) -->
*Requirement reference:* {{#if FLAG_FW_TISAX}}VDA ISA 3.1.1{{/if}}{{#if FLAG_FW_ISO27001}}{{#if FLAG_FW_TISAX}} · {{/if}}ISO/IEC 27001 A.7.1, A.7.2, A.7.3, A.7.4, A.7.6{{/if}}
<!-- FW:REF-END -->
**Requirement**
<!-- FW:TISAX-REQ-START -->
{{#if FLAG_FW_TISAX}}
{{#if FLAG_FW_ISO27001}}*Requirements per VDA ISA 2027:*{{/if}}
<!-- REQ 3.1.1-M1 -->
- **[MUST]** A security zone concept including associated protective measures based on the requirements for handling information assets is in place.
<!-- REQ 3.1.1-M2 -->
- **[MUST]** The defined protective measures are implemented.
<!-- REQ 3.1.1-M3 -->
- **[MUST]** The code of conduct for security zones is known to all persons involved.
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 3.1.1-S1 -->
- **[SHOULD]** Procedures for granting and revoking access rights are established.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 3.1.1-S2 -->
- **[SHOULD]** Policies for visitor management (including registration and escorting of visitors) are defined.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 3.1.1-S3 -->
- **[SHOULD]** Policies for carrying and using mobile IT devices and data media (e.g. registration, labelling obligations) are defined and implemented.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 3.1.1-S4 -->
- **[SHOULD]** Network/infrastructure components (own or customer networks) are protected against unauthorised access.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 3.1.1-S5 -->
- **[SHOULD]** External premises used for storing/processing information assets are taken into account in the zone concept (e.g. storage rooms, workshops, test tracks, data centres).
{{/if}}
{{#if FLAG_HIGH_PROTECTION}}
<!-- REQ 3.1.1-H1 -->
- **[HIGH]** Protective measures against simple eavesdropping and being overlooked are implemented. (C)
{{/if}}
{{/if}}
<!-- FW:TISAX-REQ-END -->
<!-- FW:ISO-REQ-START -->
{{#if FLAG_FW_ISO27001}}
{{#if FLAG_FW_TISAX}}*Requirements per ISO/IEC 27001:*{{/if}}
<!-- REQ A.7.1-1 -->
- **[ISO A.7.1]** Security perimeters are defined and used to protect areas containing information and assets.
<!-- REQ A.7.2-1 -->
- **[ISO A.7.2]** Secure entry controls and entry points are established to restrict access to authorised persons.
<!-- REQ A.7.3-1 -->
- **[ISO A.7.3]** Physical security for offices, rooms and facilities is designed and implemented.
<!-- REQ A.7.4-1 -->
- **[ISO A.7.4]** Premises are continuously monitored for unauthorised physical access.
<!-- REQ A.7.6-1 -->
- **[ISO A.7.6]** Measures for working in secure areas are defined and implemented.
{{/if}}
<!-- FW:ISO-REQ-END -->
**Implementation at {{ORG_NAME}}**
<!-- IMPL 3.1.1 -->
A security zone concept (BL-PHY-01) with implemented protective measures and a known code of conduct is in place; access rights are granted on a needs-oriented basis via {{TOOL_TICKET}}, documented and revoked when no longer needed (BL-PHY-02, process see {{LINK:VA-17}}). Visitor management, rules for mobile devices, protection of network/infrastructure components and external premises are taken into account.
{{#if FLAG_ELEVATED_PROTECTION}}
<!-- IMPL 3.1.1-elev -->
Where the protection need is high, additional protective measures against simple eavesdropping and being overlooked are implemented.
{{/if}}
<!-- Scope-Hinweis (E2): ISA 3.1.2 ist in VDA-ISA 2027 deprecated; ISA 3.1.3 existiert nicht.
Daher kein Abschnitt/REQ/IMPL für 3.1.2/3.1.3 in R07 und mapping.json. -->
## 4. Binding nature
This policy is binding for all affected roles within the scope. Compliance is monitored by {{ROLE_IT_LEAD}}.
## 5. Roles and responsibilities
| Role | Responsibility in this policy |
|-------|-------------------------------------|
| {{ROLE_IT_LEAD}} | Zones, access, utilities |
| {{ROLE_ISB}} | Specifications |
## 6. Review and update
This policy is reviewed at least {{REVIEW_CYCLE}} and on an ad-hoc basis by {{ROLE_IT_LEAD}} and approved by {{ROLE_MANAGEMENT}}.
## 7. Evidence
The evidence is not maintained in this document but centrally in the evidence register ({{LINK:NACHWEISREGISTER}}) and in the associated entries of the ISMS tool ({{TOOL_NAME}}).
## 8. Related documents
- Technical security baseline: {{LINK:BASELINE}}
- ISA mapping matrix: {{LINK:ISA_MAPPING}}
- Evidence register: {{LINK:NACHWEISREGISTER}}
- Further: {{LINK:R02}}, {{LINK:R06}}
<!-- Anforderungen 1:1 aus VDA ISA 2027; Mapping (REQ/IMPL) in mapping.json ueber Hidden-Anker. Im Lesemodus nicht sichtbar. -->
<!-- FW:ISO-SECTION-START -->
{{#if FLAG_FW_ISO27001}}
### 3.2 Environmental protection, utilities, cabling and maintenance
*Requirement reference:* ISO/IEC 27001 A.7.5, A.7.8, A.7.11, A.7.12, A.7.13
**Requirement**
<!-- REQ A.7.5-1 -->
- **[ISO A.7.5]** Protection against physical and environmental threats is designed and implemented.
<!-- REQ A.7.8-1 -->
- **[ISO A.7.8]** Equipment is sited securely and protected.
<!-- REQ A.7.11-1 -->
- **[ISO A.7.11]** Facilities are protected against failure and disruption of supporting utilities such as power and air conditioning.
<!-- REQ A.7.12-1 -->
- **[ISO A.7.12]** Power and data cabling is protected against interception, interference and damage.
<!-- REQ A.7.13-1 -->
- **[ISO A.7.13]** Equipment is maintained properly to ensure availability and integrity.
**Implementation at {{ORG_NAME}}**
<!-- IMPL ISO-PHY-UMWELT -->
Sites and technical facilities are protected against physical and environmental threats (BL-PHY-03): early fire detection, protection against water and moisture, temperature and humidity monitoring in technical rooms as well as consideration of site-specific hazards. Equipment is sited so that observation, unauthorised access and environmental risks are minimised. Power and air conditioning for critical systems are designed to be uninterruptible and are tested regularly. Power and data cabling is protected against damage and unauthorised access and is documented. Equipment is maintained according to the manufacturer's specifications; maintenance is carried out only by authorised personnel, is planned and recorded, and is supervised where performed externally.
{{/if}}
<!-- FW:ISO-SECTION-END -->
<!-- FW:ISO-SECTION-START -->
{{#if FLAG_FW_ISO27001}}
### 3.3 Clear desk and screen lock
*Requirement reference:* ISO/IEC 27001 A.7.7
**Requirement**
<!-- REQ A.7.7-1 -->
- **[ISO A.7.7]** Rules for a clear desk and locked screens are defined and implemented.
**Implementation at {{ORG_NAME}}**
<!-- IMPL ISO-PHY-CLEARDESK -->
Binding rules apply for a clear desk and locked screens (BL-PHY-04): protected documents and media are locked away when unattended; screens are locked when leaving the workplace and lock automatically after {{SESSION_TIMEOUT}}. Printouts are collected immediately and documents no longer required are destroyed according to their protection needs (BL-DEL-01). The rules also apply when working from home and at mobile workplaces ({{LINK:R06}}); compliance is checked on a sample basis.
{{/if}}
<!-- FW:ISO-SECTION-END -->
@@ -1,314 +0,0 @@
# Policy Identity and Access Management
| Document information | Value |
|-----------------------|------|
| Document type | Policy |
| Scope | {{ISMS_SCOPE}} |
| Organisation | {{ORG_NAME}} |
| Responsible | {{ROLE_IT_LEAD}} |
| Approved by | {{ROLE_MANAGEMENT}} |
| Version | {{DOC_VERSION}} |
| Date | {{DOC_DATE}} |
| Status | {{DOC_STATUS}} |
## 1. Purpose
This policy governs means of identification, secure log-on, account management as well as the granting and control of access rights. It elaborates the information security policy ({{LINK:L00}}) and serves to meet the requirements of VDA ISA 2027.
## 2. Scope
This policy applies within the defined ISMS scope ({{ISMS_SCOPE_DESCRIPTION}}).
## 3. Requirements and implementation
> Structure per section: **Requirement** (1:1 from VDA ISA; [MUST]/[SHOULD] and — where the protection need applies — [HIGH]/[VERY HIGH]) and **Implementation at {{ORG_NAME}}** (consolidated, to be adjusted where necessary).
### 3.1 Handling of means of identification
<!-- FW:REF-START ORIG:(ISA 4.1.1) -->
*Requirement reference:* {{#if FLAG_FW_TISAX}}VDA ISA 4.1.1{{/if}}{{#if FLAG_FW_ISO27001}}{{#if FLAG_FW_TISAX}} · {{/if}}ISO/IEC 27001 A.5.16{{/if}}
<!-- FW:REF-END -->
**Requirement**
<!-- FW:TISAX-REQ-START -->
{{#if FLAG_FW_TISAX}}
{{#if FLAG_FW_ISO27001}}*Requirements per VDA ISA 2027:*{{/if}}
<!-- REQ 4.1.1-M1 -->
- **[MUST]** The requirements for handling means of identification throughout the entire lifecycle are determined and met; the relevant aspects are taken into account.
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 4.1.1-S1 -->
- **[SHOULD]** Means of identification can only be created under controlled conditions.
{{/if}}
{{#if FLAG_HIGH_PROTECTION}}
<!-- REQ 4.1.1-H1 -->
- **[HIGH]** A strategy for blocking or invalidating means of identification in the event of loss is prepared and, as far as possible, implemented. (C, I, A)
{{/if}}
{{/if}}
<!-- FW:TISAX-REQ-END -->
<!-- FW:ISO-REQ-START -->
{{#if FLAG_FW_ISO27001}}
{{#if FLAG_FW_TISAX}}*Requirements per ISO/IEC 27001:*{{/if}}
<!-- REQ A.5.16-1 -->
- **[ISO A.5.16]** The full life cycle of identities is managed.
{{/if}}
<!-- FW:ISO-REQ-END -->
**Implementation at {{ORG_NAME}}**
<!-- IMPL 4.1.1 -->
Means of identification (user IDs, tokens, certificates) are assigned throughout the lifecycle uniquely to a person and under controlled conditions via the central directory ({{TOOL_IAM}}); issuance, withdrawal and blocking are requested, approved and documented in {{TOOL_TICKET}} (BL-IAM-07, see {{LINK:VA-03}}).
{{#if FLAG_ELEVATED_PROTECTION}}
<!-- IMPL 4.1.1-elev -->
Where the protection need is high, an implemented strategy for blocking/invalidating means of identification in the event of loss is in place.
{{/if}}
### 3.2 Secure log-on
<!-- FW:REF-START ORIG:(ISA 4.1.2) -->
*Requirement reference:* {{#if FLAG_FW_TISAX}}VDA ISA 4.1.2{{/if}}{{#if FLAG_FW_ISO27001}}{{#if FLAG_FW_TISAX}} · {{/if}}ISO/IEC 27001 A.8.5{{/if}}
<!-- FW:REF-END -->
**Requirement**
<!-- FW:TISAX-REQ-START -->
{{#if FLAG_FW_TISAX}}
{{#if FLAG_FW_ISO27001}}*Requirements per VDA ISA 2027:*{{/if}}
<!-- REQ 4.1.2-M1 -->
- **[MUST]** The user authentication procedures are selected on the basis of a risk assessment; possible attack scenarios (e.g. direct reachability via the internet) have been taken into account.
<!-- REQ 4.1.2-M2 -->
- **[MUST]** State-of-the-art user authentication procedures are applied.
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 4.1.2-S1 -->
- **[SHOULD]** The authentication procedures are defined and implemented on the basis of business and security requirements.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 4.1.2-S2 -->
- **[SHOULD]** Users are authenticated at least by strong passwords in line with established and recognised practices.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 4.1.2-S3 -->
- **[SHOULD]** For privileged user accounts, higher-grade procedures are used (e.g. privileged access management, two-factor authentication).
{{/if}}
{{#if FLAG_HIGH_PROTECTION}}
<!-- REQ 4.1.2-H1 -->
- **[HIGH]** Depending on the risk assessment, authentication and access control are strengthened by supplementary measures (e.g. continuous access monitoring, strong authentication, automatic log-off, lock upon inactivity, brute-force prevention). (C, I, A)
{{/if}}
{{#if FLAG_VERY_HIGH_PROTECTION}}
<!-- REQ 4.1.2-V1 -->
- **[VERY HIGH]** Before accessing data with a very high protection need, users are authenticated by means of strong authentication (e.g. two-factor) in line with the state of the art. (C, I)
{{/if}}
{{/if}}
<!-- FW:TISAX-REQ-END -->
<!-- FW:ISO-REQ-START -->
{{#if FLAG_FW_ISO27001}}
{{#if FLAG_FW_TISAX}}*Requirements per ISO/IEC 27001:*{{/if}}
<!-- REQ A.8.5-1 -->
- **[ISO A.8.5]** Secure authentication technologies and procedures are used on the basis of the access restrictions and the access control policy.
{{/if}}
<!-- FW:ISO-REQ-END -->
**Implementation at {{ORG_NAME}}**
<!-- IMPL 4.1.2 -->
The authentication procedures are selected on a risk basis and correspond to the state of the art; password requirements per BL-IAM-01 (at least {{PW_MIN_LENGTH}} characters, {{PW_COMPLEXITY}}, {{PW_ROTATION}}) are enforced via the central directory ({{TOOL_IAM}}). For remote access, administrative access and cloud services, MFA (BL-IAM-02) is enforced via {{TECH_MFA}}; privileged accounts use higher-grade procedures (PAM).
{{#if FLAG_ELEVATED_PROTECTION}}
<!-- IMPL 4.1.2-elev -->
Where the protection need is high, authentication/access control are strengthened by supplementary measures (access monitoring, auto-logout BL-IAM-03, lock BL-IAM-04, brute-force protection). {{#if FLAG_VERY_HIGH_PROTECTION}}Where the protection need is very high, access is only granted after strong authentication (two-factor).{{/if}}
{{/if}}
### 3.3 User accounts and log-on information
<!-- FW:REF-START ORIG:(ISA 4.1.3) -->
*Requirement reference:* {{#if FLAG_FW_TISAX}}VDA ISA 4.1.3{{/if}}{{#if FLAG_FW_ISO27001}}{{#if FLAG_FW_TISAX}} · {{/if}}ISO/IEC 27001 A.5.17{{/if}}
<!-- FW:REF-END -->
**Requirement**
<!-- FW:TISAX-REQ-START -->
{{#if FLAG_FW_TISAX}}
{{#if FLAG_FW_ISO27001}}*Requirements per VDA ISA 2027:*{{/if}}
<!-- REQ 4.1.3-M1 -->
- **[MUST]** The creation, modification and deletion of user accounts is carried out.
<!-- REQ 4.1.3-M2 -->
- **[MUST]** Unique and personalised user accounts are used.
<!-- REQ 4.1.3-M3 -->
- **[MUST]** The use of shared accounts is regulated (e.g. limited to cases where traceability is dispensable).
<!-- REQ 4.1.3-M4 -->
- **[MUST]** User accounts are deactivated immediately after the user leaves (e.g. upon end of contract).
<!-- REQ 4.1.3-M5 -->
- **[MUST]** User accounts are reviewed regularly.
<!-- REQ 4.1.3-M6 -->
- **[MUST]** The log-on information is provided to the user in a secure manner.
<!-- REQ 4.1.3-M7 -->
- **[MUST]** A policy for handling log-on information is defined and implemented; the relevant aspects are taken into account.
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 4.1.3-S1 -->
- **[SHOULD]** A base account with minimal access rights and functionalities exists and is used.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 4.1.3-S2 -->
- **[SHOULD]** Default accounts and passwords preconfigured by the manufacturer are deactivated (e.g. blocking or password change).
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 4.1.3-S3 -->
- **[SHOULD]** User accounts are created or authorised by the responsible body.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 4.1.3-S4 -->
- **[SHOULD]** The creation of user accounts is subject to an approval process (four-eyes principle).
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 4.1.3-S5 -->
- **[SHOULD]** User accounts of service providers are deactivated after completion of their task.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 4.1.3-S6 -->
- **[SHOULD]** Deadlines for deactivating and deleting user accounts are defined.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 4.1.3-S7 -->
- **[SHOULD]** The use of default passwords is prevented technically.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 4.1.3-S8 -->
- **[SHOULD]** In the case of strong authentication, the use of the medium (e.g. possession factor) is secure.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 4.1.3-S9 -->
- **[SHOULD]** User accounts are reviewed regularly; this also includes accounts in customers' IT systems.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 4.1.3-S10 -->
- **[SHOULD]** Interactive log-on for service accounts (technical accounts) is prevented technically.
{{/if}}
{{/if}}
<!-- FW:TISAX-REQ-END -->
<!-- FW:ISO-REQ-START -->
{{#if FLAG_FW_ISO27001}}
{{#if FLAG_FW_TISAX}}*Requirements per ISO/IEC 27001:*{{/if}}
<!-- REQ A.5.17-1 -->
- **[ISO A.5.17]** Allocation and management of authentication information is controlled by a suitable management process.
{{/if}}
<!-- FW:ISO-REQ-END -->
**Implementation at {{ORG_NAME}}**
<!-- IMPL 4.1.3 -->
User accounts are managed via a defined lifecycle (joiner/mover/leaver) (see {{LINK:VA-03}}) uniquely personalised in the central directory ({{TOOL_IAM}}); triggers are {{TOOL_TICKET}} requests from HR/manager notifications. Accounts of leavers are deactivated without delay, accounts are reviewed regularly (including in customer systems), shared accounts are regulated. Log-on information is provided securely; default accounts/passwords are deactivated, base accounts with minimal rights are used, creation follows the four-eyes principle, and interactive log-on for technical accounts is prevented.
### 3.4 Access rights
<!-- FW:REF-START ORIG:(ISA 4.2.1) -->
*Requirement reference:* {{#if FLAG_FW_TISAX}}VDA ISA 4.2.1{{/if}}{{#if FLAG_FW_ISO27001}}{{#if FLAG_FW_TISAX}} · {{/if}}ISO/IEC 27001 A.5.15, A.5.18, A.8.2, A.8.3, A.8.18{{/if}}
<!-- FW:REF-END -->
**Requirement**
<!-- FW:TISAX-REQ-START -->
{{#if FLAG_FW_TISAX}}
{{#if FLAG_FW_ISO27001}}*Requirements per VDA ISA 2027:*{{/if}}
<!-- REQ 4.2.1-M1 -->
- **[MUST]** The requirements for managing access rights (authorisation) are determined and met; the relevant aspects are taken into account.
<!-- REQ 4.2.1-M2 -->
- **[MUST]** The access rights granted for normal and privileged user accounts as well as technical accounts are reviewed regularly, also in customers' IT systems.
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 4.2.1-S1 -->
- **[SHOULD]** Strategies for authorising access to information are prepared.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 4.2.1-S2 -->
- **[SHOULD]** Authorisation roles are used.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 4.2.1-S3 -->
- **[SHOULD]** Rights are granted according to the need-to-use principle and in line with role and/or area of responsibility.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 4.2.1-S4 -->
- **[SHOULD]** Normal user accounts do not receive privileged access rights.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 4.2.1-S5 -->
- **[SHOULD]** The user's access rights are updated after a change in their responsibilities.
{{/if}}
{{#if FLAG_HIGH_PROTECTION}}
<!-- REQ 4.2.1-H1 -->
- **[HIGH]** The access rights are approved by the responsible internal information officer. (C, I, A)
{{/if}}
{{#if FLAG_VERY_HIGH_PROTECTION}}
<!-- REQ 4.2.1-V1 -->
- **[VERY HIGH]** Information is stored encrypted at content level (e.g. file level) to prevent unauthorised access (including by privileged users). Where encryption is not feasible, equivalent measures apply. (C)
{{/if}}
{{#if FLAG_VERY_HIGH_PROTECTION}}
<!-- REQ 4.2.1-V2 -->
- **[VERY HIGH]** Existing access rights are reviewed at shorter intervals (e.g. quarterly). (C)
{{/if}}
{{/if}}
<!-- FW:TISAX-REQ-END -->
<!-- FW:ISO-REQ-START -->
{{#if FLAG_FW_ISO27001}}
{{#if FLAG_FW_TISAX}}*Requirements per ISO/IEC 27001:*{{/if}}
<!-- REQ A.5.15-1 -->
- **[ISO A.5.15]** Rules to control physical and logical access to information and assets are established and implemented on the basis of business and information security requirements.
<!-- REQ A.5.18-1 -->
- **[ISO A.5.18]** Access rights are provisioned, reviewed, modified and removed in accordance with the access control policy.
<!-- REQ A.8.2-1 -->
- **[ISO A.8.2]** The allocation and use of privileged access rights is restricted and closely managed.
<!-- REQ A.8.3-1 -->
- **[ISO A.8.3]** Access to information and application functions is restricted in accordance with the access control policy.
<!-- REQ A.8.18-1 -->
- **[ISO A.8.18]** The use of utility programs capable of overriding system and application controls is restricted and tightly controlled.
{{/if}}
<!-- FW:ISO-REQ-END -->
**Implementation at {{ORG_NAME}}**
<!-- IMPL 4.2.1 -->
Access rights are granted according to the least-privilege principle (need-to-know/least privilege) on a role basis (RBAC) via the central directory ({{TOOL_IAM}}); request, technical review and approval take place in {{TOOL_TICKET}} (see {{LINK:VA-03}}). Rights are updated or revoked upon change/removal and recertified at least {{RECERT_FREQ}} (BL-IAM-05), also in customer systems; default accounts do not receive privileged rights.
{{#if FLAG_ELEVATED_PROTECTION}}
<!-- IMPL 4.2.1-elev -->
Where the protection need is high, access rights are approved by the responsible internal information officer. {{#if FLAG_VERY_HIGH_PROTECTION}}Where the protection need is very high, information is stored with content-level encryption (protection also against privileged users) and access rights are reviewed at shorter intervals (e.g. quarterly).{{/if}}
{{/if}}
## 4. Binding nature
This policy is binding for all affected roles within the scope. Compliance is monitored by {{ROLE_IT_LEAD}}.
## 5. Roles and responsibilities
| Role | Responsibility in this policy |
|-------|-------------------------------------|
| {{ROLE_IT_LEAD}} | Technical implementation of IAM |
| Business units | Approval of authorisations |
| {{ROLE_ISB}} | Monitoring |
## 6. Review and update
This policy is reviewed at least {{REVIEW_CYCLE}} and on an ad-hoc basis by {{ROLE_IT_LEAD}} and approved by {{ROLE_MANAGEMENT}}.
## 7. Evidence
The evidence is not maintained in this document but centrally in the evidence register ({{LINK:NACHWEISREGISTER}}) and in the associated entries of the ISMS tool ({{TOOL_NAME}}).
## 8. Related documents
- Associated procedures: {{LINK:VA-03}}
- Technical security baseline: {{LINK:BASELINE}}
- ISA mapping matrix: {{LINK:ISA_MAPPING}}
- Evidence register: {{LINK:NACHWEISREGISTER}}
- Further: {{LINK:R02}}, {{LINK:R05}}, {{LINK:R10}}
<!-- Anforderungen 1:1 aus VDA ISA 2027; Mapping (REQ/IMPL) in mapping.json ueber Hidden-Anker. Im Lesemodus nicht sichtbar. -->
@@ -1,156 +0,0 @@
# Policy Cryptography and Transmission Policy
| Document information | Value |
|-----------------------|------|
| Document type | Policy |
| Scope | {{ISMS_SCOPE}} |
| Organisation | {{ORG_NAME}} |
| Responsible | {{ROLE_IT_LEAD}} |
| Approved by | {{ROLE_MANAGEMENT}} |
| Version | {{DOC_VERSION}} |
| Date | {{DOC_DATE}} |
| Status | {{DOC_STATUS}} |
## 1. Purpose
This policy governs cryptographic procedures, key management and protection during information transmission. It elaborates the information security policy ({{LINK:L00}}) and serves to meet the requirements of VDA ISA 2027.
## 2. Scope
This policy applies within the defined ISMS scope ({{ISMS_SCOPE_DESCRIPTION}}).
## 3. Requirements and implementation
> Structure per section: **Requirement** (1:1 from VDA ISA; [MUST]/[SHOULD] and — where the protection need applies — [HIGH]/[VERY HIGH]) and **Implementation at {{ORG_NAME}}** (consolidated, to be adjusted where necessary).
### 3.1 Use of cryptographic procedures
<!-- FW:REF-START ORIG:(ISA 5.1.1) -->
*Requirement reference:* {{#if FLAG_FW_TISAX}}VDA ISA 5.1.1{{/if}}{{#if FLAG_FW_ISO27001}}{{#if FLAG_FW_TISAX}} · {{/if}}ISO/IEC 27001 A.8.24{{/if}}
<!-- FW:REF-END -->
**Requirement**
<!-- FW:TISAX-REQ-START -->
{{#if FLAG_FW_TISAX}}
{{#if FLAG_FW_ISO27001}}*Requirements per VDA ISA 2027:*{{/if}}
<!-- REQ 5.1.1-M1 -->
- **[MUST]** All cryptographic procedures used (e.g. encryption, signature, hash algorithms, protocols) provide the security required in the respective field of application according to a recognised industry standard, as far as legally possible.
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 5.1.1-S1 -->
- **[SHOULD]** A concept for the use of cryptography is defined and implemented; the relevant aspects are taken into account.
{{/if}}
{{#if FLAG_HIGH_PROTECTION}}
<!-- REQ 5.1.1-H1 -->
- **[HIGH]** Requirements for key sovereignty (in particular in the case of external processing) are determined and met. (C, I)
{{/if}}
{{/if}}
<!-- FW:TISAX-REQ-END -->
<!-- FW:ISO-REQ-START -->
{{#if FLAG_FW_ISO27001}}
{{#if FLAG_FW_TISAX}}*Requirements per ISO/IEC 27001:*{{/if}}
<!-- REQ A.8.24-1 -->
- **[ISO A.8.24]** Rules for the effective use of cryptography, including key management, are defined and implemented.
{{/if}}
<!-- FW:ISO-REQ-END -->
**Implementation at {{ORG_NAME}}**
<!-- IMPL 5.1.1 -->
The permissible procedures and key lengths per BL-CRY-02 ({{CRYPTO_ALGO}}) correspond to the recognised industry standard and are prescribed; outdated procedures are prohibited. A cryptography concept is documented (see {{LINK:VA-07}}), and keys are securely managed throughout their lifecycle (BL-CRY-05).
{{#if FLAG_ELEVATED_PROTECTION}}
<!-- IMPL 5.1.1-elev -->
Where the protection need is high, requirements for key sovereignty (in particular in the case of external processing) are determined and met.
{{/if}}
### 3.2 Protection during information transmission
<!-- FW:REF-START ORIG:(ISA 5.1.2) -->
*Requirement reference:* {{#if FLAG_FW_TISAX}}VDA ISA 5.1.2{{/if}}{{#if FLAG_FW_ISO27001}}{{#if FLAG_FW_TISAX}} · {{/if}}ISO/IEC 27001 A.5.14{{/if}}
<!-- FW:REF-END -->
**Requirement**
<!-- FW:TISAX-REQ-START -->
{{#if FLAG_FW_TISAX}}
{{#if FLAG_FW_ISO27001}}*Requirements per VDA ISA 2027:*{{/if}}
<!-- REQ 5.1.2-M1 -->
- **[MUST]** The network services used for transmitting information are identified and documented.
<!-- REQ 5.1.2-M2 -->
- **[MUST]** Policies and procedures in line with the classification requirements for the use of network services are defined and implemented.
<!-- REQ 5.1.2-M3 -->
- **[MUST]** Measures to protect transmitted content against unauthorised access are implemented.
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 5.1.2-S1 -->
- **[SHOULD]** Measures to ensure correct addressing and correct transmission of information are implemented.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 5.1.2-S2 -->
- **[SHOULD]** Electronic data exchange takes place using content or transport encryption in line with the respective classification.
{{/if}}
{{#if FLAG_INCLUDE_SHOULD}}
<!-- REQ 5.1.2-S3 -->
- **[SHOULD]** Remote access connections to the organisation's network have appropriate security features; the relevant aspects are taken into account.
{{/if}}
{{#if FLAG_HIGH_PROTECTION}}
<!-- REQ 5.1.2-H1 -->
- **[HIGH]** Information is transmitted encrypted (at least transport encryption) or protected by equivalently effective measures. (C)
{{/if}}
{{#if FLAG_VERY_HIGH_PROTECTION}}
<!-- REQ 5.1.2-V1 -->
- **[VERY HIGH]** Information is transmitted with content encryption. (C)
{{/if}}
{{/if}}
<!-- FW:TISAX-REQ-END -->
<!-- FW:ISO-REQ-START -->
{{#if FLAG_FW_ISO27001}}
{{#if FLAG_FW_TISAX}}*Requirements per ISO/IEC 27001:*{{/if}}
<!-- REQ A.5.14-1 -->
- **[ISO A.5.14]** Rules, procedures and agreements for the secure transfer of information are established for all transfer channels in use.
{{/if}}
<!-- FW:ISO-REQ-END -->
**Implementation at {{ORG_NAME}}**
<!-- IMPL 5.1.2 -->
The network services used are identified and documented in the network/network services register ({{LINK:REG-NET}}); policies/procedures in line with the classification are implemented. Information is protected during transmission in accordance with the protection need (at least {{TLS_MIN}}, BL-CRY-01), correct addressing is ensured and remote access is safeguarded; rules for email/file encryption are defined (BL-CRY-04, cryptography/key management see {{LINK:VA-07}}).
{{#if FLAG_ELEVATED_PROTECTION}}
<!-- IMPL 5.1.2-elev -->
Where the protection need is high, information is transmitted at least transport-encrypted or protected equivalently; {{#if FLAG_VERY_HIGH_PROTECTION}}where the protection need is very high, content encryption is applied.{{/if}}
{{/if}}
## 4. Binding nature
This policy is binding for all affected roles within the scope. Compliance is monitored by {{ROLE_IT_LEAD}}.
## 5. Roles and responsibilities
| Role | Responsibility in this policy |
|-------|-------------------------------------|
| {{ROLE_IT_LEAD}} | Procedures/keys |
| {{ROLE_ISB}} | Permissible algorithms |
## 6. Review and update
This policy is reviewed at least {{REVIEW_CYCLE}} and on an ad-hoc basis by {{ROLE_IT_LEAD}} and approved by {{ROLE_MANAGEMENT}}.
## 7. Evidence
The evidence is not maintained in this document but centrally in the evidence register ({{LINK:NACHWEISREGISTER}}) and in the associated entries of the ISMS tool ({{TOOL_NAME}}).
## 8. Related documents
- Associated procedures: {{LINK:VA-07}}
- Technical security baseline: {{LINK:BASELINE}}
- ISA mapping matrix: {{LINK:ISA_MAPPING}}
- Evidence register: {{LINK:NACHWEISREGISTER}}
- Further: {{LINK:R08}}, {{LINK:R10}}, {{LINK:R12}}
<!-- Anforderungen 1:1 aus VDA ISA 2027; Mapping (REQ/IMPL) in mapping.json ueber Hidden-Anker. Im Lesemodus nicht sichtbar. -->

Some files were not shown because too many files have changed in this diff Show More