diff --git a/backend/prisma/cleanup-xss-and-mass-assignment.ts b/backend/prisma/cleanup-xss-and-mass-assignment.ts index 2b7ad8ef..d369285b 100644 --- a/backend/prisma/cleanup-xss-and-mass-assignment.ts +++ b/backend/prisma/cleanup-xss-and-mass-assignment.ts @@ -92,11 +92,26 @@ const ALLOWED_CONSENT_SOURCES = new Set([ 'crm-backend', ]); +// Legitimer documentPath: relativer Pfad unter uploads/, keine ".."-Segmente. +// Schreibend werden Pfade ausschließlich vom multer-Upload erzeugt +// (server-kontrollierter Dateiname), bestehende Pentest-Hinterlassenschaften +// wie "../../../etc/passwd" oder "javascript:alert(1)" müssen aus der DB +// raus (Pentest 2026-05-20 LOW 27.1). +function isValidDocumentPath(v: string | null | undefined): boolean { + if (!v) return true; // null/leer ist OK + if (v.includes('..')) return false; + if (/(?:javascript|data|vbscript)\s*:/i.test(v)) return false; + if (/<[a-z!\/]/i.test(v)) return false; // HTML im Pfad + // erlaubt: "uploads/...", "/uploads/..."; keine Kontrollzeichen + return /^\/?uploads\/[A-Za-z0-9._\-\/]+$/.test(v); +} + async function cleanupConsents() { // version + documentPath: HTML strippen (waren ohne Validierung). // source: Whitelist erzwingen. + // documentPath zusätzlich gegen Pfad-Traversal absichern (27.1). let versionStripped = 0; - let pathStripped = 0; + let pathNulled = 0; let sourceFixed = 0; const consents = await prisma.customerConsent.findMany({ select: { id: true, source: true, documentPath: true, version: true }, @@ -107,9 +122,11 @@ async function cleanupConsents() { data.version = stripHtmlString(c.version); versionStripped++; } - if (c.documentPath && c.documentPath !== stripHtmlString(c.documentPath)) { - data.documentPath = stripHtmlString(c.documentPath); - pathStripped++; + if (c.documentPath && !isValidDocumentPath(c.documentPath)) { + // ".../etc/passwd", "