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>
74 KiB
Entwicklungsstand dev — Konsolidierte Übergabe (PM + neue Entwickler)
Stand: 2026-07-30 · Branch
dev· Sync-Hinweis: lokal deutlich vororigin/dev(origin/dev=9b479ab); enthält u. a. Prod-Deployment-Vorbereitung, Wizard-Kickoff + Story A1-1 und diese Doku.devist auforigin(git.certvia.de) undlocal-gitea(intern) gepusht. Vor dem nächsten Push zuerstgit fetchund 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 nachdevkonsolidiert (Gate grün); noch offene Feature-Branches rebasen aufdev. Deployment-Pflichtschritte aus dem Security-Paket siehe §1a (F-04 RLS scharfschalten, F-06×F-10scripts/sync-role-permissions.tsnachmigrate). 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_URLmit Passwort, BullMQ/ioredis) — der Worker fehlt noch indocker-compose.coolify.yml. · Basis-Doku:docs/HANDOVER-DEV.md,docs/HANDOVER-PM.md,docs/SPEC.mdZweck: Ein Dokument, das den kompletten Stand der Weiterentwicklung aufdevzusammenfasst — fachlich (für PM) und technisch (für einen neuen Entwickler).devist noch nicht nachmaingemergt.
Neu (2026-08-07):
feature/settings-roles-platform-uiintegriert (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-adminintegriert — Proxy-Gate für Plattform-Routen (src/proxy.ts):/platform/login+/api/platform-authsind jetzt public, und/admin,/admins,/profile,/platformgaten aufs Plattform-Session-Cookie und leiten anonyme Besucher auf/platform/loginstatt 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,/dashboardweiterhin →/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): auchcsrfToken(__Host-platform-authjs.csrf-token) undcallbackUrl(platform-authjs.callback-url). Grund: zwei Auth.js-Instanzen auf derselben Domain teilten sich die Default-Cookiesauthjs.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:) stattNODE_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 istNODE_ENV=production, der interne Testserver wird aber ggf. über http bedient (AUTH_URL=http bzw. fehlendesX-Forwarded-Proto); ein hart auf__Secure-/__Host-/securegesetztes 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 ProdAUTH_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 (keintenant_id, nicht inTENANT_MODELS, keine RLS) +User.identityId; Migrationidentity_foundation; Reseed auf Identity+Membership inkl. 2. Mandantdemo2+ Multi-Membership-Fixturemulti@demo.example. Expand/Contract (User-Auth-Felder bleiben vorerst Legacy; Contract-Migration droppt sie am Ende von WS1–WS4). Neuer Testscripts/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.tsauthentifiziert gegen die globaleIdentity(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 alsauthorizeTenantCredentials()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. Testscripts/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) lesenmustChangePassword/sessionsValidAfter/mfaEnrolledAt+ globalenidentity.statusaus der Identity; Membership-Status/Rechte weiter ausUser. Reset-Kette (auth-recovery/auth-selfservice/auth-token) aufPrincipalType "identity". Mandanten-Admin-Passwort-Reset entfernt (goldene Regel 5). E-Mail-Änderung für Mandanten-Konten deaktiviert = Phase 2. Testscripts/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/inviteFunctionHolderohne 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. Testscripts/test-invitation.ts.- WS6 Seed/Provision/Bootstrap: bereits durch WS0 abgedeckt (
provisionTenant/seed.tsIdentity-fähig,sync-role-permissions.tsorthogonal) — 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)/layoutleitet auf/select-tenant. Wechsel server-autoritativ übersetActiveTenant(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). Testscripts/test-tenant-switch.ts.- WS4b WebAuthn→Identity: Passkeys sind identitätsgebunden (Migration
20260811140000_webauthn_identity:webauthn_credentialsverlierttenant_id/RLS, verweist aufidentities);WebAuthnCredentialraus ausTENANT_MODELS. Passkey-Login/-Verwaltung überidentityId.- Contract-Migration (
20260811150000_contract_user_auth_columns): die 10 ungenutztenUser-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):
/loginSchritt 1 (E-Mail+Passwort) → bei aktiver MFA kurzlebiger, signierter, einzweckigermfa_pending-Cookie (KEINE Session) →/login/mfaSchritt 2 →login-ticket-Provider prägt die Session. BausteineverifyIdentityPassword/verifyIdentityMfa/finalizeIdentityLogin(kein Passwort-Orakel, Lockout wie beim vollen Login).src/server/login-ticket.ts(HMAC). Runtime-verifiziert. Testscripts/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-adminintegriert — globale Vorlagen-Modelle + Migrationpolicy_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 nachdevgemergt): Phase 1 (Passwort-Pepper) ist erledigt (Argon2-secret, zentral, indev) — unverändert. Phasen 2–3 liegen jetzt als Ops-Runbook vor:docs/DEPLOY-PROD-CONTABO.mdum Host-Encryption at-rest (LUKS/dm-crypt fürs Daten-Volumepgdata+MinIO, Boot-Unlock-Verfahren A/B, Passphrase im Passwortmanager) und verschlüsselte Backups (pgBackRestaes-256-cbc+ageje Umgebung + restic für MinIO, Coolify-Cron/Retention/Restore-Test, Bezug zur App-Backup-EngineBACKUP_ENC_KEY/§9) ergänzt; neuesdocs/SECRETS-REGISTER.md(Ownership + Rotationsregel je Umgebung fürAUTH_SECRET/MFA_ENC_KEY/PASSWORD_PEPPER/BACKUP_ENC_KEY/pgBackRest-Key/age-Keypair;PASSWORD_PEPPER+MFA_ENC_KEYals 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
devkonsolidiert. Konflikt nur inschema.prisma(User-Relationsblock → Union mitidentity) +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.PlatformSettingerweitert (backupTarget,backupLocalDir,backupS3*,backupS3SecretKeyEncverschlüsselt at-rest), Migrationbackup_target_config(additiv). StatischesbackupStore→getBackupStore()mit Präzedenz DB → Env (S3_*/BACKUP_LOCAL_DIR) → lokaler Default.backups, fail-secure bei unvollständiger S3-Config; alle Call-Sites umgestellt. Persistentesbackups-Volume (app + backup-worker,/app/.backups) — Lokal überlebt Redeploys. Neuer Testscripts/test-backup-target.ts. Gate grün (tsc/lint/build + 28/28),/admin/backupim 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 (Migrationincidents,src/app/(app)/incidents/*,src/server/actions/incidents.ts,docs/KONZEPT-incidents.md, Testscripts/test-incidents.ts). IM-B (Meldefristen/Notifications) noch in Arbeit (eigener Branchlane-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.cvbinline in den Browser, ohne Worker/Redis/S3 („Sicherung herunterladen"-Button im Export-Popup).S3BackupStore.ensureBucket()legt einen fehlenden MinIO-Bucket beim erstenputautomatisch an (behebt „The specified bucket does not exist"). Neuer Testscripts/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
devgemergt; 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; Migrationincident_inbound_d, Testscripts/test-incident-inbound.ts). Damit ist das Modul „Vorfälle" funktional komplett (IM-A…IM-D). Docker-Fix:prisma/schema.prismawird jetzt auch in dierunner-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); sonstENOENT→ „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:
-
Produktionshärtung Phase 1 — API-seitige Modul-Durchsetzung, getrennter Superadmin-Store mit eigenem Login + MFA, nicht-destruktiver Richtlinien-Re-Import.
-
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.
-
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).
-
GAP-Report-Umsetzung (Richtlinien-Vorlagenpaket) — WP1–WP4 + E2: sechs neue Verfahrensanweisungen (ISB-Volltexte), Inline-VA-Verlinkung, Rendering-Fix, generisches editierbares Register-Datenmodell.
-
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.
-
Produktions-Deployment-Vorbereitung (Commits
cc8d486,f368d8f) — Prod-Bootstrapscripts/bootstrap-admin.ts(legt Erst-Superadmin implatformAdmin-Store + Mandant/Mandanten-Admin idempotent an, ersetzt in Prod den Demo-Seed; perBOOTSTRAP_*-Env immigrate-Job derdocker-compose.coolify.yml);.env.prod.example; Fonts selbst gehostet (next/font/local, committete woff2 insrc/app/fonts→ reproduzierbarer Offline-Build, DSGVO); Runbookdocs/DEPLOY-PROD-CONTABO.md(VPS/Coolify/Gitea, TLSapp.certvia.de, Bootstrap, Backups, Go-Live-Checkliste). -
Branding-Umstellung auf Certvia — in
devintegriert (Gate grün): getrennte Token-Ebenen--brand-*(Logo/Print/Export) vs.--ui-*(Produktpalette, inhaltlich unverändert) mitsrc/lib/brand.tsals 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 insrc/proxy.tsmusste.webmanifestfreigeben, sonst lieferte/site.webmanifestdie 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,guardkann Schritte ausblenden), resumierbarer Fortschritt (OnboardingProgress). F2 — generalisiertesObjectReviewStatus(5 Zustände inkl.zurueckgewiesen) als wiederverwendbares Review-Enum; RBAC-Rechtvalidate_objects+ klonbare Rolleexternal_validator; Wizard-State-Machine mit Rechte-Trennung: Bearbeiter (onboarding:use) advance/rework/reset, Validator (validate_objects) validieren/zurückweisen (mit Begründung). „Weiter"-Gate: nurvalidiertzählt. Migrationobject_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) leitetFLAG_HIGH_PROTECTION/FLAG_VERY_HIGH_PROTECTIONzentral ab (Wizard/Provisioning seeden daraus, nicht mehr aus dem Fragebogen); Schutzbedarf-FrageQ-FEAT-01+ Regelnfeat01-*entfernt. A2-2 — Scope-ObjektWizardScope+ Filter-Engine (src/lib/scope-filter.ts:activeRequirements/scopeSummary, client-safe): filtert die Anforderungen nach AL-Baseline,FLAG_INCLUDE_SHOULDund Prüfzielen; Grunddatenseed/scoping/c1-scope.json(412 Anforderungen, generiert viascripts/build-c1-scope.ts); ActionsaveScope(moduleGuard('onboarding')) am Wizard-Schritt „scoping"; Migrationwizard_scope; Testsscripts/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, Testsscripts/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, Testsscripts/test-maturity.ts), ModellControlAssessment(Migrationcontrol_assessments, RLS, inTENANT_MODELS) mit Belegstatus/Reifegrad-Bestätigung/Gap-Aufgaben; SoA-Kontext (src/server/soa-context.ts,actions/soa.ts), Control-Grunddatenseed/scoping/c5-controls.json. A6 — Standard-Risikokatalog (C4): globaler KatalogRiskCatalogEntry(Migrationrisk_catalog, kein RLS) + Import/Übernahme (prisma/import-risks.ts,/risks/catalog); A6-2 Standardmaßnahme→Aufgabe und Restrisiko-Akzeptanz (Migrationrisk_acceptance: Felderaccepted_at/by/rationaleanrisks, 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), Testsscripts/test-gap-consolidation.ts(keine neue Migration/Modell). - Dev B: F1 (Task-Objekt um Wizard-Felder
type/owner/dueDate/priority/status/resources/origin/linkserweitert, Migrationtasks_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 invariables.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); Testsscripts/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), ModellWizardFact(Migrationwizard_facts, RLS, inTENANT_MODELS); echte Schritte via Side-Effect-Importregister-steps.tsin die Registry eingeklinkt (überschreibt Platzhalter). Der Fragebogen schreibt nur Fakten/Flags, nie die gesperrten Zentralvariablen. B4 (Richtlinien-Import/Upload) — indevintegriert (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 intoggleTenantModule); 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 VorlagenP01_Prototypenschutz+VA-20(Kap. 8.x) undD01_Datenschutz(9.x) + 5mapping.json-Anforderungen (condition-gated per Prüfziel-Flag),_verify.py= OK. Keine neue Migration (nur String-Status/Seed-Content). B5 (Umsetzungshinweise C6) — indevintegriert, Gate grün: B5-1 DatenmodellImplementationHint(Migrationimplementation_hints; globaler C6-Katalog, identisch je Mandant → keintenantId/RLS) + C6-Import (~397 Hinweis-Blöcke viaprisma/import-hints.ts/scripts/import-c6-hints.ts); B5-2 kontextsensitives Umsetzungshinweis-Panel (/policies/hints,src/server/actions/hints.ts), aus/policiesverlinkt. Prüfziele Single Source (Follow-up zu A2): der context-Step seedet die Prüfziele ausWizardScopestatt aus einer zweiten Quelle (Doppelquelle behoben;feature-rules.ts/facts.ts/questions.ts,test-rules.tsangepasst). B6 — Vorlagenpaket-Versionierung + kontrollierte Diff-Übernahme: ModellPolicyPackageState(Migrationpolicy_package_state, RLS, inTENANT_MODELS) hält je Mandant Paket-Version/-Stand;stampPackageState(inprisma/import-policies.ts) beim Import; Updates-Ansicht/policies/updateszeigt/übernimmt Änderungen kontrolliert. B7-1 — Assessment-Readiness-Dashboard als Wizard-Schritt 9 „readiness" (src/lib/readiness.ts, Testsscripts/test-readiness.ts) mit Interpretationstexten (C9 §1/§2). B7-2 — VDA-ISA-Katalog-Export (CSV) + Management-Zusammenfassung (C9 §3/§4): Export-Routeonboarding/export/route.ts, Export-Enginesrc/lib/export/vda-isa.ts+src/server/export-context.ts, Zusammenfassungs-Seiteonboarding/summary, Testsscripts/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
devkonsolidiert (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, wennscripts/sync-role-permissions.tsgegen 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 immigrate-Init-Job (docker-compose.coolify.yml, direkt nachprisma 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, wasdevergä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 keinentenantId. - Superadmin-Bereich liegt unter
src/app/(platform)/…(Layout erzwingt Plattform-Session + ggf. MFA); Mandanten-App untersrc/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/mustChangePasswordautoritativ aus der DB →/change-passwordbzw. Abmelde-Screen bei deaktivierten Konten. - Passwort-Policy:
src/lib/password-policy.ts(reine Validierung, client-safe) +src/server/password.ts(Argon2id + Generator); QuelleTenantSettings.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(alsprebuildverdrahtet): jede Datei insrc/server/actions/muss dort eingetragen sein (Modul-Key oderEXEMPT), sonst failt der Build. → Neue Action-Datei? Dort eintragen. - Neues Modul
tasksinsrc/lib/modules.ts(für Bestands-Tenants per Default aktiv, da fehlendeTenantModule-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, Komponentenuser-table.tsx/user-forms.tsx/role-manager.tsx. - Aufgaben/Freigabe:
src/app/(app)/tasks/,src/server/actions/tasks.ts(+submitForApprovalinpolicies.ts); Dashboard-Kachel indashboard/page.tsx. - Register:
src/components/generic-register.tsx,src/server/actions/register.ts, Definitionen inprisma/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_INCLUDEinsrc/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_INCLUDEinsrc/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 vonresolveLink("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-ServiceimportPolicyPackage+ PlattformimportPolicyPackageForTenant) undpolicy-upload.ts(uploadOwnPolicy); Aktivierungs-Trigger intoggleTenantModule(admin.ts); Storage-Adaptersrc/server/storage/adapter.ts(Stub, Epic S1); UIsrc/app/(app)/policies/page.tsx(Buttons + Report-Banner) +src/app/(app)/policies/upload/page.tsx. Eigene Uploads liegen im NamensraumEIG-*(außerhalb des Paket-Namensraums → vom Re-Import unberührt). - Branding (in
dev): Tokens insrc/app/globals.css(--brand-*/--ui-*) mit JS-Pendantsrc/lib/brand.ts; Komponentensrc/components/brand/{certvia-logo,tenant-brand,powered-by-gefim}.tsx; Assetspublic/assets/logo/,public/favicon/,public/site.webmanifest; Dokument-/Mail-CDsrc/lib/{document-brand,email-brand}.ts; Dokudocs/BRANDING-CERTVIA.md(+ Design-Paket unterdocs/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 Änderungenpython3 _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-datasourcefremde, vom anderen Branch angewandte Schemaänderungen (z. B. wollte A1-1 fälschlich Dev Bstasks-Spaltenlinks/origin/priority/resourcesdroppen). 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, meldetmigrate 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 beitasks_wizard_fields(…103650in DB →…104412committet). - Zentrale Variablen (Organisation/Rollen/Schutzbedarf-Flags,
src/lib/policy-variables.ts) sind im Richtlinien-Editor gesperrt und werden serverseitig geblockt — nur in/settingspflegbar. - 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 generischeManagedRegister-Modell.importPoliciesarchiviert 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_REGISTERSinimport-managed.tslöscht Dokument +ManagedRegister+ Zeilen idempotent beim Import). Ihre{{LINK:REG-…}}in den Seed-Texten bleiben unverändert — nurresolveLink(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 ausGENERIC_REGISTERSentfernen, inRETIRED_REGISTERSeintragen,resolveLink-Fall ergänzen. - Software/Projekte als Asset-Typen: folgen exakt dem bestehenden Muster von SUPPLIER/IT_SERVICE (Asset + 1:1-Profil,
refNoje Mandant, Cockpit-Popups auf/suppliersund/assets, backHref-gesteuert). Neue Action-Dateien (software.ts→suppliers,projects.ts→assets) sind inscripts/check-module-guards.tsregistriert.
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):
devdeployen; die 7 neuen Migrationen laufen automatisch im Init-Container (prisma migrate deploy), Bootstrap-Seed legt Demo-Daten + Demo-Approver an. Die letzte Migration ändert dasAssetType-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äuftscripts/bootstrap-admin.ts(Erst-Superadmin implatformAdmin-Store + Mandant/Mandanten-Admin, idempotent), gesteuert perBOOTSTRAP_*-Env immigrate-Job derdocker-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ührtprebuild-Guard-Check aus); für das Vorlagenpaketpython3 seed/isms-vorlagenpaket-v2/_verify.py(OK = rückstandsfrei, Mapping sauber). - Sync-Status (Gitea aktuell offline): lokaler
devist 27 Commits vororigin/dev(inkl. der beiden Dev-B-Integrations-Merges F3+B2 und B3). Sobald Gitea erreichbar ist: erstgit fetch, prüfen oborigin/devnoch9b479abist (bzw. ob jemand anderes gepusht hat), danngit 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
- ISB-Freigabe der neuen/angepassten Richtlinien- und VA-Texte (fachlich, inkl. P01/D01/VA-20 aus B4-3).
- 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. - 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). - Paket 4 — SMTP + E-Mail-Einladungs-/Aktivierungs-/Reset-Flow (Aktivierung ist bereits gekapselt).
- Tenant-weite MFA-Pflicht scharfschalten (Enrollment-Gate).
- 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 → keinup. - Root Cause:
src/server/auth.tserzeugte den Konstant-Zeit-Dummy-Hash auf Modulebene (const DUMMY_HASH_PROMISE = hashPassword(...)).hashPassword()liest denPASSWORD_PEPPER, der zur Docker-Build-Zeit fehlt (nurDATABASE_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.envden Pepper liefert (im Image per.dockerignoreausgeschlossen). - Fix: Dummy-Hash lazy + memoisiert (
dummyHash()), Aufruf erst zur Laufzeit inverifyIdentityPassword— gleiches Muster wieassertSecureEnv()/pepper(). Konstant-Zeit-Verhalten (F-05) bleibt erhalten. - Verifiziert:
docker build --target buildergrün OHNE gesetztenPASSWORD_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-workerohne IMAP-Konfigprocess.exit(1)+restart: unless-stopped→ Crash-Loop → Coolify wertet Deploy als unhealthy →compose down(auch der gesunden App). - Fix:
scripts/incident-inbound-worker.tsidlet 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 Konzeptsdocs/KONZEPT-garage-migration.md(4 Lanes), keine Datenmigration (nur Test-Instanzen, Neu-Deploy). - Infra:
garage-Service ersetztminioindocker-compose.yml+docker-compose.coolify.yml;deploy/garage.toml(Single-Node,s3_region=us-east-1, RPC-/Admin-Token); Volumesgarage_meta(kritisch, ins Backup) +garage_data. - Provisioning:
scripts/garage-provision.ts— idempotenter Init-Job (Layout + Bucketisms-documents+ Key-Import aus S3_*-Env + Rechte). - App-Code:
ensureBucket()inadapter.ts+backup-store.tsnur noch verifizierend (HeadBucket), kein S3-CreateBucketmehr (Garage kann das nicht); fehlender Bucket → sprechender Konfigfehler statt stiller Heilung.CreateBucketCommand-Imports entfernt. - Test/Doku:
scripts/test-garage-storage.ts(skippt ohneS3_ENDPOINT, echter E2E mit gesetzten S3_*), Runbook indocs/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_TOKENliteral 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.tomlals Verzeichnis an (Storage-Behandlung) → Garage bekommt/etc/garage.tomlals Ordner. - Fix: Neue Dockerfile-Stage
garage(FROM dxflrs/garage:v1.2.0+COPY deploy/garage.toml /etc/garage.toml);docker-compose.coolify.ymlnutztbuild: target garagestattimage:+ Bind-Mount. Secrets bleiben zur Laufzeit ausGARAGE_RPC_SECRET/GARAGE_ADMIN_TOKEN. Lokalesdocker-compose.ymlbehält den Bind-Mount (auf normalem Docker unkritisch). - Verifiziert:
docker build --target garage+ Start mit gültigem Secret → Daemon läuft,/garage statusExit 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
cb2bc0bAdmin-Action + UI (admin/[id]/page.tsx,actions/admin.ts, i18n de/en): ISO/TISAX pro Mandant nachträglich aktivieren/deaktivieren.1475193Abnahmetest-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 nachbiaStatus; 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 unterprocesses(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
interfacesbleibt zusätzlich erhalten.
Prozess-Abhängigkeiten (strukturiert: „benötigt" / „wird benötigt von")
- Neues Modell
ProcessDependency(sourcebenötigttarget, optionalenote), Migration20260907084110_add_process_dependenciesinkl. RLS (tenant_isolation + FORCE), Unique(source, target), FK-Cascade beim Prozess-Löschen; inTENANT_MODELSaufgenommen. Auswahl innerhalb des Mandanten. - Server-Actions in
actions/processes.ts:addProcessDependency(idempotenter Upsert, Selbstbezug ausgeschlossen, beide Prozesse müssen existieren) undremoveProcessDependency;deleteProcesslöst Kanten in beide Richtungen. Beidebia: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" independency-graph.tsx(blendet Assets aus → reine Prozess-Abhängigkeitskarte). Verifiziert amdemo-Mandanten: IT-Betrieb als SPOF (3) korrekt erkannt.
Datenimport (Excel) — zurückgezogen
- Das zuvor auf
devergänzte Excel-Datenimport-Feature (geteilte Logiksrc/server/import/, UI/settings/import, Ops-Skript, TabelleImportRef/Migration20260904081701_add_import_refs, KonzeptKONZEPT-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.mdbleibt 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.tszod-Schema um „5";settings/risk-criteria/page.tsx+onboarding/steps/criteria/step.tsxeditorDims 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.