diff --git a/backend/scripts/seed-magic-byte-test.ts b/backend/scripts/seed-magic-byte-test.ts
new file mode 100644
index 00000000..e7e07376
--- /dev/null
+++ b/backend/scripts/seed-magic-byte-test.ts
@@ -0,0 +1,146 @@
+/**
+ * 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();
+ }
+})();