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:
@@ -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 /
|
||||
|
||||
Reference in New Issue
Block a user