Commit Graph

10 Commits

Author SHA1 Message Date
duffyduck ab971618d5 factory-import: --save-as-builtin Flag + README-Überarbeitung
Schließt die Lücke „nach Import landet die ZIP nicht im Image-Default":

  ./factory-import.sh --save-as-builtin
  → entpackt die ZIP nach erfolgreichem DB-Import zusätzlich in
    backend/factory-defaults/ (alter Inhalt vorher aufgeräumt, README.md
    und .gitkeep bleiben). Beim nächsten Image-Build sind die Defaults
    drin und seeden frische VMs automatisch.

README-Abschnitt „Factory-Defaults" komplett überarbeitet:
- Drei Transport-Pfade explizit erklärt (laufende DB / Drop-Box / Image)
- HTML-Standardtexte + AppSetting-Whitelist dokumentiert
- Auto-Seed-Verhalten + Berechtigungen aktualisiert
- Typische Workflows als End-zu-End-Sequenz inkl. scp-Sync

Live verifiziert: STALE_FILE.txt im backend/factory-defaults/ wurde beim
--save-as-builtin sauber entfernt, README.md blieb erhalten, Subfolder neu
befüllt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 20:04:02 +02:00
duffyduck 4407bbfbb8 factory-defaults: CLI-Sync zwischen dev und prod
Zwei kleine Bash-Wrapper im Repo-Root, die den vorhandenen Export- und
Import-Endpoint per curl ansteuern und damit den Hin- und Her-Transfer von
Stammdaten + HTML-Templates zwischen Instanzen ohne Browser ermöglichen.

  ./factory-export.sh                    # ZIP nach factory-exports/
  ./factory-import.sh                    # nimmt jüngste ZIP automatisch
  ./factory-import.sh path/zur.zip       # explizit

Konfigurierbar via OPENCRM_URL / OPENCRM_EMAIL / OPENCRM_PASSWORD;
ohne PASSWORD wird interaktiv abgefragt.

Workflow: prod erweitert Anbieter → ./factory-export.sh → scp → dev
./factory-import.sh – funktioniert in beide Richtungen.

`factory-exports/` ist gitignored (nur .gitkeep getrackt).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 19:51:19 +02:00
duffyduck 365c7994d5 factory-defaults: builtin-Werkseinstellungen beim Auto-Seed einspielen
Neue VMs sollen direkt mit den im Repo abgelegten Stammdaten +
Auftragsvorlagen + HTML-Templates hochkommen, ohne dass man jedes Mal
manuell ein ZIP hochlädt.

- Dockerfile: kopiert backend/factory-defaults nach
  /app/factory-defaults-builtin und backend/scripts nach /app/scripts
- seed-factory-defaults.ts: ROOT-Pfad über FACTORY_DEFAULTS_DIR überschreibbar
- entrypoint.sh: nach erfolgreichem Auto-Seed läuft `tsx
  scripts/seed-factory-defaults.ts` mit FACTORY_DEFAULTS_DIR auf den
  builtin-Pfad. Trigger NUR bei frischer DB (RAN_SEED=true), bestehende
  Installs werden nie nachträglich überschrieben.

`backend/factory-defaults/*` bleibt gitignored – Inhalte legt jeder
Operator-User selbst lokal ab (z.B. via Export-ZIP entpacken), sie landen
beim nächsten Container-Build im Image.

Live verifiziert: frischer Container mit RUN_SEED=true zieht 10 Anbieter,
4 Tarife, 18 Kündigungsfristen, 18 Laufzeiten, 8 Kategorien, 2 PDF-Vorlagen
und 2 HTML-Templates ein; PDFs landen mit eindeutigem Suffix in uploads/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 19:41:16 +02:00
duffyduck 2c7a87ccd3 factory-defaults: HTML-Templates + Import über UI
Erweitert das bestehende Factory-Defaults-Bundle um vier HTML-Standardtexte
(Datenschutzerklärung, Impressum, Vollmacht-Vorlage, Website-Datenschutz)
und ergänzt den bisherigen CLI-Only-Import um einen Upload-Pfad in der UI.

Backend:
- collectFactoryDefaults() zieht jetzt auch die Whitelist-AppSettings
- exportFactoryDefaults() legt sie als app-settings/app-settings.json ins ZIP
- importFactoryDefaults(buffer) liest die ZIP idempotent ein – upserts pro
  Kategorie, Whitelist-Filter für AppSettings, Anti-Zip-Slip durch basename
  beim PDF-Lookup
- POST /api/factory-defaults/import (multer memoryStorage, max 50 MB,
  settings:update)
- seed-factory-defaults.ts (CLI) gleichermaßen um seedAppSettings() erweitert

Frontend:
- Import-Card in FactoryDefaults.tsx: Datei-Upload statt CLI-Anleitung
- Erfolgs-Box mit Counts pro Kategorie + Warnings (z.B. fehlende PDFs im ZIP)
- Preview zeigt jetzt auch die Anzahl HTML-Templates

Live verifiziert: Round-Trip Export → DELETE privacyPolicyHtml → Import →
Wert (13.6 KB) wieder vollständig hergestellt, Audit-Log zeigt EXPORT +
UPDATE-Eintrag mit Detail-Counts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 19:26:33 +02:00
duffyduck 45f63d1c48 docs: User-DSGVO-/Entwickler-Zugriff-Fix in Erledigt-Liste
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:27:18 +02:00
duffyduck 4201a90fd0 docs: HTTPS_ENABLED-Flag in Erledigt-Liste dokumentieren
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:00:56 +02:00
duffyduck 27a0fdbc45 db: Prisma-Migrations-System statt db push (datenerhaltend)
`db push --accept-data-loss` konnte bei Schema-Änderungen still Daten verlieren
(Renames, Type-Changes, NOT NULL ohne Default). Umstellung auf versionierte
Migrations:

- 0_init aus aktuellem Schema generiert (alte gedriftete Migrations entfernt)
- entrypoint: Auto-Baseline für bestehende DBs ohne `_prisma_migrations`,
  dann `migrate deploy` (idempotent, kein Daten-Loss)
- npm run schema:sync: legt automatisch eine Migration mit Zeitstempel an
  (`prisma migrate dev --name auto_<ts>`)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 17:02:35 +02:00
duffyduck 0cf3dd6a7b Security-Hardening Runde 10: Security-Monitoring + Alerting
Defense-in-Depth für alles, was in den ersten 9 Runden nicht durch Code
verhindert wurde: zumindest gesehen + alarmiert werden.

📊 SecurityEvent-Tabelle (Prisma)
- Type/Severity/IP/User/Endpoint + Indexen für Filter+Threshold-Detection
- Trennt sich vom AuditLog: AuditLog ist forensisch + hash-gekettet,
  SecurityEvent ist optimiert für Realtime-Alerting + Aggregation.

🪝 Hooks an kritischen Stellen
- Login (Success/Failed) – auth.controller
- Logout, Password-Reset (Request + Confirm) – auth.controller
- Rate-Limit-Hit – middleware/rateLimit
- IDOR-403 – utils/accessControl (canAccessCustomer / canAccessContract)
- SSRF-Block – emailProvider.controller (test-connection + test-mail-access)
- JWT-Reject (alg=none, expired, manipuliert) – middleware/auth

🚨 Threshold-Detection + Alerting (securityAlert.service.ts)
- Cron jede Minute: prüft Brute-Force-Patterns je IP
  - 10× LOGIN_FAILED in 60 min  → CRITICAL Brute-Force-Verdacht
  -  5× ACCESS_DENIED in 5 min  → CRITICAL IDOR-Probing-Verdacht
  -  3× SSRF_BLOCKED in 60 min  → CRITICAL SSRF-Probing
  -  3× TOKEN_REJECTED HIGH in 5 min → CRITICAL JWT-Manipulation
- CRITICAL-Events: Sofort-Alert per E-Mail (debounced)
- Cron stündlich: Digest mit HIGH+MEDIUM-Events (wenn aktiviert)
- Sofort-Alert + Digest laufen über System-E-Mail-Provider
  (gleicher Pfad wie Geburtstagsgrüße, Passwort-Reset)

🖥 Frontend: Settings → "Sicherheits-Monitoring"
- Alert-E-Mail-Adresse + Digest-Toggle
- Test-Alert-Button + Digest-jetzt-Button
- Stats-Cards pro Severity (CRITICAL/HIGH/MEDIUM/LOW/INFO)
- Filter (Type/Severity/Search/IP) + Pagination
- Auto-Refresh alle 30 s
- Verlinkt aus Settings-Übersicht (settings:read Permission)

🧪 Live-verifiziert
- Login-Fehlversuch → LOGIN_FAILED Event
- Portal probt 4× fremde Customer-IDs → 4× ACCESS_DENIED
- SSRF-Probe (169.254.169.254) → SSRF_BLOCKED Event
- 12× LOGIN_FAILED simuliert → Cron erzeugt CRITICAL nach ≤60s
- CRITICAL-Sofort-Alert binnen 30s zugestellt
- Test-Alert-Button: E-Mail zugestellt
- Hourly-Digest mit 5 Events: E-Mail mit Tabelle zugestellt

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 09:25:47 +02:00
duffyduck 45fe270a38 Security-Hardening Runde 9: Diminishing returns
Letzte Runde – nichts Kritisches mehr gefunden, was den Aufwand wert
wäre. Diminishing returns sind erreicht.

🔧 npm audit fix
- 9 Vulnerabilities → 1 (lodash, path-to-regexp, undici, minimatch
  transitiv geupdatet via package-lock.json).
- Verbliebene nodemailer-Vuln braucht Major-Update v6→v8 (breaking).
  Wir setzen die betroffenen Felder (envelope.size, transport name)
  nicht aus User-Input – als v1.1-Item dokumentiert.

🔍 Audit-Log-Hash-Chain
- War vor Runde 9 invalid (~350 Einträge) durch frühere Schema-
  Migrationen, nicht durch Manipulation.
- rehashAll repariert; integrity-check verifiziert die Chain wieder.
  Verfahren funktioniert – wäre eine echte Manipulation, würde sie
  auffallen.

🟢 Geprüft + sauber (kein Bug)
- From-Header-Injection in smtpService (Stage 3 deckt das schon ab).
- Concurrent Password-Reset Token-Reuse (atomares Delete).
- Frontend localStorage Token-Pattern (Standard-SPA, XSS-resistent durch
  DOMPurify in allen Render-Stellen).

📋 Bewusst NICHT gemacht (in HARDENING.md dokumentiert)
- Authenticated Rate-Limit (Aufgabe vom Reverse-Proxy).
- JWT in HttpOnly-Cookie statt localStorage (CSRF-Token-System nötig).
- nodemailer Major-Update.

Der Block "Wann ist dicht dicht?" in SECURITY-HARDENING.md formuliert
die Endkriterien: 5 Punkte erfüllt, was bleibt sind zero-days +
Server-Misconfig in Production – beides nicht durch Code-Änderung
lösbar.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 08:47:20 +02:00
duffyduck 73f271ae03 docs: todo.md von backend/ nach docs/ verschoben
todo.md gehört thematisch zur Doku, nicht zum Backend-Code.
Interne Pfade (TESTING.md, SECURITY-*.md) auf relative ./-Pfade angepasst.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 08:23:27 +02:00