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
+9 -2
View File
@@ -204,7 +204,6 @@ const USER_UPDATABLE_FIELDS = [
'telegramUsername',
'signalNumber',
'roleIds',
'password', // nur Admin, wird im Service gehashed
// hasGdprAccess + hasDeveloperAccess sind keine User-Spalten der Service
// mappt sie auf die versteckten Rollen DSGVO/Developer (siehe
// setUserGdprAccess / setUserDeveloperAccess). Müssen aber auf der Whitelist
@@ -212,9 +211,17 @@ const USER_UPDATABLE_FIELDS = [
'hasGdprAccess',
'hasDeveloperAccess',
// Nicht: id, customerId, tokenInvalidatedAt, passwordResetToken, passwordResetExpiresAt
// Nicht: password wird über dedizierten Endpoint POST /users/:id/password
// gesetzt (Pentest Runde 12 (2026-05-18) MITTEL: generisches User-Update
// hatte password in der Whitelist, ein Admin konnte stillschweigend ohne
// dedizierten Audit-Trail Passwörter überschreiben).
] as const;
const USER_CREATE_FIELDS = USER_UPDATABLE_FIELDS;
// Bei CREATE braucht's das initial-Passwort
const USER_CREATE_FIELDS = [
...USER_UPDATABLE_FIELDS,
'password',
] as const;
/**
* Strippt HTML-Tags und Script-/Style-Inhalt aus einem String, damit ein