Pentest 24.6 INFO + 26.7 LOW: PENDING-Status sperren + documentPath-Validator
24.6 (Portal kann Consent auf PENDING zurücksetzen): - gdpr.controller updateCustomerConsent prüft jetzt explizit, dass der Portal-User nur GRANTED oder WITHDRAWN setzen kann. PENDING ist nur der initiale System-Status; ein Reset darauf hätte die DSGVO-Auswertung verfälscht. 26.7 (documentPath ohne Validierung): - Neuer Helper isValidDocumentPath + assertValidDocumentPath in utils/sanitize: nur /?uploads/<safe>, keine "..", keine javascript:/data:/vbscript:, kein HTML. - consent.service.updateConsent ruft den Assert auf – Defense-in- Depth gegen zukünftige Caller, die documentPath aus User-Input durchreichen könnten. - authorization.service.grantAuthorization analog. - Cleanup-Skript (prisma/cleanup-xss-and-mass-assignment) entfernt seine lokale Kopie der Path-Validierung und nutzt den shared Helper – Single Source of Truth. 27.1 (Altdaten in Staging-DB): - Cleanup-Skript läuft sowieso bei jedem Container-Start. Nina- Records mit "../../../etc/passwd" werden beim nächsten Restart genullt (oder verschwinden mit dem VM-Snapshot-Wechsel). Live-Test isValidDocumentPath: 13/13 OK – legitime Pfade durch, Traversal/JS-URI/HTML blockiert. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -225,6 +225,27 @@ export function validateContractDocumentType(raw: unknown): string {
|
||||
return canonical;
|
||||
}
|
||||
|
||||
// Pentest 26.7 LOW (defense-in-depth, 2026-06-02): documentPath wird
|
||||
// (außer beim Upload-Endpoint) NIE direkt aus User-Input übernommen.
|
||||
// Falls doch jemand auf die Idee kommt, das Feld irgendwo zu mappen,
|
||||
// fangen wir hier Path-Traversal / Javascript-URIs / HTML ab.
|
||||
// Spiegelt isValidDocumentPath aus prisma/cleanup-xss-and-mass-assignment.ts
|
||||
// 1:1 – Single Source of Truth für Lese- UND Schreibpfad.
|
||||
export function isValidDocumentPath(v: string | null | undefined): boolean {
|
||||
if (!v) return true; // null/leer ist OK – Feld bleibt einfach unbesetzt
|
||||
if (typeof v !== 'string') return false;
|
||||
if (v.includes('..')) return false;
|
||||
if (/(?:javascript|data|vbscript)\s*:/i.test(v)) return false;
|
||||
if (/<[a-z!/]/i.test(v)) return false;
|
||||
return /^\/?uploads\/[A-Za-z0-9._\-/]+$/.test(v);
|
||||
}
|
||||
|
||||
export function assertValidDocumentPath(v: string | null | undefined, fieldLabel = 'documentPath'): void {
|
||||
if (!isValidDocumentPath(v)) {
|
||||
throw new Error(`${fieldLabel} ist kein gültiger Upload-Pfad (erlaubt: /uploads/<safe>).`);
|
||||
}
|
||||
}
|
||||
|
||||
// Pentest 51.3 + 60.3 (MEDIUM, 2026-06-01): Telefon-/Vorwahl-Felder
|
||||
// dürfen NIE CRLF oder andere Control-Chars enthalten – sonst sind sie
|
||||
// ein Header-Injection-Vektor (Mail, HTTP), wenn der Wert mal in einen
|
||||
|
||||
Reference in New Issue
Block a user