Files
craftvia/docs/STAND-dev-branch.md
msolarczekandClaude Opus 5 c8e6f30a27
CI / build-and-check (push) Canceled after 0s
CI / audit (push) Canceled after 0s
CI / sbom (push) Canceled after 0s
Basis: Certvia dev@a48c5fb als Fundament für Craftvia
Unveränderter Stand von certvia/dev (a48c5fb) plus Craftvia-Spezifikation
und Brandbook unter docs/craftvia/. ISMS-Module werden im Folgecommit entfernt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 11:05:39 +02:00

405 lines
74 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Entwicklungsstand `dev` — Konsolidierte Übergabe (PM + neue Entwickler)
> Stand: 2026-07-30 · Branch **`dev`** · **Sync-Hinweis:** lokal **deutlich vor `origin/dev`** (`origin/dev` = `9b479ab`); enthält u. a. Prod-Deployment-Vorbereitung, Wizard-Kickoff + Story A1-1 und diese Doku. `dev` ist auf **`origin` (git.certvia.de)** und **`local-gitea` (intern)** gepusht. Vor dem nächsten Push zuerst `git fetch` und prüfen, ob niemand weitere Commits hat. **Alle Feature-Branches** (Dev A: A1–A8; Dev B: F1–F4, B1–B7 inkl. Prüfziel-Follow-up) sowie **Certvia-Branding**, der **Wizard-Parallelisierungs-Fix** und zuletzt das **Security-Härtungspaket P1/P2/P3 (F-01…F-20, u. a. RLS scharf F-04, moduleGuard-DB-Authz F-06, MFA-Replay, Container-/Supply-Chain-Härtung F-11/F-18)** sowie **SEC1 (Mailversand/Queue)**, **SEC2 (Auth-Self-Service: Passwort-Reset/-Wechsel/E-Mail-Änderung, Session-Invalidierung, Rate-Limit)** und zuletzt die **TISAX-Umstrukturierung (M0–M4: Fundament, Strukturanalyse, Cockpit mit RACI/Evidence, Audit-Wizard, TISAX-Tasks, Wizard-Neustruktur; v3: Prozesshaus + geführtes BIA je Prozess)** sowie **SEC3/SEC4 (MFA pro Mandant erzwingen, WebAuthn/Passkeys, TOTP-Verschlüsselung at rest, Plattform-Admin-Verwaltung)** sind per DevOps-Integration additiv nach `dev` konsolidiert (Gate grün); noch offene Feature-Branches rebasen auf `dev`. **Deployment-Pflichtschritte** aus dem Security-Paket siehe §1a (F-04 RLS scharfschalten, F-06×F-10 `scripts/sync-role-permissions.ts` nach `migrate`). **SEC1/SEC2 fürs Deployment:** eigener **Mail-Worker-Service** (`npm run worker:mail` = `scripts/mail-worker.ts`) nötig, echte **SMTP-Variablen** setzen, und die Queue nutzt **Redis** (`REDIS_URL` mit Passwort, BullMQ/ioredis) — der Worker fehlt noch in `docker-compose.coolify.yml`. · Basis-Doku: `docs/HANDOVER-DEV.md`, `docs/HANDOVER-PM.md`, `docs/SPEC.md`
> Zweck: Ein Dokument, das den kompletten Stand der Weiterentwicklung auf `dev` zusammenfasst — fachlich (für PM) und technisch (für einen neuen Entwickler). `dev` ist noch **nicht** nach `main` gemergt.
> **Neu (2026-08-07):** `feature/settings-roles-platform-ui` integriert (Einstellungen umstrukturiert: Risikokriterien/Rollen als Menü-Karten + Link zur Plattform-Administration; neue Route `/settings/risk-criteria`). Zusätzlich **Audit-Trail einsehbar je Mandant**: wiederverwendbares server-gerendertes Popup (`src/components/audit-trail.tsx`, Muster wie die übrigen Popups über `?audit=1`) an **zwei** Stellen — Mandanten-Einstellungen `/settings` (eigener Mandant, TENANT_MODELS/RLS-gefiltert) und Plattform-Konsole `/admin/[id]` (Superadmin, cross-tenant über Owner-Client auf den Mandanten gefiltert). **Keine neue Migration.** Gate grün (tsc/lint/build + 17/17 Tests), Popup im Browser verifiziert.
>
> **Fix (2026-08-07):** `dev-fix-platform-admin` integriert — **Proxy-Gate für Plattform-Routen** (`src/proxy.ts`): `/platform/login` + `/api/platform-auth` sind jetzt public, und `/admin`, `/admins`, `/profile`, `/platform` gaten aufs Plattform-Session-Cookie und leiten anonyme Besucher auf **`/platform/login`** statt fälschlich auf die Mandanten-Login-Maske. Plus klarerer Modul-Toggle in `/admin/[id]` (Status-Pill + eigener „Aktivieren/Deaktivieren"-Button). Verifiziert: `/admin|/admins|/profile` → 307 `/platform/login`, `/dashboard` weiterhin → `/login`. Gate grün (17/17).
>
> **Fix (2026-08-07, 2):** Plattform-Auth bekommt **eigene Cookie-Namen für ALLE** Auth.js-Cookies, nicht nur `sessionToken` (`src/server/platform-auth.ts`): auch `csrfToken` (`__Host-platform-authjs.csrf-token`) und `callbackUrl` (`platform-authjs.callback-url`). Grund: zwei Auth.js-Instanzen auf derselben Domain teilten sich die Default-Cookies `authjs.csrf-token`/`authjs.callback-url`; da ein Nutzer zugleich Mandanten- und Plattform-Konto sein kann, überschrieb die zuletzt aktive Instanz das gemeinsame CSRF-Cookie → CSRF-Validierung der Plattform-Instanz schlug fehl (Sign-out warf, CSRF-POSTs/Session-Erneuerung landeten auf der Login-Maske). Getrennte Cookie-Namen isolieren beide Auth-Domänen vollständig. Gate grün (17/17).
>
> **Fix (2026-08-07, 3):** Plattform-Cookie-`secure`/Prefix folgt jetzt dem **AUTH_URL-Protokoll** (`https:`) statt `NODE_ENV` (`src/server/platform-auth.ts`, `PLATFORM_SECURE_COOKIES`), exakt wie Auth.js es für die Mandanten-Instanz tut (`useSecureCookies ?? url.protocol === "https:"`). Grund: im Container ist `NODE_ENV=production`, der interne Testserver wird aber ggf. über **http** bedient (AUTH_URL=http bzw. fehlendes `X-Forwarded-Proto`); ein hart auf `__Secure-`/`__Host-`/`secure` gesetztes Cookie wird vom Browser über http verworfen → Plattform-Session bleibt leer (jede Aktion → Login-Maske, Logout wirft), während die Mandanten-Session weiterläuft. **Betriebshinweis:** In Prod `AUTH_URL=https://…` setzen (dann greifen wieder secure-Cookies + `__Host-`/`__Secure-`-Prefixe). Gate grün (17/17).
>
> **Identity/Mandanten (Option C), WS0 (Fundament) — 2026-08-11:** globales `Identity`-Modell (kein `tenant_id`, **nicht** in `TENANT_MODELS`, keine RLS) + `User.identityId`; Migration `identity_foundation`; Reseed auf Identity+Membership inkl. 2. Mandant `demo2` + Multi-Membership-Fixture `multi@demo.example`. **Expand/Contract** (User-Auth-Felder bleiben vorerst Legacy; Contract-Migration droppt sie am Ende von WS1–WS4). Neuer Test `scripts/test-identity-schema.ts`. Grundlage/Fahrplan: `docs/FEINDESIGN-identity-mandanten.md`, `docs/KONZEPT-identity-mandanten.md`, `docs/UEBERGABE-identity-mandanten.md`, `docs/PROMPT-uebergabe-identity-mandanten.md`. Nächste Schritte: WS1 (Login gegen Identity, Two-Step + MFA-pending) → WS2 (`/select-tenant`), parallel WS3/WS4/WS6; Abschluss = Contract-Migration.
>
> **Identity/Mandanten (Option C), WS1/WS3/WS4/WS6 — 2026-08-11** (`feature/identity-auth-core` → `dev`, `--no-ff`, Gate grün: tsc/lint/build + **21/21** Tests; **noch nicht auf die Remotes gepusht**):
> - **WS1 Auth-Kern:** `auth.ts` authentifiziert gegen die globale `Identity` (Passwort/MFA/Lockout an der Identity); Mitgliedschaften werden geladen, der aktive Mandant per Organisations-Slug oder Single-Membership gewählt (mehrere **ohne** Slug ⇒ noch kein Login → `/select-tenant` = WS2). Login-Kern als `authorizeTenantCredentials()` exportiert. Session/JWT (`next-auth.d.ts`) neu: `identityId`/`activeMembershipId`/`memberships[]` (optional, da Plattform-Auth dieselben Typen nutzt); `tenantId` = aktiver Mandant → **356 dbForTenant-Call-Sites unverändert**. Test `scripts/test-identity-login.ts`.
> - **WS4 Passwort/MFA an Identity:** Passwortwechsel, MFA-Enroll/-Disable, Recovery-Codes und der **Session-Kill-Switch** (`sessionsValidAfter`) gehören der Identity (`account.ts`, `account/page.tsx`, `sessions.ts`). Guards (`action-guard.ts`, `(app)/layout.tsx`, `change-password`, `enroll-mfa`) lesen `mustChangePassword`/`sessionsValidAfter`/`mfaEnrolledAt` + globalen `identity.status` aus der Identity; Membership-Status/Rechte weiter aus `User`. Reset-Kette (`auth-recovery`/`auth-selfservice`/`auth-token`) auf `PrincipalType "identity"`. **Mandanten-Admin-Passwort-Reset entfernt** (goldene Regel 5). **E-Mail-Änderung für Mandanten-Konten deaktiviert = Phase 2.** Test `scripts/test-identity-account.ts`. **Abgetrennt (offen): WS4b WebAuthn→Identity** (Schema-Migration + `TENANT_MODELS` + `webauthn.ts`; Passkey-Login läuft bis dahin mandantengebunden).
> - **WS3 Einladungs-Lifecycle:** Nutzeranlage **nur per Einladung** (goldene Regel 4) — `TokenType "invitation"` (7 Tage) + neue öffentliche Seite `/invite` + `redeemInvitation`. `createTenantUser`/`createUser`/`inviteFunctionHolder` ohne pwMode „set"; **bekannte Identity → nur Mitgliedschaft ergänzen** (kein Passwort-Reset), unbekannt → Identity + Einladung; **neutrale Rückmeldung** (kein Cross-Tenant-Leak). `user-forms.tsx` = reines Einladungsformular. Test `scripts/test-invitation.ts`.
> - **WS6 Seed/Provision/Bootstrap:** bereits durch WS0 abgedeckt (`provisionTenant`/`seed.ts` Identity-fähig, `sync-role-permissions.ts` orthogonal) — kein Netto-neuer Code.
> - **Keine neue Migration** (WS1/WS3/WS4/WS6 sind code-only; das WS0-Schema trägt).
>
> **Identity/Mandanten (Option C) — WS2/WS4b/WS5 + Contract, UMBAU KOMPLETT — 2026-08-11** (`feature/identity-tenant-context` → `dev`, `--no-ff`, Gate grün: tsc/lint/build + **23/23**; **noch nicht gepusht**):
> - **WS2 Mandantenkontext:** Multi-Membership-Login ohne Organisations-Slug ergibt eine Session OHNE aktiven Mandanten → `(app)/layout` leitet auf **`/select-tenant`**. Wechsel server-autoritativ über **`setActiveTenant`** (`actions/tenant-switch.ts`, `unstable_update` + async jwt-`update`-Trigger → `resolveActiveMembership`: Rechte je Wechsel NEU aufgelöst). **Sidebar-`TenantSwitcher`** (nur bei >1 Mitgliedschaft). **Browser-verifiziert:** Wechsel demo↔demo2 inkl. Mandantenisolation (demo: 9 Assets, demo2: 0). Test `scripts/test-tenant-switch.ts`.
> - **WS4b WebAuthn→Identity:** Passkeys sind identitätsgebunden (Migration `20260811140000_webauthn_identity`: `webauthn_credentials` verliert `tenant_id`/RLS, verweist auf `identities`); `WebAuthnCredential` **raus aus `TENANT_MODELS`**. Passkey-Login/-Verwaltung über `identityId`.
> - **Contract-Migration** (`20260811150000_contract_user_auth_columns`): die 10 ungenutzten `User`-Auth-Spalten entfernt (`password_hash, must_change_password, failed_logins, locked_until, mfa_secret, mfa_enrolled_at, recovery_codes, last_totp_step, sessions_valid_after, is_platform_admin`) → **`User` = reine Mitgliedschaft** (`tenantId, identityId, email, name, status`). Expand/Contract abgeschlossen. DROP COLUMN → kein Reset.
> - **WS5 Two-Step-Login (Entscheidung A):** `/login` Schritt 1 (E-Mail+Passwort) → bei aktiver MFA kurzlebiger, signierter, einzweckiger `mfa_pending`-Cookie (KEINE Session) → **`/login/mfa`** Schritt 2 → `login-ticket`-Provider prägt die Session. Bausteine `verifyIdentityPassword`/`verifyIdentityMfa`/`finalizeIdentityLogin` (kein Passwort-Orakel, Lockout wie beim vollen Login). `src/server/login-ticket.ts` (HMAC). Runtime-verifiziert. Test `scripts/test-two-step-login.ts`.
> - **Damit ist der Umbau „Zentrale Identität mit Mandanten-Mitgliedschaften" vollständig** (alle 5 goldenen Regeln + Entscheidung A). Zwei neue Migrationen. **Offen: nur der Push auf beide Remotes** (origin + local-gitea) — bewusst zurückgehalten. Phase-2 (per-Mandant Step-up, E-Mail-Änderung als Identity-Op, PlatformAdmin-Konsolidierung) bleibt ausgeklammert.
>
> **Richtlinien-Vorlagen Plattform-Editor + EN-Paket — 2026-08-11:** `dev-fix-platform-admin` integriert — globale Vorlagen-Modelle + Migration `policy_templates`, Plattform-Editor (Entwurf/Veröffentlichen, Editoren für Anforderungen/Variablen), **Sprachwahl je Mandant** (`setTenantLocale`) und das **komplette EN-Übersetzungspaket** der Richtlinien-Vorlagen (Leitlinie, R01–R14, VA-01–VA-20, D01/P01, Baseline/Nachweisregister). Mandanten-Import liest die veröffentlichte DB-Version.
>
> **Härtung Argon2id — 2026-08-11:** Passwort-Hash-Parameter fixiert/dokumentiert (`src/server/password.ts`, `ARGON2_OPTIONS`: m=19456 KiB, t=2, p=1, outputLen=32; Argon2id = Lib-Default). Salt automatisch pro Hash (PHC-String), **kein** Pepper (dokumentiert). Nur neue Hashes betroffen.
>
> **Härtung Phase 2–3 (Ops-Doku) — 2026-08-11** (Lane `lane-haertung-ops`, **nur Markdown, kein App-Code/Migration, noch nicht nach `dev` gemergt**): **Phase 1 (Passwort-Pepper) ist erledigt** (Argon2-`secret`, zentral, in `dev`) — unverändert. Phasen 2–3 liegen jetzt als **Ops-Runbook** vor: `docs/DEPLOY-PROD-CONTABO.md` um **Host-Encryption at-rest** (LUKS/dm-crypt fürs Daten-Volume `pgdata`+MinIO, Boot-Unlock-Verfahren A/B, Passphrase im Passwortmanager) und **verschlüsselte Backups** (pgBackRest `aes-256-cbc` + `age` je Umgebung + restic für MinIO, Coolify-Cron/Retention/Restore-Test, Bezug zur App-Backup-Engine `BACKUP_ENC_KEY`/§9) ergänzt; neues **`docs/SECRETS-REGISTER.md`** (Ownership + Rotationsregel je Umgebung für `AUTH_SECRET`/`MFA_ENC_KEY`/`PASSWORD_PEPPER`/`BACKUP_ENC_KEY`/pgBackRest-Key/`age`-Keypair; `PASSWORD_PEPPER`+`MFA_ENC_KEY` als **nicht rotierbar** = Reset markiert). **Restore-Kohärenz-Regel** prominent in beiden Docs: Umgebungs-Secrets stehen nicht im Artefakt → Restore in fremde Umgebung bricht Passwort-/MFA-Prüfung bzw. Entschlüsselung. **DB-TLS (`sslmode`) + Vault/KMS bleiben Phase 2** (offen). Gate: nur `.md`, tsc/lint unberührt.
>
> **Merge-Zyklus 2026-08-11:** WS0 + Richtlinien/EN-Lane + Argon2 zusammen nach `dev` konsolidiert. Konflikt nur in `schema.prisma` (User-Relationsblock → Union mit `identity`) + `provision.ts` (auto-merge: Identity-Upsert **und** Policy-Variablen). DB neu gebaut (`migrate reset`, **52 Migrationen**, beide neuen koexistieren) + Seed. Gate grün: tsc/lint/build + **18/18 Tests**.
>
> **Konfigurierbarer Backup-Zielspeicher — 2026-08-17** (`lane-backup-target` → `dev`, `--no-ff`): Backup-/DSGVO-Zielspeicher jetzt **im Betreiber-Portal wählbar** (`/admin/backup`, Full-Admin + MFA-Step-up) — **Lokal** (persistentes Volume) oder **S3/MinIO**. `PlatformSetting` erweitert (`backupTarget`, `backupLocalDir`, `backupS3*`, `backupS3SecretKeyEnc` **verschlüsselt at-rest**), Migration `backup_target_config` (additiv). Statisches `backupStore` → **`getBackupStore()`** mit Präzedenz **DB → Env (`S3_*`/`BACKUP_LOCAL_DIR`) → lokaler Default `.backups`**, fail-secure bei unvollständiger S3-Config; alle Call-Sites umgestellt. **Persistentes `backups`-Volume** (app + backup-worker, `/app/.backups`) — Lokal überlebt Redeploys. Neuer Test `scripts/test-backup-target.ts`. Gate grün (tsc/lint/build + **28/28**), `/admin/backup` im Browser klickgeprüft (Lokal↔S3, Secret-Feld „nie Klartext"). Konzept/Prompt: `docs/KONZEPT-backup-target.md`, `docs/PROMPT-lane-backup-target.md`.
>
> **Modul „Vorfälle" (Incident-Management) IM-A — 2026-08-17** (`lane-incidents-a` → `dev`, vom Team): Fundament + Kern-Lifecycle/UI (Migration `incidents`, `src/app/(app)/incidents/*`, `src/server/actions/incidents.ts`, `docs/KONZEPT-incidents.md`, Test `scripts/test-incidents.ts`). IM-B (Meldefristen/Notifications) noch **in Arbeit** (eigener Branch `lane-incidents-b`).
>
> **Direkt-Download der Sicherung + S3-Bucket-Selbstheilung — 2026-08-17** (`lane-backup-download` @ `fe13a84` → `dev`, `--no-ff`): Neue Route `/api/platform/backup/download` (Full-Admin) — `exportTenant(persist:false)` streamt die verschlüsselte `.cvb` **inline in den Browser**, **ohne Worker/Redis/S3** („Sicherung herunterladen"-Button im Export-Popup). `S3BackupStore.ensureBucket()` legt einen fehlenden MinIO-Bucket beim ersten `put` **automatisch** an (behebt „The specified bucket does not exist"). Neuer Test `scripts/test-backup-download.ts`. Gate grün (tsc/lint/build + **30/30**). Merge über isoliertes Worktree (paralleler Team-Arbeitsbaum unberührt).
>
> **Modul „Vorfälle" IM-B/C/D (komplett) + Backup-Docker-Fix — 2026-08-18** (vom Team nach `dev` gemergt; von mir validiert + gepusht): **IM-B** (Meldefristen/Timer + Meldepflicht + Benachrichtigungen), **IM-C** (Verknüpfungen, Abschluss/Lessons-Learned, Export/Meldevorlagen), **IM-D** (E-Mail-to-Ticket Inbound + Provisionierung; Migration `incident_inbound_d`, Test `scripts/test-incident-inbound.ts`). Damit ist das Modul „Vorfälle" funktional komplett (IM-A…IM-D). **Docker-Fix:** `prisma/schema.prisma` wird jetzt auch in die `runner`-Stage kopiert — der Inline-Backup-Download läuft in der App (standalone), und die Backup-Topologie liest die Schema-Datei zur Laufzeit (Prisma 7 entfernt FK-Relationen aus dem Laufzeit-DMMF); sonst `ENOENT` → „Export fehlgeschlagen". Gate grün (tsc/lint/build + **33/33**).
---
## 1. Executive Summary
Auf `main` lag das Fundament (Auth/RBAC/Mandanten-Isolation, Assets/BIA, Risiko, Maßnahmen, Abhängigkeiten, Lieferanten, Richtlinien Phase 1/2a, Admin-Konsole Phase 1). Der Branch **`dev`** ergänzt fünf große Blöcke — alle mit `tsc`+`lint`+`build`, Modul-Guard-Check und (wo relevant) Browser-Verifikation abgeschlossen:
1. **Produktionshärtung Phase 1** — API-seitige Modul-Durchsetzung, getrennter Superadmin-Store mit eigenem Login + MFA, nicht-destruktiver Richtlinien-Re-Import.
2. **Benutzer- & Rollenverwaltung** (ohne E-Mail-Flow) — Plattform-Admin verwaltet Nutzer je Mandant; Mandanten-Admin verwaltet Nutzer **und** Rollen intern; Force-Change-Passwort, Passwort-Policy, optionale MFA je Nutzer; Popup-UI wie bei Assets.
3. **Freigabe-Workflow + Aufgaben-Modul** — Richtlinien-Freigabe an eine konkrete Person, Bearbeitung im neuen Modul „Aufgaben", Dashboard-Kachel; plus zentralisierte Richtlinien-Governance (zentrale Variablen, Schutzbedarf, Coverage-Filter nach Assessment-Level).
4. **GAP-Report-Umsetzung (Richtlinien-Vorlagenpaket)** — WP1–WP4 + E2: sechs neue Verfahrensanweisungen (ISB-Volltexte), Inline-VA-Verlinkung, Rendering-Fix, generisches editierbares Register-Datenmodell.
5. **Register-Konsolidierung in Fachmodule** — vier generische Register in die Fachmodule überführt: **Software** und **Projekte** als eigene Asset-Typen (Popups wie bei Assets/Lieferanten), **kritische IT-Dienste** als schreibgeschützte Auto-Sicht aus BIA, REG-NET auf Referenzfeld reduziert; Richtlinien-Links zeigen jetzt auf die Module statt auf Register.
6. **Produktions-Deployment-Vorbereitung** (Commits `cc8d486`, `f368d8f`) — Prod-Bootstrap `scripts/bootstrap-admin.ts` (legt Erst-Superadmin im `platformAdmin`-Store + Mandant/Mandanten-Admin idempotent an, ersetzt in Prod den Demo-Seed; per `BOOTSTRAP_*`-Env im `migrate`-Job der `docker-compose.coolify.yml`); `.env.prod.example`; **Fonts selbst gehostet** (`next/font/local`, committete woff2 in `src/app/fonts` → reproduzierbarer Offline-Build, DSGVO); Runbook **`docs/DEPLOY-PROD-CONTABO.md`** (VPS/Coolify/Gitea, TLS `app.certvia.de`, Bootstrap, Backups, Go-Live-Checkliste).
7. **Branding-Umstellung auf Certvia** — **in `dev` integriert (Gate grün)**: getrennte Token-Ebenen `--brand-*` (Logo/Print/Export) vs. `--ui-*` (Produktpalette, inhaltlich unverändert) mit `src/lib/brand.ts` als JS-Pendant; Certvia-Logo als Inline-SVG-Komponente (`<CertviaLogo>`), Favicon-/PWA-Icons + `site.webmanifest`, Metadaten/OG/i18n/TOTP-Issuer, gebrandete Auth-Seiten sowie **neue 404-/500-Seiten**, Druck-/Dokument-CD (`@media print` + `src/lib/document-brand.ts`) als Andockpunkt für den offenen DOCX/PDF-Export, brandfähige E-Mail-Basis (`src/lib/email-brand.ts`), Mandanten-Branding-Default (`resolveTenantBranding`). **GEFIM bleibt Dachmarke** („Ein Produkt von GEFIM"). Reines Branding, keine Funktionsänderung — Ausnahme: der Route-Gate-Matcher in `src/proxy.ts` musste `.webmanifest` freigeben, sonst lieferte `/site.webmanifest` die Login-HTML. Details: **`docs/BRANDING-CERTVIA.md`**.
**In Arbeit (parallele Entwicklung, 2-Lane Dev A × Dev B):** **Onboarding-Wizard** (`docs/wizard-uebergabe/`). **Beide Lanes bis hierher in `dev` konsolidiert** (inkl. Dev-B **B4**, per Fast-Forward integriert)**:**
- **Dev A:** A1-1 — Wizard-Shell: Modul `onboarding`, Step-Registry (`registerStep`, Keys 1–9, `guard` kann Schritte ausblenden), resumierbarer Fortschritt (`OnboardingProgress`). **F2** — generalisiertes `ObjectReviewStatus` (5 Zustände inkl. `zurueckgewiesen`) als wiederverwendbares Review-Enum; RBAC-Recht `validate_objects` + klonbare Rolle `external_validator`; Wizard-State-Machine mit Rechte-Trennung: Bearbeiter (`onboarding:use`) advance/rework/reset, Validator (`validate_objects`) validieren/zurückweisen (mit Begründung). „Weiter"-Gate: nur `validiert` zählt. Migration `object_review_status`. **A1-2** — Dashboard-Kachel „Onboarding-Fortschritt" (Anteil validierter Schritte + nächster offener Schritt, verlinkt in den Wizard). **A2-1** — Assessment-Level (AL2/AL3) als **einzige** Quelle des Schutzbedarfs: `protectionFlags(level)` (`src/server/assessment-level.ts`) leitet `FLAG_HIGH_PROTECTION`/`FLAG_VERY_HIGH_PROTECTION` zentral ab (Wizard/Provisioning seeden daraus, **nicht** mehr aus dem Fragebogen); Schutzbedarf-Frage `Q-FEAT-01` + Regeln `feat01-*` entfernt. **A2-2** — Scope-Objekt `WizardScope` + Filter-Engine (`src/lib/scope-filter.ts`: `activeRequirements`/`scopeSummary`, client-safe): filtert die Anforderungen nach AL-Baseline, `FLAG_INCLUDE_SHOULD` und Prüfzielen; Grunddaten `seed/scoping/c1-scope.json` (412 Anforderungen, generiert via `scripts/build-c1-scope.ts`); Action `saveScope` (`moduleGuard('onboarding')`) am Wizard-Schritt „scoping"; Migration `wizard_scope`; Tests `scripts/test-scope-filter.ts` (grün). **A3-1** — Validierungs-Workflow generalisiert: wiederverwendbares generisches Objekt-Review (`src/server/object-review.ts`, `src/lib/object-review.ts`, `src/components/object-review.tsx`) über Objekttypen hinweg (u. a. Risiken), an Risk-/Task-Actions angebunden. **A4** — ISMS-Rollen als Wizard-Schritt 3 „roles" inkl. Funktionstrennung FT-01…06 (`src/lib/ft-rules.ts`, Tests `scripts/test-ft-rules.ts`), ISB-Bestellung (`src/lib/isb-bestellung.ts`) und Rollen-Logik (`src/server/roles.ts`). **A5-1** — Asset-Schritt (Wizard-Schritt 5) auf dem Bestandsmodul (`steps/assets/step.tsx`). **A7** — **Control-Assessment** (Wizard-Schritt 7): Reifegrad-Engine R0–R3 + Zielreifegrad (`src/lib/maturity.ts`, Tests `scripts/test-maturity.ts`), Modell `ControlAssessment` (Migration `control_assessments`, RLS, in `TENANT_MODELS`) mit Belegstatus/Reifegrad-Bestätigung/Gap-Aufgaben; SoA-Kontext (`src/server/soa-context.ts`, `actions/soa.ts`), Control-Grunddaten `seed/scoping/c5-controls.json`. **A6** — **Standard-Risikokatalog (C4)**: globaler Katalog `RiskCatalogEntry` (Migration `risk_catalog`, kein RLS) + Import/Übernahme (`prisma/import-risks.ts`, `/risks/catalog`); A6-2 Standardmaßnahme→Aufgabe und **Restrisiko-Akzeptanz** (Migration `risk_acceptance`: Felder `accepted_at/by/rationale` an `risks`, VA-09). **A8** — **Gap-Konsolidierung** (Wizard-Schritt 8): Aggregations-/Priorisierungs-Engine (`src/lib/gap-consolidation.ts`: Dedup, Priorisierung, Quick-Wins), Task-Abgleich + Kontext (`src/server/gap-context.ts`, `src/server/actions/gap.ts`), Tests `scripts/test-gap-consolidation.ts` (keine neue Migration/Modell).
- **Dev B:** F1 (Task-Objekt um Wizard-Felder `type/owner/dueDate/priority/status/resources/origin/links` erweitert, Migration `tasks_wizard_fields`), B1 (Auto-Generierung von Aufgaben-Vorschlägen aus Triggern, C2 §8; `src/lib/task-triggers.ts`, `src/lib/tasks.ts`), F4 (Wizard-Flags in `variables.schema.json`: `FLAG_PROTOTYPE_PROTECTION`, `FLAG_ISB_EXTERNAL`, `FLAG_ISB_INTERNAL`; `_verify.py` = OK). **F3+B2** — Regel-/Mapping-Engine: deklarative DSL (`src/lib/rules/dsl.ts`), Auswerter (`engine.ts`, `evaluateCondition`) und C2-§5-Regelwerk (`feature-rules.ts`: Feature-Flags, Schutzbedarf-Ableitung, Control-Scope, Risiko-/Aufgaben-Trigger); Tests `scripts/test-rules.ts` (18 Fälle, grün). **B3** — Fragebogen als Wizard-Schritt 2 „context": Frage-Katalog A–F (`src/lib/onboarding/questions.ts`), Fakten-Ableitung (`facts.ts`, `deriveContext` über die Regel-Engine), Schritt-Komponente + `saveFacts`-Action (`onboarding-facts.ts`), Modell `WizardFact` (Migration `wizard_facts`, RLS, in `TENANT_MODELS`); echte Schritte via Side-Effect-Import `register-steps.ts` in die Registry eingeklinkt (überschreibt Platzhalter). Der Fragebogen schreibt **nur** Fakten/Flags, nie die gesperrten Zentralvariablen. **B4** (Richtlinien-Import/Upload) — **in `dev` integriert (Fast-Forward), Gate grün**: **B4-1** nicht-destruktiver Vorlagen-Import bei Aktivierung des Richtlinien-Moduls + Self-Service-Button (`/policies`, Report-Banner) und Admin-Button (`src/server/actions/policy-package.ts`, Trigger in `toggleTenantModule`); **B4-2** „Eigene Richtlinie hochladen" (`/policies/upload`, `policy-upload.ts`) mit Pflicht-Control-Zuordnung + gekapseltem Storage-Adapter-Stub (`src/server/storage/adapter.ts`, echtes Backend = Epic S1) → EIGENES-Dokument (`EIG-*`) + PolicyRequirement-Zeilen (Coverage/Nachweislage); **B4-3** neue Vorlagen `P01_Prototypenschutz` + `VA-20` (Kap. 8.x) und `D01_Datenschutz` (9.x) + 5 `mapping.json`-Anforderungen (condition-gated per Prüfziel-Flag), `_verify.py` = OK. **Keine neue Migration** (nur String-Status/Seed-Content). **B5** (Umsetzungshinweise C6) — **in `dev` integriert, Gate grün**: **B5-1** Datenmodell `ImplementationHint` (Migration `implementation_hints`; **globaler C6-Katalog**, identisch je Mandant → kein `tenantId`/RLS) + C6-Import (~397 Hinweis-Blöcke via `prisma/import-hints.ts` / `scripts/import-c6-hints.ts`); **B5-2** kontextsensitives Umsetzungshinweis-Panel (`/policies/hints`, `src/server/actions/hints.ts`), aus `/policies` verlinkt. **Prüfziele Single Source** (Follow-up zu A2): der context-Step seedet die Prüfziele aus `WizardScope` statt aus einer zweiten Quelle (Doppelquelle behoben; `feature-rules.ts`/`facts.ts`/`questions.ts`, `test-rules.ts` angepasst). **B6** — Vorlagenpaket-**Versionierung** + kontrollierte **Diff-Übernahme**: Modell `PolicyPackageState` (Migration `policy_package_state`, RLS, in `TENANT_MODELS`) hält je Mandant Paket-Version/-Stand; `stampPackageState` (in `prisma/import-policies.ts`) beim Import; Updates-Ansicht `/policies/updates` zeigt/übernimmt Änderungen kontrolliert. **B7-1** — Assessment-**Readiness-Dashboard** als Wizard-Schritt 9 „readiness" (`src/lib/readiness.ts`, Tests `scripts/test-readiness.ts`) mit Interpretationstexten (C9 §1/§2). **B7-2** — **VDA-ISA-Katalog-Export** (CSV) + **Management-Zusammenfassung** (C9 §3/§4): Export-Route `onboarding/export/route.ts`, Export-Engine `src/lib/export/vda-isa.ts` + `src/server/export-context.ts`, Zusammenfassungs-Seite `onboarding/summary`, Tests `scripts/test-vda-isa.ts`. Damit ist **B7 vollständig** (keine neue Migration). **Wizard-Schritte 4 + 6 (echte Komponenten)** — Richtlinien-Schritt „policies" (Schritt 4, Guard: nur bei aktivem Richtlinien-Modul) und Risiken-Schritt „risks" (Schritt 6, Guard: nur bei aktivem Risiko-Modul) zeigen jetzt den echten Modul-Status statt Platzhalter (`steps/policies/step.tsx`, `steps/risks/step.tsx`, `src/server/actions/onboarding-steps.ts`) — keine Doppel-Datenhaltung. Damit sind **alle Wizard-Schritte 1–9 echte Komponenten**.
- **Offen:** **Keine offenen Feature-Branches mehr** — alle bestehenden Lanes in `dev` konsolidiert (Dev A: A1–A8; Dev B: F1/B1/F4/F3+B2/B3/B4/B5/B6/B7 + Prüfziel-Follow-up). Nächste geplante Stories siehe §6.
**Migrationen (31 neu, laufen beim Deploy automatisch über den Init-Container):**
`platform_admins` · `policy_lifecycle_archived` · `user_mgmt_and_platform_settings` · `tasks` · `strip_tool_variable_articles` · `managed_registers` · `software_and_project_assets` · `onboarding_progress` · `tasks_wizard_fields` · `object_review_status` · `wizard_facts` · `wizard_scope` · `implementation_hints` · `policy_package_state` · `control_assessments` · `risk_catalog` · `risk_acceptance` · `task_description` · `control_implementations` · `login_lockout` · `mail_fundament` (SEC1) · `auth_tokens_sessions` (SEC2) · `tisax_m0_foundation` · `tisax_m1_m4_consolidated` · `tisax_rekey_onboarding_steps` · `tisax_v3_process_fields` · `tisax_v4_catalog_parent` · `tisax_v5_audit` · `tisax_v7_policy_domain` · `webauthn_credentials` (SEC3) · `platform_admin_role` (SEC4)
---
## 1a. Sicherheitspaket P1/P2 (Branch `dev-security-p1`, Stand 2026-07-30)
Nach einem externen Secure-Code-Review (AEGIS-SAST, 21 Findings) wurde ein Härtungspaket umgesetzt: **alle P1 (2) und P2 (5) sowie mehrere P3** sind behoben. Das Paket liegt auf **`dev-security-p1`** (von `dev` abgezweigt, Gate grün, noch **nicht** nach `dev` gemergt). Parallelisiert in drei konfliktfreien Lanes plus Vorlauf, danach additiv integriert.
| Finding | Priorität | Behebung | Kern-Dateien |
|---|---|---|---|
| **F-01** Auth.js „fail open" + Next.js-CVEs | P1 | `next-auth`→`5.0.0-beta.32` (`@auth/core` 0.41.3), `next`→`16.2.12`; Guards **positiv** statt existenzbasiert; **Fail-Secure-Startprüfung** `assertSecureEnv()` (lazy/memoisiert, build-safe) | `package.json`, `src/server/env.ts`, `auth.ts`, `platform-auth.ts` |
| **F-02** Cross-Tenant-Leck bei `findUnique`+`select` | P1 | Tenant-Guard **fail-closed**: skalare `where`→`findFirst` mit `tenantId`-Vorfilter (verhindert statt erkennt), Compound-Unique→`tenantId`-Injektion + harter Abbruch; drei Aufrufstellen zusätzlich explizit gefiltert; Regressionstest | `src/server/db.ts`, `actions/risks.ts`, `actions/risk-catalog.ts`, `scripts/test-tenant-isolation.ts` |
| **F-03** Stored XSS im Richtlinien-Rendering | P2 | `renderPolicyHtml` sanitisiert `marked`-Output gegen strikte Allowlist (`sanitize-html`); Variablenwerte + Upload-Titel HTML-kodiert vor `noEscape`-Handlebars | `src/lib/policy-render.ts`, `actions/policy-upload.ts` |
| **F-05** Kein Brute-Force-Schutz Mandanten-Login | P2 | Kontosperre (5/15 min) + `denied`-Audit analog Plattform-Login; Enumeration via Dummy-Hash konstanter Laufzeit; DoS-Ausnahme letzter aktiver Admin | `auth.ts`, Migration `login_lockout` (`User.failedLogins/lockedUntil`) |
| **F-07** Keine Security-Header/CSP | P2 | CSP + HSTS + `nosniff` + `X-Frame-Options: DENY` + Referrer-/Permissions-Policy; `unsafe-eval`/`ws:` nur im Dev | `next.config.ts` |
| **F-08** Kritische Kontoänderung ohne Re-Auth | P2/P3 | Passwortwechsel verlangt aktuelles Passwort (+ TOTP bei aktiver MFA); MFA-Deaktivierung erfordert TOTP-Code; Ausnahme nur beim erzwungenen Erstwechsel | `actions/account.ts`, `actions/platform.ts` |
| **F-09** 30-Tage-Sessions | P3 | `maxAge` 8 h (Mandant) / 2 h (Plattform) + `updateAge` | `auth.ts`, `platform-auth.ts` |
| **F-12** Weitere verwundbare Abhängigkeiten | P3 | `overrides` für `postcss`/`sharp`/`valibot`; **Produktionsbaum (`--omit=dev`): 0 kritisch / 0 hoch** (vorher 2/5) | `package.json` |
| **F-15** Upload ohne Validierung | P3 | Größenlimit vor RAM-Read, Endungs-/MIME-Allowlist, Magic-Byte-Prüfung, kanonischer MIME statt `f.type` | `actions/policy-upload.ts` |
| **F-16** (Teil) Isolationsverletzung unsichtbar | P3 | Verletzungen als `[SECURITY]`-Log; zentrale Audit-Anbindung (`action:"denied"`) als Folge-TODO offen (Importzyklus `audit.ts`↔`db.ts`) | `src/server/db.ts` |
| **F-19** Unvalidiertes `callbackUrl` | P4 | Allowlist (nur eigene absolute Pfade), doppelt validiert | `src/app/login/page.tsx` |
**Korrekturen am Bericht** (dessen Codevorschläge waren an drei Stellen falsch): `npm install next-auth@latest` hätte auf **v4** downgegradet (Major-Bruch) — korrekt ist `5.0.0-beta.32`. Der pauschale `findUnique`→`findFirst`-Umbau bricht an den Compound-Unique-Keys (`tenantId_key` etc.) → stattdessen Hybrid-Guard. Das RLS-Snippet aus F-04 funktioniert nicht (Kontext in falscher Transaktion) → F-04 bewusst herausgelöst.
| **F-06** JWT-Sessions ohne Widerruf | P2 | ✅ **Branch `dev-security-f06-session-auth`** (auf `dev-security-f04-rls`): `moduleGuard` prüft Kontostatus, `mustChangePassword` und effektive Rechte je Mutation **autoritativ aus der DB** statt aus dem JWT → Deaktivierung/Rechteentzug wirkt sofort (vorher bis Token-Ablauf); `requirePlatformSession` prüft Plattform-Admin-Status. Test `test-action-guard-authz.ts` (4 Nachweise), Browser-Regression ok | `src/server/action-guard.ts`, `platform-auth.ts` |
| **F-04** RLS definiert, aber wirkungslos | P2 | ✅ **Separates Paket, Branch `dev-security-f04-rls`** (auf `dev-security-p1` aufgesetzt): env-gesteuert (`RLS_ENFORCED`). Zweite Verbindung als `isms_app` (`RLS_DATABASE_URL`, NOBYPASSRLS); `dbForTenant` setzt `app.tenant_id` transaktionslokal auf derselben Connection; Policies mit `USING`+`WITH CHECK` + `FORCE` auf allen **49** Tenant-Tabellen. Owner-Rolle (Superuser/BYPASSRLS) bleibt für Migrationen/Seed/Login und lokal unberührt → Default-Betrieb unverändert. Test `test-rls-enforcement.ts` (5 Nachweise) | `src/server/db.ts`, Migration `rls_enforce`, `docker-compose.coolify.yml`, `.env*.example`, `DEPLOY-PROD-CONTABO.md` |
**Gate (kombinierter Stand A+B+C):** `tsc`=0 · `lint` sauber · `build` grün · `_verify.py`=OK · `test-tenant-isolation`=OK (15 Fälle) · Browser-Smoke (Login, Dashboard, Richtlinien-Register, CSP-Header, Guard-Redirect) verifiziert. **F-04 separat:** `tsc`/`lint`/`build` grün · `test-rls-enforcement`=OK (5/5, inkl. Nachweis „Owner-Betrieb heil unter FORCE") · Owner-Pfad-Smoke verifiziert.
### P3/P4-Paket (Branch `dev-security-p3`, auf `dev-security-f06-session-auth`, Stand 2026-07-30)
Vier dateidisjunkte Lanes, additiv gemergt, kombiniertes Gate grün. **Nur die MFA-Lane migriert.**
| Finding | Prio | Behebung | Kern-Dateien |
|---|---|---|---|
| **F-10** Aufgaben-Modul ohne Rechteprüfung | P3 | Neues Recht `task:write`; `guard("task:write")` in `createTask`/`proposeTasksFromTriggers`/`claimTask`/`updateTask`; `CreateTaskInput` per Zod (Längen-/Größenlimits); `owner`/`assignee` gegen aktiven Mandanten-Nutzer geprüft | `rbac.ts`, `actions/tasks.ts` |
| **F-13** Privilege Escalation über `role:manage` | P3 | **Pragmatische Variante** (Entscheidung): Delegation erlaubt (Admin darf fachliche/geklonte Rollen für andere anlegen), aber **Selbstzuweisung** höher privilegierter Rollen gesperrt → Selbst-Eskalation ausgeschlossen; volle Auditierung | `actions/tenant-users.ts` |
| **F-20** Schwache Passwort-Policy bei Provisionierung | P3 | `validatePassword(…, DEFAULT_PASSWORD_POLICY)` statt `min(8)` | `actions/admin.ts` |
| **F-16-zentral** Sicherheitsereignisse nur im Log | P3 | Importzyklus per Lazy-Import gelöst → Isolationsverletzung wird echter `denied`-Audit; Error-Boundary `withActionErrors` (generische Außenmeldung, volles internes Log) bereitgestellt | `db.ts`, neu `action-error.ts` |
| **F-17** Recovery-Codes SHA-256, TOTP-Replay | P3 | Recovery-Codes → Argon2id, Entropie 40→80 Bit (Alt-Codes per Kompatibilitätspfad bis Neuausstellung); TOTP-Replay-Schutz via `lastTotpStep` (User+PlatformAdmin) | `mfa.ts`, `auth.ts`, `platform-auth.ts`, `account.ts`, `platform.ts`, Migration `mfa_totp_replay` |
| **F-11** Supply Chain | P3 | `npm ci` (Base-Image → `node:22.14.0-slim`/glibc, npm 11 gepinnt), alle Images auf feste Tags/Digests, schlanke `migrate`-Stage, SBOM dokumentiert; lokaler `docker build` grün (386 MB) | `Dockerfile`, `docker-compose*.yml` |
| **F-18** Container-Härtung | P3/P4 | Redis-Passwort, internes Netz (`internal: true`), `no-new-privileges`/`cap_drop: ALL`/Ressourcenlimits, Dev-Compose auf `127.0.0.1` gebunden | `docker-compose*.yml`, `.env*.example` |
| **CI/Renovate** (Begleitmaßnahmen) | — | `.gitea/`+`.github/workflows/ci.yml` (tsc/lint/build + `npm audit --omit=dev --audit-level=high`-Gate), `renovate.json` (Auth-Updates manuell) | `.gitea/`, `.github/`, `renovate.json` |
**Gate (kombiniert):** `tsc`=0 · `lint` · `build` · `_verify.py`=OK · 7 Sicherheitstests grün (`tenant-isolation`, `rls-enforcement`, `action-guard-authz`, `tenant-users-authz`, `mfa-hardening`, `action-error`) · Browser-Smoke (Task-Anlage F-10) verifiziert.
> ✅ **Deployment-Schritt (F-06 × F-10) — automatisiert:** Da F-06 Rechte **DB-autoritativ** prüft, wirkt eine neue Permission (z. B. `task:write`) erst, wenn `scripts/sync-role-permissions.ts` gegen die Ziel-DB läuft (legt Permission + Rolle→Recht-Verknüpfung an; additiv, idempotent). Ein reiner Re-Login genügt NICHT. Das läuft jetzt **bei jedem Deploy automatisch im `migrate`-Init-Job** (`docker-compose.coolify.yml`, direkt nach `prisma migrate deploy`, über die Owner-`DATABASE_URL`) — deckt jede künftig neu eingeführte Permission ab. Betroffene Nutzer danach neu einloggen. Manuell nachziehen nur, falls ohne Redeploy nötig: `npx tsx scripts/sync-role-permissions.ts`.
**Noch offen:** **F-14** (Demo-Seed-Härtung — auf Wunsch bewusst zurückgestellt), **F-21** (Monitoring/Log-Aggregation/Tamper-Schutz Audit-Trail — eigenes Betriebspaket). Damit sind **alle P1, alle P2 und die adressierten P3** behoben. Details: AEGIS-Bericht.
**Aktivierung F-04 in Prod:** Rolle `isms_app` LOGIN+starkes Passwort geben, `RLS_DATABASE_URL` setzen, `RLS_ENFORCED=true` am `app`-Service; Migrations-/Owner-Rolle muss BYPASSRLS/Superuser sein. Anleitung in `docs/DEPLOY-PROD-CONTABO.md`. Lokal bewusst **aus**.
---
## 2. Für das Projektmanagement — fachlicher Status
### ✅ Neu fertig auf `dev`
| Thema | Nutzen | Status |
|-------|--------|--------|
| **API-seitige Modul-Durchsetzung** | Deaktiviertes Modul sperrt jetzt auch Schreibzugriffe serverseitig (nicht nur Navigation); automatischer Vollständigkeitscheck als Build-Gate | ✅ |
| **Getrennter Superadmin-Login** | Betreiber-Zugang unter `/platform/login` mit eigenem Store, ohne Mandantenkontext; Kundenfachdaten für Superadmin gesperrt | ✅ |
| **MFA (optional)** | TOTP für Plattform-Admins **und** Mandanten-Nutzer freiwillig; Policy-Flag „MFA-Pflicht" kann Erzwingung wiederherstellen; Recovery-Codes | ✅ |
| **Benutzerverwaltung (Superadmin)** | Superadmin legt je Kunde Nutzer an (Initial-/Einmal-Passwort), weist Rollen zu, deaktiviert/reaktiviert, setzt Passwörter zurück | ✅ |
| **Benutzer- & Rollenverwaltung (Kunde)** | Mandanten-Admin verwaltet **intern** Nutzer und Rollen (eigene Rollen + granulare Rechte, Standardrollen klonbar); strikt mandantengetrennt; Lockout-Schutz | ✅ |
| **Nutzer-Onboarding ohne E-Mail** | Start mit Initialpasswort + erzwungenem Wechsel beim ersten Login; E-Mail-Einladung (Paket 4) später nahtlos ergänzbar | ✅ |
| **Popup-Bedienung** | Anlegen/Bearbeiten von Nutzern über Popups wie bei Assets; Tabelle nur Anzeige | ✅ |
| **Zuständigkeiten geklärt** | Superadmin steuert Kern-Einstellungen (Module, TISAX-Tiefe); Mandanten-Admin nur seinen Bereich (Stammdaten, Nutzer/Rollen) | ✅ |
| **Richtlinien-Governance zentral** | Zentrale Variablen (Unternehmensname, Rollen, Schutzbedarf) nur in Einstellungen pflegbar; Coverage-Matrix zeigt nur Controls des aktiven Assessment-Levels (AL2 ohne „sehr hoch") | ✅ |
| **Scoping & zentraler Schutzbedarf (A2)** | Anforderungs-Scope leitet sich zentral aus Assessment-Level (AL2/AL3), `FLAG_INCLUDE_SHOULD` und den Prüfzielen ab — nicht mehr aus dem Fragebogen; Schutzbedarf hat damit eine einzige, konsistente Quelle (AL → Flags). Scope-Filter (`scope-filter.ts`) + `c1-scope.json` (412 Anforderungen) | ✅ |
| **Umsetzungshinweise (B5)** | Kontextsensitive C6-Umsetzungshinweise (~397 Blöcke) zu den Controls, als eigenes Panel unter `/policies/hints` und aus den Richtlinien verlinkt — praktische Hilfestellung bei der Umsetzung | ✅ |
| **Validierungs-Workflow generalisiert (A3)** | Das Vier-Augen-/Review-Muster ist jetzt objekttyp-übergreifend nutzbar (nicht nur Aufgaben) — z. B. für Risiken | ✅ |
| **ISMS-Rollen & Funktionstrennung (A4)** | Wizard-Schritt „Rollen": ISMS-Rollen zuweisen, Funktionstrennungs-Regeln FT-01…06 prüfen (z. B. ISB ≠ IT-Leitung), ISB-Bestellung | ✅ |
| **Vorlagen-Versionierung & Diff (B6)** | Das Vorlagenpaket ist versioniert; Updates werden je Mandant kontrolliert per Diff-Ansicht (`/policies/updates`) übernommen statt blind überschrieben | ✅ |
| **Assessment-Readiness & Export (B7)** | Readiness-Dashboard (Reifegrad-Interpretation, C9) **plus** VDA-ISA-Katalog-Export (CSV) und Management-Zusammenfassung — Grundlage für Reporting/Abschluss | ✅ |
| **Wizard-Schritte Richtlinien & Risiken (B)** | Onboarding-Schritte 4 (Richtlinien) und 6 (Risiken) zeigen den echten Modul-Status statt Platzhaltern — der Wizard ist damit über alle 9 Schritte durchgängig | ✅ |
| **Umsetzungshinweise im Control-Schritt (#10)** | Je Control-Anforderung lassen sich die Umsetzungshinweise **dokumentieren, abhaken und in eine Aufgabe überführen**; der Umsetzungsstatus je Spiegelstrich fließt in den Reifegrad ein (`ControlImplementation`) | ✅ |
| **Aufgaben/Maßnahmen-Vereinheitlichung (7-9)** | Aufgaben und Maßnahmen im selben Bedien-Design (gemeinsamer Anlage-Dialog); Standardmaßnahmen aus dem Risikokatalog werden zu echten, verknüpften Maßnahmen; neue Rolle **`pm`** mit Voll-Sicht auf Aufgaben (`task:read_all`) | ✅ |
| **Wizard-UX-Feinschliff (#1/#3/#6)** | Interne Identifier aus den Wizard-Texten entfernt, Assets-Schritt gibt Rückmeldung beim „Aufgaben anlegen", Aufgaben-Sichtbarkeit als Pool-Popup | ✅ |
| **Asset-Schritt (A5)** | Wizard-Schritt 5 bindet das Bestands-/Asset-Modul ein — Assets werden im Onboarding erfasst statt separat | ✅ |
| **Control-Assessment & Reifegrad (A7)** | Wizard-Schritt 7: Controls bewerten (Belegstatus, Reifegrad R0–R3, Zielreifegrad); offene Punkte erzeugen automatisch Gap-Aufgaben | ✅ |
| **Standard-Risikokatalog (A6)** | Vorgefertigter Risikokatalog (C4) zum Übernehmen; Standardmaßnahmen erzeugen Aufgaben; dokumentierte **Restrisiko-Akzeptanz** (VA-09) | ✅ |
| **Gap-Konsolidierung (A8)** | Wizard-Schritt 8 bündelt alle offenen Punkte/Gaps aus den Schritten — dedupliziert, priorisiert (Quick-Wins) und gleicht sie mit Aufgaben ab | ✅ |
| **Freigabe-Workflow + Aufgaben** | Einreicher wählt Freigeber → Aufgabe; Freigeben/Ablehnen (mit Kommentar) im Modul „Aufgaben" (Vier-Augen); Dashboard-Kachel für offene Freigaben | ✅ |
| **Nicht-destruktiver Re-Import** | Richtlinien-Paket-Updates erhalten Status/Freigabe/Overrides/Variablenwerte; entfernte Einträge werden deaktiviert statt gelöscht; Änderungsreport | ✅ |
| **GAP-Report Richtlinien** | 6 neue Verfahrensanweisungen (VA-14..19), alle zuständigen VAs inline verlinkt, Rendering-Fehler behoben, verwaltete Register editierbar | ✅ |
| **Software-Verwaltung** | Software ist ein Asset (Typ SOFTWARE) und wird im Lieferantenbereich unter „Software" gepflegt (Popups wie IT-Services): Anbieter/Lieferant, Version/Patch-Stand, Freigabestatus, Freigeber, Review. Ersetzt die frühere „Software-Whitelist" | ✅ |
| **Projekte** | Projekte sind Assets (Typ PROJECT) im Assetinventar: bewertbar wie Assets (Kritikalität C/I/A), Risiken zuordenbar, IS-Klassifizierung, ISB-Einbindung, Projektstatus — Bedienung als Popup | ✅ |
| **Kritische IT-Dienste** | Schreibgeschützte Auto-Sicht (`/assets?view=critical`): leitet kritische Dienste automatisch aus Verfügbarkeit und BIA ab, mit RTO/RPO aus den verknüpften Prozessen und Abhängigkeiten — keine gepflegte Doppelliste mehr | ✅ |
| **Register-Konsolidierung** | Doppelte Register (externe IT-Dienste, Software-Whitelist, kritische Dienste, Projekte) entfernt und in die Fachmodule überführt; Richtlinien-Verweise zeigen jetzt auf die Module. REG-NET (Netzplan) auf ein Referenz-/Speicherort-Feld reduziert | ✅ |
| **Produktmarke Certvia** | Die Anwendung heißt und erscheint durchgängig als **Certvia** — Logo und Wortmarke, Tab-/App-Icon, Seitentitel, Anmelde- und Fehlerseiten, Druckansicht. GEFIM bleibt als Dachmarke sichtbar („Ein Produkt von GEFIM"). Ohne eigenes Mandanten-Logo zeigt jeder Kunde Certvia. Reine Design-Umstellung ohne Funktionsänderung | ✅ |
| **Wizard-Parallelisierung (Fix)** | Bearbeitung und Validierung im Onboarding-Wizard entkoppelt — Schritte können parallel bearbeitet werden, statt streng nacheinander; dazu `scripts/sync-role-permissions.ts` zum Nachziehen neuer Rechte für bestehende Mandanten | ✅ |
### 🟥 Bewusst zurückgestellt / offen
| Thema | Anmerkung |
|-------|-----------|
| **Paket 4 — SMTP + E-Mail-Einladungs-/Reset-Flow** | Auf Kundenwunsch später; Aktivierung läuft vorerst über Initial-/Einmal-Passwort, gekapselt für spätere Token-Aktivierung |
| **Netzplan-Datei-Upload (REG-NET)** | Datei-Upload-/Storage-Infrastruktur existiert noch nicht; REG-NET verweist vorerst per Feld auf den extern gepflegten Netzplan. Upload als eigenes Paket (Storage-Backend + Coolify-Volume) |
| **Tenant-weite MFA-Pflicht scharfschalten** | Flag `securityPolicy.mfaRequired` vorhanden + von „MFA deaktivieren" respektiert; Enrollment-Erzwingung beim Login noch nicht verdrahtet (analog Force-Change-Gate) |
| **Admin Phase 2** | Impersonation, Plan/Limits, Logo-Upload, DSGVO-Export/Retention |
| **NIS2-Modul** | nie begonnen (großes Framework, Incident-Reporting mit Fristen-Timern) |
| **Richtlinien-Versionierung/Diff, DOCX/PDF-Export** | offen |
| **ISB-Freigabe der neuen GAP-Texte** | fachlicher Prozessschritt (kein Code): neue/geänderte VA-/Richtlinien-Texte durch den ISB freigeben |
| **Platzhalter-Module** | SoA & Controls, Vorfälle, Nachweise, Management-Review, KI-Chat |
---
## 3. Für neue Entwickler — technische Landkarte
> Setup, Stack, Konventionen und Fallstricke unverändert in **`docs/HANDOVER-DEV.md`** (§1–§9). Hier nur, was `dev` ergänzt.
### 3.1 Neue Datenmodelle (Prisma)
| Modell | Zweck | Mandantengebunden? |
|--------|-------|--------------------|
| `PlatformAdmin` | Getrennter Superadmin-Store (kein `tenant_id`), Argon2id, TOTP-Secret, Recovery-Codes, Lockout | nein (plattformweit) |
| `PlatformSetting` (Singleton) | Plattform-Policy, u. a. `mfaRequired` | nein |
| `Task` / `TaskComment` | Generisches Aufgaben-/Freigabe-Modell (erster Typ `policy_approval`), Kommentar-Historie | ja |
| `ManagedRegister` / `RegisterRow` | Generisches editierbares Register (code, columns JSON, Cross-Links supplier/asset) | ja |
| `SoftwareProfile` | 1:1 zu `Asset` (Typ SOFTWARE): Anbieter-Verknüpfung (`providerAssetId`→SUPPLIER), Version, Freigabestatus (`SoftwareApprovalStatus`), Freigeber, Kritikalität, Review | ja |
| `ProjectProfile` | 1:1 zu `Asset` (Typ PROJECT): IS-Klassifizierung, ISB-Einbindung, `ProjectStatus`. Kritikalität via C/I/A am Asset, Risiken via `RiskAsset` | ja |
| `AssetType` neu | Enum um `SOFTWARE` und `PROJECT` erweitert | — |
| `User.*` neu | `mustChangePassword`, `mfaEnrolledAt`, `recoveryCodes` | ja |
| `PolicyDocument.archivedAt`, `PolicyRequirement.archivedAt` | Lifecycle für nicht-destruktiven Re-Import (deaktivieren statt löschen) | ja |
| `AuditLog.tenantId` nullable | Plattform-Ereignisse ohne Mandantenbezug (`scope=platform`) | — |
| `WizardScope` (A2) | Scope des Onboarding-Wizards: Prüfziele, Geltungsbereich, Standorte, Ausschlüsse — Basis der Scope-Filter-Engine (`scope-filter.ts`) | ja (RLS) |
| `ImplementationHint` (B5) | Globaler C6-Umsetzungshinweis-Katalog je Anforderung (organisatorisch/technisch/Nachweise/Ressourcen, AL-Filter) | nein (globaler Katalog, kein RLS) |
| `PolicyPackageState` (B6) | Je Mandant: Version/Stand des importierten Vorlagenpakets — Basis für kontrollierte Diff-Übernahme von Updates | ja (RLS) |
| `ControlAssessment` (A7) | Je Mandant/Control: Belegstatus, Reifegrad (R0–R3), Zielreifegrad — Basis für Gap-Aufgaben und SoA | ja (RLS) |
| `RiskCatalogEntry` (A6) | Globaler Standard-Risikokatalog (C4) zum Übernehmen — Content identisch je Mandant | nein (globaler Katalog, kein RLS) |
| `ControlImplementation` (#10) | Je Mandant/Control-Spiegelstrich: Umsetzungsstatus (dokumentiert/abgehakt), koppelt an Reifegrad und Aufgaben | ja (RLS) |
| `Task.description` (7-9) | Beschreibungsfeld am bestehenden `Task` (Migration `task_description`) für die Aufgaben/Maßnahmen-Parität | ja |
| `Risk.acceptedAt/By/Rationale` (A6) | Restrisiko-Akzeptanz-Felder am bestehenden `Risk` (Migration `risk_acceptance`) | ja |
Alle neuen tenant-gebundenen Modelle sind in **`TENANT_MODELS`** (`src/server/db.ts`) und haben **RLS-Policies** (in den jeweiligen Migrationen).
### 3.2 Auth-Architektur (wichtig!)
- **Zwei getrennte NextAuth-Instanzen:** Mandanten-Login (`src/server/auth.ts`, `/login`) und **Plattform-Login** (`src/server/platform-auth.ts`, eigener Cookie + basePath `/api/platform-auth`, `/platform/login`). Plattform-Session trägt **keinen** `tenantId`.
- **Superadmin-Bereich** liegt unter `src/app/(platform)/…` (Layout erzwingt Plattform-Session + ggf. MFA); Mandanten-App unter `src/app/(app)/…`.
- **MFA-Helfer** `src/server/mfa.ts` (otplib TOTP + Recovery-Codes) — von Plattform- und Nutzer-MFA gemeinsam genutzt.
- **Force-Change:** `(app)`-Layout prüft Kontostatus/`mustChangePassword` autoritativ aus der DB → `/change-password` bzw. Abmelde-Screen bei deaktivierten Konten.
- **Passwort-Policy:** `src/lib/password-policy.ts` (reine Validierung, client-safe) + `src/server/password.ts` (Argon2id + Generator); Quelle `TenantSettings.securityPolicy.password`.
### 3.3 Modul-Durchsetzung (§3.4) — Konvention für neue Actions
- Jede mutierende Server-Action eines **gegateten Moduls** läuft über `moduleGuard("<key>")` (`src/server/action-guard.ts`): Session → `assertModuleEnabled` → RBAC.
- **Vollständigkeitscheck** `scripts/check-module-guards.ts` (als `prebuild` verdrahtet): jede Datei in `src/server/actions/` muss dort eingetragen sein (Modul-Key oder `EXEMPT`), sonst **failt der Build**. → Neue Action-Datei? Dort eintragen.
- Neues Modul **`tasks`** in `src/lib/modules.ts` (für Bestands-Tenants per Default aktiv, da fehlende `TenantModule`-Zeile = aktiv).
### 3.4 Wo liegt was (neu)
- **Superadmin/Plattform:** `src/app/(platform)/admin/**`, `/platform/{login,enroll-mfa,profile}`, `src/server/actions/{admin,platform,platform-users}.ts`.
- **Benutzer-/Rollenverwaltung (Kunde):** `src/app/(app)/settings/users/`, `src/server/actions/{tenant-users,account}.ts`, Komponenten `user-table.tsx`/`user-forms.tsx`/`role-manager.tsx`.
- **Aufgaben/Freigabe:** `src/app/(app)/tasks/`, `src/server/actions/tasks.ts` (+ `submitForApproval` in `policies.ts`); Dashboard-Kachel in `dashboard/page.tsx`.
- **Register:** `src/components/generic-register.tsx`, `src/server/actions/register.ts`, Definitionen in `prisma/import-managed.ts` (`GENERIC_REGISTERS` + `RETIRED_REGISTERS`-Cleanup).
- **Software (Lieferantenbereich):** Tab in `src/app/(app)/suppliers/page.tsx` (`?tab=software`), `src/components/software-modals.tsx`, `src/server/actions/software.ts`, `SOFTWARE_INCLUDE` in `src/lib/supplier-include.ts`.
- **Projekte (Assetinventar):** Typ-Filter/Popup in `src/app/(app)/assets/page.tsx`, `src/components/project-modals.tsx`, `src/server/actions/projects.ts`, `PROJECT_INCLUDE` in `src/lib/supplier-include.ts`.
- **Kritische IT-Dienste:** Auto-Sicht-Zweig in `src/app/(app)/assets/page.tsx` (`?view=critical`) — abgeleitet aus Verfügbarkeit + BIA (`ProcessAsset`→`Process`→`BiaEntry`); Link-Ziel von `resolveLink("REG-CRIT-SERVICES")`.
- **Richtlinien-Import (nicht-destruktiv):** `prisma/import-policies.ts` (Diff/Upsert, `{ dryRun }`, Änderungsreport, `archivedAt`).
- **Richtlinien-Import/Upload (B4):** Actions `src/server/actions/policy-package.ts` (Import-Kapselung: Self-Service `importPolicyPackage` + Plattform `importPolicyPackageForTenant`) und `policy-upload.ts` (`uploadOwnPolicy`); Aktivierungs-Trigger in `toggleTenantModule` (`admin.ts`); Storage-Adapter `src/server/storage/adapter.ts` (Stub, Epic S1); UI `src/app/(app)/policies/page.tsx` (Buttons + Report-Banner) + `src/app/(app)/policies/upload/page.tsx`. Eigene Uploads liegen im Namensraum `EIG-*` (außerhalb des Paket-Namensraums → vom Re-Import unberührt).
- **Branding (in `dev`):** Tokens in `src/app/globals.css` (`--brand-*` / `--ui-*`) mit JS-Pendant `src/lib/brand.ts`; Komponenten `src/components/brand/{certvia-logo,tenant-brand,powered-by-gefim}.tsx`; Assets `public/assets/logo/`, `public/favicon/`, `public/site.webmanifest`; Dokument-/Mail-CD `src/lib/{document-brand,email-brand}.ts`; Doku `docs/BRANDING-CERTVIA.md` (+ Design-Paket unter `docs/branding/`).
- **Vorlagenpaket (Source of Truth):** `seed/isms-vorlagenpaket-v2/` — 28→**37 Dokumente** (VA-14..19; **P01/D01/VA-20** neu für Prototypen-/Datenschutz, B4-3), `mapping.json` (321 Anforderungen), `variables.schema.json`, Baseline, Nachweisregister, **`_verify.py`** (Rendering-/Anker-Check; nach Änderungen `python3 _verify.py` → **OK**).
### 3.5 Konventionen / Fallstricke (Ergänzungen)
- **Dev-Server & Server-Actions:** Änderungen an Server-Actions greifen im Turbopack-Dev manchmal erst nach **Neustart** des Dev-Servers.
- **Migrations-Flow Prisma 7** wie gehabt (`migrate diff --from-config-datasource … --to-schema … --script`, dann RLS-DO-Block manuell anhängen, `migrate deploy`). Data-Migrationen (z. B. `strip_tool_variable_articles`) sind reine SQL-`UPDATE`-Migrationen.
- **Geteilte DB bei paralleler 2-Lane-Entwicklung (Dev A × Dev B):** Beide Worktrees zeigen auf **dieselbe** lokale Postgres-DB. Dadurch enthält `migrate diff --from-config-datasource` **fremde**, vom anderen Branch angewandte Schemaänderungen (z. B. wollte A1-1 fälschlich Dev Bs `tasks`-Spalten `links/origin/priority/resources` droppen). **Regel:** beim Erzeugen einer Migration nur die **eigenen** DDL-Blöcke übernehmen und Fremd-Drops von Hand entfernen; anschließend prüfen, dass die Spalten/Objekte des anderen erhalten sind. Migrations-Reihenfolge & „nie gleichzeitig" strikt nach Contracts §6. **Merge-Nachsorge:** Wird eine Migration im anderen Branch neu erzeugt (anderer Timestamp) und die geteilte DB hat noch die alte angewandt, meldet `migrate status` „applied ≠ lokal". Fix ohne Datenverlust: stalen `_prisma_migrations`-Eintrag löschen + `prisma migrate resolve --applied <neuer_name>` (kein SQL läuft neu). So geschehen bei `tasks_wizard_fields` (`…103650` in DB → `…104412` committet).
- **Zentrale Variablen** (Organisation/Rollen/Schutzbedarf-Flags, `src/lib/policy-variables.ts`) sind im Richtlinien-Editor gesperrt und werden serverseitig geblockt — nur in `/settings` pflegbar.
- **Rendering (Variante B):** Tool-Variablen-Defaults ohne führenden Artikel (`TOOL_TICKET`=„Ticketsystem" etc.); der Fließtext setzt Artikel/Deklination. Bestandswerte werden per Migration migriert.
- **Register vs. Managed-Register-Docs:** die verwalteten Register (CRYPTO/RISKMATRIX/CLASSIFICATION/HANDBUCH) sind spezifische Modelle; die verbliebenen `REG-*` (SENS-ROLES, AUDIT-PLAN, NET) nutzen das **generische** `ManagedRegister`-Modell. `importPolicies` archiviert nur den Paket-Namensraum (L00/R*/VA-*/BASELINE/NACHWEIS) — Register bleiben unberührt.
- **Register-Konsolidierung (Block 5):** REG-EXT-SERVICES / REG-SW-WHITELIST / REG-CRIT-SERVICES / REG-PROJECTS sind **stillgelegt** (Liste `RETIRED_REGISTERS` in `import-managed.ts` löscht Dokument + `ManagedRegister` + Zeilen idempotent beim Import). Ihre `{{LINK:REG-…}}` in den Seed-Texten bleiben unverändert — nur `resolveLink` (`src/lib/policy-render.ts`) biegt sie auf die Modul-Routen um (`/suppliers?tab=services|software`, `/assets?view=critical`, `/assets?type=PROJECT`). Wer ein Register neu stilllegen will: Code aus `GENERIC_REGISTERS` entfernen, in `RETIRED_REGISTERS` eintragen, `resolveLink`-Fall ergänzen.
- **Software/Projekte als Asset-Typen:** folgen exakt dem bestehenden Muster von SUPPLIER/IT_SERVICE (Asset + 1:1-Profil, `refNo` je Mandant, Cockpit-Popups auf `/suppliers` **und** `/assets`, backHref-gesteuert). Neue Action-Dateien (`software.ts`→`suppliers`, `projects.ts`→`assets`) sind in `scripts/check-module-guards.ts` registriert.
### 3.6 Demo-Logins (Passwort `Demo1234!`)
- Mandanten-Login `/login`: `admin@demo.example` (Mandanten-Admin + ISB), **`bea.approver@demo.example`** (ISB, zweiter Freigeber für den Vier-Augen-Workflow), `auditor@…`, `owner@…`, `user@…`.
- Plattform-Login `/platform/login`: `admin@demo.example` (getrennte Session; MFA optional, Enrollment beim ersten Login nur wenn Policy es verlangt).
---
## 4. Commit-Übersicht (`main..dev`, neueste zuerst)
```
55260a7 Merge SEC3+SEC4 (MFA pro Mandant erzwingen, WebAuthn/Passkeys, TOTP-Verschlüsselung at rest, Plattform-Admin-Verwaltung) in dev
8a154f0 Merge TISAX v7-Fortsetzung (Fachbereich-Ableitung beim Import, Richtlinien nach Fachbereich im Onboarding, parametrisierbares Redirect-Ziel) in dev
a0c0d14 Merge TISAX v6/v7 (ABGABE-xlsx-Export, echter MinIO/S3-Adapter, PolicyDocument.domain/Fachbereich, Audit-Permissions) in dev
850b691 Merge TISAX v5 (Audit-Vorbereitung Phase 1: Audit-Übersicht/Wizard-Shell, Readiness/GAP-Tab, Nachweise-Tab, Control-Beschreibungen mit KI-Entwurf + ABGABE-CSV-Export) in dev
6370cee Merge TISAX v4 (Prozesshaus-Ausbau: Löschen, Katalog-Teilprozesse parentCode, Details-Overlay; BIA-Popup vereinfacht + Träger-/Risiko-Neuanlage) in dev
23df560 Merge TISAX v3 (Prozesshaus + geführtes BIA je Prozess, Popup-Bearbeitung Rollen/Kriterien, zusätzliche Prozess-Felder) in dev
f1c11aa Feature: Plattform-Konsole legt Tenant-Nutzer per Einladungslink an (wie Mandanten-Admin)
2bfc071 Merge TISAX-Integration (M0–M4 + Wizard-Neustruktur) in dev — Fundament, Strukturanalyse, Cockpit (RACI/Evidence), Audit-Wizard, TISAX-Tasks, Content-Seed; 3 Migrationen + RLS für neue Tabellen
2fd522c Feature: Benutzer-Anlegen verschickt Einladungslink per Mail (invitation-Template, /reset-Token 7 Tage) statt Passwort-Anzeige
4d28c86 Fix: Benutzer-/Mail-Actions graceful statt Fehlerseite + Formular-Werterhalt
6b4c6fb Coolify: Mail-Worker-Service (SEC1) ergänzt
c47ace0 Merge SEC1+SEC2: Mailversand/Queue (SEC1, BullMQ/ioredis/nodemailer, Worker) + Auth-Self-Service (SEC2, AuthToken, Reset/Passwortwechsel/E-Mail-Änderung, Session-Invalidierung, Rate-Limit) in dev
3998f9f Fix: /tasks SSR-Crash — entfernte Variable `open` im Untertitel wiederhergestellt
3932ff2 Merge 7-9/4: Aufgaben als Kanban-Board (Maßnahmen-Design) + Vorschläge/Bearbeiten korrigiert in dev
00e7e79 Dockerfile: Fachcontent (docs/wizard-uebergabe) ins migrate-Image für Demo-Seed (C4/C6)
fb1935e Dockerfile: Richtlinien-Vorlagenpaket (seed/) in migrate- und runner-Image
d0dfe0b Merge Security: P1/P2/P3-Härtungspaket (F-01…F-20) in dev — Tenant-Isolation, Auth/Session, RLS scharf (F-04), moduleGuard-DB-Authz (F-06), Web-Härtung, MFA-Replay, Supply-Chain/Container (F-11/F-18)
63b583d Merge Dev-A: A6-1 + A6-2 (Standard-Risikokatalog C4, Standardmaßnahme→Aufgabe, Restrisiko-Akzeptanz) in dev
e6c5e51 Merge Dev: UX-Fixes (#1/#3/#6) + Aufgaben/Maßnahmen-Vereinheitlichung (7-9) + Umsetzungshinweise Control-Schritt (#10, ControlImplementation) in dev
6b4b190 Merge Dev-B: Wizard-Schritte 4 (Richtlinien) + 6 (Risiken) — echte Komponenten statt Platzhalter in dev
0629613 Merge Branding: Umstellung auf Certvia (Design-Tokens, Logo/Icons, Metadaten, i18n, Auth-/Systemseiten, Dokument-CD) in dev
7f4a85c Merge Fix: Wizard-Bearbeitung von Validierung entkoppeln (Parallelisierung) + Rollen-Rechte-Sync-Skript in dev
6b20e13 Merge Dev-B: B7-2 (VDA-ISA-Katalog-Export CSV + Management-Zusammenfassung, C9) in dev
2cc422c Merge Dev-A: A8-1/A8-2 (Gap-Konsolidierungs-Engine) + A8 (Gap-Schritt 8: Aggregation/Priorisierung/Task-Abgleich) in dev
76cfebc Merge Dev-A: A5-1 (Asset-Schritt) + A7-1/A7-2 (Control-Assessment + Reifegrad-Engine) in dev
7dc5ae1 Merge Dev-B: B6 (Vorlagenpaket-Versionierung + kontrollierte Diff-Übernahme) in dev
1238564 Merge Dev-B: B7-1 (Assessment-Readiness-Dashboard + Interpretationstexte C9) in dev
de76856 Merge Dev-A: A4 (ISMS-Rollen Schritt 3 + Funktionstrennung FT-01…06) in dev
012c0fa Merge Dev-B: Prüfziele als Single Source aus WizardScope (Doppelquelle behoben) in dev
227d7a3 Merge Dev-A: A3-1 (generisches Objekt-Review / Validierungs-Workflow) in dev
8c9f648 Merge Dev-B: B5-1 + B5-2 (Umsetzungshinweise C6, kontextsensitives Panel) in dev [+ 98d5e26 B5-1, 93e2f44 B5-2]
d5c11df Merge Dev-A: A2-1 + A2-2 (Assessment-Level zentral, Scope-Filter) in dev [+ 69cc25a A2-1, 177df8b A2-2]
78be114 Doku: STAND B4 · c473cad B4-3 (P01/D01/VA-20) · eb7c899 B4-2 (Upload) · 269506b B4-1 (Import) — Dev B, per FF in dev
4e1b116 Merge Dev-B: B3 Fragebogen (Schritt 2 context) in dev [+ f818093 B3-Feature-Commit]
872c35e Merge Dev-B: F3+B2 Regel-/Mapping-Engine in dev [+ 20afd63 B2-Feature-Commit]
(DevOps-Integration: dev-b2-regel-engine + dev-b3-fragebogen additiv gemergt; Gate grün)
b9d8027 Story A1-2: Dashboard-Kachel „Onboarding-Fortschritt" (Dev A)
949d7c5 Story F2: ObjectReviewStatus + external_validator + Wizard-Validierung (Dev A)
12c94fe Merge Dev-B-Lane in dev: F1 + B1 + F4 (Konsolidierung beider Lanes)
0d52090 F4: Wizard-Flags in variables.schema.json (Dev B)
ddd2dc4 B1: Auto-Generierung von Aufgaben-Vorschlägen (C2 §8) (Dev B)
30c3638 F1: Task-Objekt um Wizard-Felder erweitern (Contracts §1) (Dev B)
97b3b4e Story A1-1: Onboarding-Wizard-Shell (Step-Registry, State-Machine, Gate) (Dev A)
(darüber Wizard-Kickoff-Commits: Sign-off, Dev-B-Bestätigung, Contracts, Übergabepaket)
(sowie lokale Doku-Commits — Onboarding-Prompt + STAND-Updates)
f368d8f Fix: Fonts selbst hosten (next/font/local) + Bootstrap an Superadmin-Store anpassen
cc8d486 Feat: Prod-Bootstrap (Erst-Superadmin) + Contabo-Runbook
── ab hier auf origin/dev (gepusht) ──
9b479ab Software und Projekte als Assets + Auto-Sicht kritische Dienste
150a1d9 Register-Konsolidierung: 4 Register in Fachmodule überführt
0806030 Datenmodell: Asset-Typen SOFTWARE und PROJECT mit Fachprofilen
2ec0230 Doku: Konsolidierte Übergabe des dev-Stands (PM + neue Entwickler)
95d4479 GAP WP3.1: IMPL-Texte mit verwalteten Registern verdrahtet
bd56a47 GAP WP3.0: Generisches, editierbares Register-Datenmodell + Editor
b9784e5 GAP E2: ISA 3.1.3-Stub entfernt (3.1.2 deprecated)
b7d5df4 GAP WP2.2 (Rendering Variante B) + WP4 (Feinschliff)
36c2903 GAP WP1: Neue VAs 14–19 + Register + Baseline + Eltern-Wiring
522508a GAP WP2.1: Inline-VA-Verweise + Verify-Fix (F17)
742277f Doku: Benutzer-Popups, Aufgaben-Modul, Richtlinien-Governance, Demo-Approver
1d8f563 Freigabe-Workflow + Aufgaben-Modul + Dashboard-Kachel
73c9813 Benutzerverwaltung als Popup (Einstellungen + Admin)
959d816 Richtlinien-Fixes: zentrale Variablen, Schutzbedarf zentral, Coverage nach Level
d38b265 Benutzer bearbeiten (Name/E-Mail) + Kern-Einstellungen nur Superadmin
303dcd0 Doku: Produktionshärtung + Benutzer-/Rollenverwaltung
d0821e2 Paket B — Mandanten-Admin: Benutzer- & Rollenverwaltung
3db820e Paket C — MFA optional (Superadmin + Tenant)
908b098 Paket A — Plattform-Admin: Benutzerverwaltung je Mandant
db36c79 Fundament Benutzer-/Rollenverwaltung: Schema, Passwort-Policy, Force-Change
e07dcd9 Nicht-destruktiver Richtlinien-Re-Import (Härtung Paket 3)
8e4040d Separater Superadmin-Store + eigener Login + MFA (Härtung Paket 2)
8348764 API-seitige Modul-Durchsetzung (§3.4, Härtung Paket 1)
```
---
## 5. Deployment & Verifikation
- **Test-/Coolify-Deploy (Demo):** `dev` deployen; die 7 neuen Migrationen laufen automatisch im Init-Container (`prisma migrate deploy`), Bootstrap-Seed legt Demo-Daten + Demo-Approver an. Die letzte Migration ändert das `AssetType`-Enum und legt zwei RLS-Tabellen an — einmal komplett durchlaufen lassen, bevor die neuen Ansichten getestet werden.
- **Prod-Deploy (ohne Demo-Seed):** Runbook **`docs/DEPLOY-PROD-CONTABO.md`**. Statt Demo-Seed läuft `scripts/bootstrap-admin.ts` (Erst-Superadmin im `platformAdmin`-Store + Mandant/Mandanten-Admin, idempotent), gesteuert per `BOOTSTRAP_*`-Env im `migrate`-Job der `docker-compose.coolify.yml`; Env-Referenz `.env.prod.example`. Fonts sind selbst gehostet (kein Google-Fonts-Fetch zur Build-Zeit → reproduzierbarer Offline-Build).
- **Lokale Verifikation:** `npx tsc --noEmit` → `npm run lint` → `npm run build` (führt `prebuild`-Guard-Check aus); für das Vorlagenpaket `python3 seed/isms-vorlagenpaket-v2/_verify.py` (**OK** = rückstandsfrei, Mapping sauber).
- **Sync-Status (Gitea aktuell offline):** lokaler `dev` ist **27 Commits vor `origin/dev`** (inkl. der beiden Dev-B-Integrations-Merges F3+B2 und B3). Sobald Gitea erreichbar ist: **erst `git fetch`**, prüfen ob `origin/dev` noch `9b479ab` ist (bzw. ob jemand anderes gepusht hat), dann `git push origin dev`. Bei Divergenz nicht blind force-pushen — abstimmen.
- **Merge `dev` → `main`:** liegt beim PM/Lead (Gitea-PR: `…/msolarczek/ISMS-Tool/pulls/new/dev`).
---
## 6. Empfohlene nächste Schritte
1. **ISB-Freigabe** der neuen/angepassten Richtlinien- und VA-Texte (fachlich, inkl. **P01/D01/VA-20** aus B4-3).
2. **Storage-Backend (Epic S1)** — echten Adapter hinter `src/server/storage/adapter.ts` (B4-2-Stub) einhängen (S3/MinIO/Coolify-Volume); danach Datei-Persistenz eigener Richtlinien + Netzplan-Upload REG-NET.
3. **Wizard-Stories vollständig konsolidiert** — Dev A: A1–A8, Dev B: F1–F4, B1–B7 (inkl. B7-2 Export + Prüfziel-Follow-up). **Alle Feature-Branches in `dev`.** Als Nächstes (neue Stories/Epics): SoA-/Reifegrad-Auswertungen auf Basis von A7, Reporting-Ausbau auf Basis des VDA-ISA-Exports, echtes Storage-Backend (Epic S1).
4. **Paket 4** — SMTP + E-Mail-Einladungs-/Aktivierungs-/Reset-Flow (Aktivierung ist bereits gekapselt).
5. **Tenant-weite MFA-Pflicht** scharfschalten (Enrollment-Gate).
6. **Admin Phase 2** (Impersonation, Plan/Limits) oder **NIS2-Modul** — je nach Vertriebs-/Compliance-Priorität.
## Hotfix: Docker-Build brach ohne PASSWORD_PEPPER ab (Deploy-Blocker)
- **Symptom:** Coolify-Deploy „erfolgreich", aber KEINE certvia-Container. Ursache war der Image-Build selbst: `RUN npx prisma generate && npm run build` (Dockerfile:40) exit 1 → kein `up`.
- **Root Cause:** `src/server/auth.ts` erzeugte den Konstant-Zeit-Dummy-Hash auf **Modulebene** (`const DUMMY_HASH_PROMISE = hashPassword(...)`). `hashPassword()` liest den `PASSWORD_PEPPER`, der zur Docker-**Build**-Zeit fehlt (nur `DATABASE_URL`-Platzhalter gesetzt). `next build` (Page-Data-Collection für `/api/auth/[...nextauth]`) lud das Modul → Import-Zeit-Throw (fail-secure) → Build-Abbruch. Lokal grün, weil `.env` den Pepper liefert (im Image per `.dockerignore` ausgeschlossen).
- **Fix:** Dummy-Hash lazy + memoisiert (`dummyHash()`), Aufruf erst zur Laufzeit in `verifyIdentityPassword` — gleiches Muster wie `assertSecureEnv()`/`pepper()`. Konstant-Zeit-Verhalten (F-05) bleibt erhalten.
- **Verifiziert:** `docker build --target builder` grün OHNE gesetzten `PASSWORD_PEPPER` (Coolify-Build-Bedingung); tsc grün.
## Hotfix 2: incident-inbound-worker Crash-Loop riss den Stack ab
- **Symptom (nach Build-Fix):** migrate ✅, app ✅ `Ready`, mail-/backup-worker ✅ — aber Coolify-Status flippte auf „exited" und riss den ganzen Stack ab. Ursache: `incident-inbound-worker` ohne IMAP-Konfig `process.exit(1)` + `restart: unless-stopped` → Crash-Loop → Coolify wertet Deploy als unhealthy → `compose down` (auch der gesunden App).
- **Fix:** `scripts/incident-inbound-worker.ts` idlet jetzt bei fehlender IMAP-Konfig (`idleUntilSignal()`), statt zu exiten. Container bleibt „running", SIGTERM beendet sauber (Exit 0). Bei nachträglicher IMAP-Konfig: Container-Neustart aktiviert den Betrieb.
- **Verifiziert:** tsc grün; Smoke-Test ohne IMAP-Env → Prozess bleibt am Leben, loggt Idle-Hinweis, kein Crash.
## Merge: Objektspeicher MinIO → Garage (self-hosted)
- feature/garage-migration → dev (`--no-ff`). Umsetzung des Konzepts `docs/KONZEPT-garage-migration.md` (4 Lanes), keine Datenmigration (nur Test-Instanzen, Neu-Deploy).
- **Infra:** `garage`-Service ersetzt `minio` in `docker-compose.yml` + `docker-compose.coolify.yml`; `deploy/garage.toml` (Single-Node, `s3_region=us-east-1`, RPC-/Admin-Token); Volumes `garage_meta` (kritisch, ins Backup) + `garage_data`.
- **Provisioning:** `scripts/garage-provision.ts` — idempotenter Init-Job (Layout + Bucket `isms-documents` + Key-Import aus S3_*-Env + Rechte).
- **App-Code:** `ensureBucket()` in `adapter.ts` + `backup-store.ts` nur noch verifizierend (HeadBucket), **kein** S3-`CreateBucket` mehr (Garage kann das nicht); fehlender Bucket → sprechender Konfigfehler statt stiller Heilung. `CreateBucketCommand`-Imports entfernt.
- **Test/Doku:** `scripts/test-garage-storage.ts` (skippt ohne `S3_ENDPOINT`, echter E2E mit gesetzten S3_*), Runbook in `docs/DEPLOY-COOLIFY.md`.
- **Gate grün:** tsc, lint, build, Storage-Smoke-Test.
- **Deploy-Hinweise:** Coolify-Env `S3_ENDPOINT=http://garage:3900`, `S3_REGION=us-east-1`, `GARAGE_RPC_SECRET`/`GARAGE_ADMIN_TOKEN` **literal** setzen (Interpolationsfalle); `minio`-Service erst nach Abnahme entfernen (Rollback-Netz).
## Hotfix Garage-Deploy: garage.toml ins Image backen (Coolify-Bind-Mount-Falle)
- **Symptom:** `garage`-Container crasht ~2s nach Start → unhealthy → Deploy bricht ab. Log: `Error: IO error: Is a directory (os error 21)` nach „Loading configuration…".
- **Ursache:** Coolify legt die Quelle des relativen Bind-Mounts `./deploy/garage.toml` als **Verzeichnis** an (Storage-Behandlung) → Garage bekommt `/etc/garage.toml` als Ordner.
- **Fix:** Neue Dockerfile-Stage `garage` (`FROM dxflrs/garage:v1.2.0` + `COPY deploy/garage.toml /etc/garage.toml`); `docker-compose.coolify.yml` nutzt `build: target garage` statt `image:` + Bind-Mount. Secrets bleiben zur Laufzeit aus `GARAGE_RPC_SECRET`/`GARAGE_ADMIN_TOKEN`. Lokales `docker-compose.yml` behält den Bind-Mount (auf normalem Docker unkritisch).
- **Verifiziert:** `docker build --target garage` + Start mit gültigem Secret → Daemon läuft, `/garage status` Exit 0.
## Merge: Framework-Erweiterung — ISO 27001 neben TISAX (AP1–AP5 + B1)
- Kumulativer Merge von `feature/framework-assessment` (enthielt alle Framework-Lanes: iso27001-framework-mapping, framework-core, ap2, ap3-soa, ap4-mgmt, ap5-doccontrol, assessment) → `dev` (`--no-ff`, a747b57). 150 Dateien.
- **AP1** Framework-Dimension (`TenantFramework`, `PolicyTemplateVersion.framework`), **AP2** Provisionierung & Framework-Flags + framework-fähiger Vorlagen-Editor, **AP3** SoA-Modul (ISO-Anwendbarkeitserklärung, 93 Annex-A-Zeilen), **AP4** Managementklauseln (9.1/9.3/10.2, CAPA + Wirksamkeitsprüfung), **AP5** Dokumentenlenkung (Prüfzyklen, Versionshistorie, Lesebestätigungen), **A1–A4/B2/B3** ISO-Inhalte, **B1** Assessment-/Readiness-Strategie-Schicht.
- **Variante A** (Konzept D4): ein Dokumentensatz, zwei Mappings — `docs/FRAMEWORK-MAPPING-ISO27001.md`, `docs/KONZEPT-framework-iso27001.md`.
- **Gate grün:** tsc, lint, build. Neue Tests grün: `test-framework-{core,templates,provision,assessment,dryrun}`, `test-{assessment-iso,soa,doc-control,review}`. **TISAX-Snapshot-Regression: 0 Abweichungen** (45 Controls, AL2) — TISAX-Verhalten bit-genau erhalten.
## Fix: Normen (Frameworks) je Mandant nachträglich umschaltbar
- `cb2bc0b` Admin-Action + UI (`admin/[id]/page.tsx`, `actions/admin.ts`, i18n de/en): ISO/TISAX pro Mandant nachträglich aktivieren/deaktivieren. `1475193` Abnahmetest-Fix: „wiedererkannt" zählt reaktivierte Dokumente mit.
- Neuer Test `scripts/test-framework-toggle.ts` (Aktivieren/Deaktivieren + Zustand wiederherstellen). Gate grün: tsc/lint/build, test-framework-toggle/-dryrun, TISAX-Snapshot 0 Abweichungen.
## BIA-Prozessübersicht: Prozesshaus-Ansicht (Standard) + Tabellen-Umschalter
- `/processes` (`src/app/(app)/processes/page.tsx`) zeigt statt der flachen Tabelle standardmäßig ein **Prozesshaus**: Bahnen nach Kategorie (Management/Kern/Support), Hauptprozesse als aufklappbare Karten (native `<details>`), Teilprozesse eingerückt (Baumkonnektor). Je Hauptprozess **BIA-Rollup** aus den Teilprozessen: Kritikalität = Maximum, RTO/RPO/MTD = schärfster (kleinster) Wert; Farbcode + Statuspunkt nach `biaStatus`; Schnittstellen (`Process.interfaces`) als Chip. Umschalter „Prozesshaus ⇄ Tabelle“ über `?view=tabelle` (Prozesshaus = Default); Tabellen-Ansicht unverändert.
- **Kein Datenmodell-Umbau** — nutzt vorhandene Felder (`parentId`, `category`, `biaStatus`, `BiaEntry`). KPIs: Prozesse gesamt / im Scope / BIA vollständig / hohe Kritikalität (≥3). Modals (Detail/Anlegen/Bearbeiten) unverändert. Neue i18n-Keys unter `processes` (de/en). Gate grün: tsc/lint/build. Konzept-Mockup: `docs/MOCKUP-bia-prozessuebersicht.html`.
- **Nachtrag — jetzt umgesetzt:** strukturierte Prozess-zu-Prozess-Abhängigkeiten (siehe nächster Abschnitt); der frühere Freitext `interfaces` bleibt zusätzlich erhalten.
## Prozess-Abhängigkeiten (strukturiert: „benötigt" / „wird benötigt von")
- Neues Modell **`ProcessDependency`** (`source` benötigt `target`, optionale `note`), Migration `20260907084110_add_process_dependencies` **inkl. RLS** (tenant_isolation + FORCE), Unique `(source, target)`, FK-Cascade beim Prozess-Löschen; in `TENANT_MODELS` aufgenommen. Auswahl **innerhalb des Mandanten**.
- Server-Actions in `actions/processes.ts`: `addProcessDependency` (idempotenter Upsert, Selbstbezug ausgeschlossen, beide Prozesse müssen existieren) und `removeProcessDependency`; `deleteProcess` löst Kanten in beide Richtungen. Beide `bia:write`-gegated + Audit-Log.
- UI: Bearbeiten-Modal neuer Abschnitt „Abhängigkeiten" (Chips + Entfernen, Auswahl-Form „benötigt: [Prozess] + Notiz", plus read-only „wird benötigt von"); Detail-Modal zeigt beide Richtungen; **Prozesshaus** zeigt „↳ benötigt: …"-Chips und „▲ N Prozesse hängen hiervon ab" (Teilprozesse kompakt inline) + Link zum Abhängigkeits-Graph. Neue i18n-Keys `deps*` (de/en). Gate grün: tsc/lint/build, Modul-Guard-Check 45 Dateien ok.
- **Netz-/Graph-Darstellung:** `buildDependencyGraph` (`src/server/dependency-graph.ts`) um Prozess→Prozess-Kanten (`ProcessDependency`, Label „benötigt von", Flussrichtung Abhängigkeit→Nutzer) erweitert; sie erscheinen im bestehenden Graphen `/dependencies` (React Flow + dagre) und fließen in Analyse/kritischen Pfad ein. SPOF-Erkennung jetzt auch für **Prozesse** (gemeinsam benötigte Prozesse wie „IT-Betrieb", von denen mehrere kritische Prozesse abhängen). Neuer Client-Filter **„Nur Prozesse"** in `dependency-graph.tsx` (blendet Assets aus → reine Prozess-Abhängigkeitskarte). Verifiziert am `demo`-Mandanten: IT-Betrieb als SPOF (3) korrekt erkannt.
## Datenimport (Excel) — zurückgezogen
- Das zuvor auf `dev` ergänzte Excel-Datenimport-Feature (geteilte Logik `src/server/import/`, UI `/settings/import`, Ops-Skript, Tabelle `ImportRef`/Migration `20260904081701_add_import_refs`, Konzept `KONZEPT-datenimport.md`) wurde **vollständig entfernt** (nie deployt, daher ohne DB-Auswirkung). Bestandsdaten werden weiterhin über die Modul-Formulare bzw. den Onboarding-Wizard erfasst.
- `docs/HANDBUCH-KUNDENBETREUUNG.md` bleibt als produkt-/supportorientiertes Einstiegshandbuch für die Kundenbetreuung (Zugang/Rollen, Provisionierung, Framework-Wahl, Module, Onboarding, Support-Fälle, Doku-Verweise) — die Datenimport-Abschnitte wurden herausgenommen.
## Fix: Risikokriterien 5×5 vervollständigt (Matrix ↔ Einstellungen konsistent)
- Bug: Risiko-Bewertung (Formular-Skala 1–5, Matrix 5×5, Score-Schwellen bis 25) war 5×5, aber die Kriterien-Daten nur 4-stufig geseedet (EW_LEVELS=4, Schadensdimensionen 1–4) → 5. Stufe ohne Definition.
- Entscheidung (PO): **5×5** — Kriterien ergänzen (Bewertung/Matrix/Score bleiben unverändert).
- Seed `prisma/import-managed.ts`: 5. EW-Stufe („Nahezu sicher") + 5. Stufe je Schadensdimension.
- Editor `onboarding/steps/criteria/criteria-editor.tsx` (Settings + Wizard geteilt): Typ/Update/Neuanlage/Rendering auf 1–5; `actions.ts` zod-Schema um „5"; `settings/risk-criteria/page.tsx` + `onboarding/steps/criteria/step.tsx` editorDims um „5".
- Backfill `scripts/backfill-risk-5x5.ts` (idempotent): ergänzt EW-Stufe 5 + Schadensstufe 5 für Bestandsmandanten. Für Test/Prod nach Redeploy je Instanz einmal ausführen (demo, gefim).
- Gate grün (tsc/lint/build); Backfill lokal verifiziert.