# 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 (``), 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("")` (`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 ` (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 `
`), 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.