Security-Hardening Runde 15: Pentest Runde 12 Folge-Fixes

M2-Reste – XSS-Strings + Mass-Assignment-Settings noch in DB:
Idempotentes Cleanup-Script prisma/cleanup-xss-and-mass-assignment.ts.
Strippt HTML aus Customer/User-String-Feldern, entfernt AppSettings
ohne Whitelist-Eintrag. Wird im entrypoint.sh nach Migrations + Seed
einmalig pro Container-Start ausgeführt.

User-Update + password-Feld:
password aus USER_UPDATABLE_FIELDS raus (CREATE behält es), neuer
dedizierter Endpoint POST /api/users/:id/password mit Audit-Log
"Passwort … durch Admin gesetzt" und Komplexitäts-Check.

JS-Runtime-Fehler-Leak:
ORM_LEAK_PATTERNS um TypeError/ReferenceError/SyntaxError/RangeError +
"Cannot read properties of undefined/null" + "is not a function/
defined" erweitert. Greift im globalen res.json()-Wrapper.

POST /contracts substring-Crash:
Controller validiert type/customerId, sonst 400. generateContractNumber
fängt nullish type ab (Fallback "CON").

Seed-Admin-Passwort:
Default "admin" verletzte 12-Zeichen-Policy. Jetzt 16-char
Zufallspasswort (alle 4 Klassen garantiert via Fisher-Yates) oder per
SEED_ADMIN_PASSWORD-ENV überschreibbar. BCRYPT-Cost 12 (war 10).
Passwort wird einmalig in stdout ausgegeben mit Warnung.

AppSettings-Whitelist: companyName + defaultEmailDomain ergänzt
(kamen aus seed.ts, in 1. Whitelist vergessen).

Live-verifiziert:
- POST /contracts {} → 400 "Vertrags-Typ erforderlich" (vorher
  TypeError-Stack)
- PUT /users/6 {password:"HackerPW2026!"} → 200 aber Login mit altem
  PW geht weiter
- POST /users/6/password mit "kurz" → 400 mit Komplexitäts-Fehlern
- Cleanup-Script: planted XSS bereinigt, hackerSetting+debugMode
  entfernt, idempotenter Re-Lauf

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-18 15:09:13 +02:00
parent d545790a69
commit cf8c6c84c2
11 changed files with 254 additions and 17 deletions
+33
View File
@@ -97,6 +97,39 @@ isolierte Instanz (keine Multi-Tenancy im Code), Provisioning + Abrechnung
## ✅ Erledigt
- [x] **🚨 Pentest Runde 12 Folge-Fixes: XSS-Reste, User-PW-Endpoint, JS-Error-Leak, Seed-PW**
- **M2-Reste (XSS-Strings noch in DB)**: neues idempotentes Script
`prisma/cleanup-xss-and-mass-assignment.ts` läuft beim
Container-Start. Strippt HTML aus Customer/User-String-Feldern;
entfernt AppSettings, deren Key nicht in `ALLOWED_SETTING_KEYS`
steht. Mehrfacher Aufruf ändert nichts.
- **User-Update akzeptierte `password`-Feld**: stillschweigend ohne
dedizierten Audit-Eintrag. Jetzt: `password` aus
`USER_UPDATABLE_FIELDS` raus (CREATE behält es weiterhin); neuer
Endpoint `POST /api/users/:id/password` mit eigenem Audit-Log
"Passwort … durch Admin gesetzt", Komplexitäts-Check inklusive.
- **JS-Runtime-Fehler leakten weiter**: ORM-Leak-Patterns erweitert
um `TypeError`, `ReferenceError`, `SyntaxError`, `RangeError`,
"Cannot read propert(y|ies) of (undefined|null)", "is not a
function", "is not defined". Greift im globalen
`res.json()`-Wrapper.
- **POST /contracts substring-Crash**: defensiv `type` fehlt →
400 mit klarer Meldung; `generateContractNumber()` fängt auch
leere/null type ab (Fallback "CON").
- **Seed-Admin-Passwort "admin" verletzte Policy**: jetzt
16-Zeichen-Zufallspasswort beim Seed (mit allen 4 Klassen
garantiert) oder via `SEED_ADMIN_PASSWORD`-ENV überschreibbar;
BCRYPT-Cost auf 12 (war 10); Passwort wird **einmalig** beim
Seed in stdout ausgegeben mit Warnung.
- **AppSettings-Whitelist ergänzt**: `companyName`,
`defaultEmailDomain` (kommen aus seed.ts, waren in der ersten
Whitelist vergessen).
- **Live-verifiziert**: POST /contracts {} → klare 400 statt
JS-Crash; PUT /users/6 {password:...} ignoriert (Login mit
altem PW geht weiter); POST /users/6/password mit kurz → 400;
Cleanup-Script: 1 Customer bereinigt, 2 unbekannte AppSettings
entfernt (hackerSetting, debugMode), Re-Lauf → 0 Änderungen.
- [x] **🚨 Pentest Runde 11 Factory-Reset, Settings-Whitelist, Prisma-Leak, XSS-Strip**
- **C2 KRITISCH Factory Reset ohne Bestätigung**:
Eingeloggter Admin konnte mit leerem oder beliebigem Body