/** * Pentester-Hilfsscript (Pentest R152): legt einen KONTROLLIERTEN „getarnten" * Upload an, um den Magic-Byte-Mismatch→attachment-Zweig des Download-Endpoints * live zu belegen. * * Was passiert: * - Es wird eine dedizierte TEST-Bankkarte (accountHolder-Marker) angelegt – * ECHTE Kundendaten werden NICHT angefasst/überschrieben. * - An diese Karte wird eine Datei mit `.pdf`-Endung gehängt, deren Inhalt * aber ein SVG mit `; function uploadsDir(): string { return path.join(process.cwd(), 'uploads', UPLOAD_SUBDIR); } function absoluteFilePath(): string { return path.join(uploadsDir(), FILENAME); } async function create(customerIdArg?: number): Promise { // Ziel-Customer bestimmen (Default: erster vorhandener Kunde). const customer = customerIdArg ? await prisma.customer.findUnique({ where: { id: customerIdArg }, select: { id: true, customerNumber: true } }) : await prisma.customer.findFirst({ orderBy: { id: 'asc' }, select: { id: true, customerNumber: true } }); if (!customer) { console.error('Kein Kunde gefunden – bitte zuerst einen Kunden anlegen oder --customer angeben.'); process.exit(1); } // Getarnte Datei schreiben. fs.mkdirSync(uploadsDir(), { recursive: true }); fs.writeFileSync(absoluteFilePath(), SVG_XSS, 'utf8'); // Test-Bankkarte anlegen oder wiederverwenden (idempotent, kein Duplikat). const existing = await prisma.bankCard.findFirst({ where: { accountHolder: MARKER } }); const card = existing ? await prisma.bankCard.update({ where: { id: existing.id }, data: { customerId: customer.id, documentPath: RELATIVE_PATH } }) : await prisma.bankCard.create({ data: { customerId: customer.id, accountHolder: MARKER, iban: 'DE00000000000000000000', documentPath: RELATIVE_PATH, description: 'Pentest R152 – getarnte Datei (SVG-als-PDF). Nach Test via cleanup entfernen.', }, }); const head = Buffer.alloc(12); const fd = fs.openSync(absoluteFilePath(), 'r'); fs.readSync(fd, head, 0, 12, 0); fs.closeSync(fd); console.log('=== Magic-Byte-Test angelegt ==='); console.log('Test-Customer :', customer.customerNumber, `(id ${customer.id})`); console.log('Test-Bankkarte : id', card.id, `– accountHolder="${MARKER}"`); console.log('Datei (Disk) :', absoluteFilePath()); console.log('Erste 12 Bytes :', JSON.stringify(head.toString('latin1')), '(kein PDF/PNG/JPEG/GIF/WebP-Magic)'); console.log('documentPath :', RELATIVE_PATH); console.log(''); console.log('--- So testen (Token des berechtigten Users anhängen) ---'); console.log(` curl -sSI "https:///api/files/download?path=${RELATIVE_PATH}&disposition=inline&token="`); console.log(' Erwartet: 200, Content-Disposition: attachment, X-Content-Type-Options: nosniff'); console.log(' (NICHT inline) + Backend-Log "Magic-Byte-Check fehlgeschlagen".'); console.log(` Ohne &disposition=inline: ebenfalls attachment.`); console.log(' Als fremder Portal-User: 403 (Ownership-Check).'); console.log(''); console.log('>>> Nach dem Test aufräumen: npx tsx scripts/seed-magic-byte-test.ts cleanup'); } async function cleanup(): Promise { let removed = 0; const cards = await prisma.bankCard.findMany({ where: { accountHolder: MARKER } }); for (const c of cards) { await prisma.bankCard.delete({ where: { id: c.id } }); removed++; } // Datei löschen (falls vorhanden). const abs = absoluteFilePath(); let fileDeleted = false; if (fs.existsSync(abs)) { fs.unlinkSync(abs); fileDeleted = true; } console.log('=== Cleanup ==='); console.log('Gelöschte Test-Bankkarten:', removed); console.log('Datei gelöscht :', fileDeleted ? abs : '(nicht vorhanden)'); } (async () => { const mode = process.argv[2] || 'create'; const custIdx = process.argv.indexOf('--customer'); const customerIdArg = custIdx >= 0 ? parseInt(process.argv[custIdx + 1], 10) : undefined; try { if (mode === 'create') { await create(Number.isFinite(customerIdArg as number) ? customerIdArg : undefined); } else if (mode === 'cleanup') { await cleanup(); } else { console.error(`Unbekannter Modus "${mode}". Nutze: create | cleanup`); process.exit(1); } } catch (err) { console.error('Fehler:', err instanceof Error ? err.message : err); process.exit(1); } finally { await prisma.$disconnect(); } })();