/**
* 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();
}
})();