Pentest 2026-05-20 LOW/INFO Sammelfix

27.1 Path-Traversal-Strings in DB:
- cleanupConsents validierte documentPath zuvor nur per stripHtml,
  ließ "../../../etc/passwd" durch. Neuer isValidDocumentPath-Check
  akzeptiert nur "/uploads/<safe>", alles andere → NULL.
- cleanupDocumentPaths scannt fünf weitere Tabellen (BankCard,
  IdentityDocument, Invoice, RepresentativeAuthorization nullable;
  ContractDocument NOT NULL → nur Report).

Orphaned User:
- reportOrphanedUsers warnt beim Container-Start vor User ohne
  Rollenzuordnung (im Permission-System unsichtbar). Löschen nicht
  automatisch wegen False-Positive-Risiko.

Seed-PW-Policy:
- generateInitialPassword() nutzte Math.random() (vorhersagbar).
  Jetzt crypto.randomInt() für Pick + Fisher-Yates-Shuffle.

PUT /users/:id mit permissions / password:
- Vorher silent-drop durch Whitelist + HTTP 200, Caller glaubte
  faelschlich, Werte waeren uebernommen. Jetzt HTTP 400 mit
  konkreter Hilfe-Message.

/api/health ohne Auth:
- Pentest-Befund INFO: bewusst so, Container-Healthcheck und
  Reverse-Proxy pingen ohne Bearer-Token. Antwort liefert nur
  {status,timestamp} – keine Version, kein DB-Status, kein
  Info-Leak. Comment im Code dokumentiert die Entscheidung.

Live-verifiziert auf dev: alle fuenf Findings durchgetestet,
jeweils mit dirty Input → erwartete Sanitization/Antwort.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-20 07:49:06 +02:00
parent adc3b70492
commit 8e48d3b432
5 changed files with 184 additions and 13 deletions
+25 -3
View File
@@ -81,10 +81,32 @@ export async function createUser(req: Request, res: Response): Promise<void> {
export async function updateUser(req: Request, res: Response): Promise<void> {
try {
const userId = parseInt(req.params.id);
// `permissions` und `password` darf der generische Update nicht
// entgegennehmen. Vorher landeten sie auf dem Floor (Whitelist-Drop),
// der Caller bekam aber 200 zurück und glaubte fälschlich, die Werte
// wären übernommen worden. Stattdessen sofort 400, damit Tooling /
// Client den Fehler sieht. (Pentest 2026-05-20)
// - permissions kommen aus Rollen (PUT roleIds bzw. die DSGVO-/
// Developer-Checkboxen) und können nicht direkt am User hängen.
// - password wird über POST /users/:id/password gesetzt
// (eigene Komplexitäts-Validierung + Audit-Trail).
const body = req.body || {};
const forbidden = ['permissions', 'password', 'passwordHash'];
const offending = forbidden.filter((k) => k in body);
if (offending.length > 0) {
res.status(400).json({
success: false,
error: `Felder nicht erlaubt: ${offending.join(', ')}. ` +
(offending.includes('permissions')
? 'Permissions werden über roleIds / hasGdprAccess / hasDeveloperAccess gesteuert. '
: '') +
(offending.includes('password') || offending.includes('passwordHash')
? `Passwort über POST /users/${userId}/password setzen.`
: ''),
} as ApiResponse);
return;
}
// Whitelist: nur erlaubte Felder aus req.body übernehmen (Mass-Assignment-Schutz)
// password ist NICHT in der Whitelist generisches Update darf kein
// Passwort setzen. Dafür gibt es POST /users/:id/password mit eigenem
// Audit-Eintrag (Pentest Runde 12, MITTEL).
const data = pickUserUpdate(req.body);
// Vorherigen Stand laden für Audit inkl. Rollen, damit hasGdprAccess /