/** * Vulnerable PDF Generation Server * * This server demonstrates a realistic scenario where pdfmake * is used server-side with user-controlled document definitions. * * VULNERABILITY: User-controlled URLs in docDefinition trigger SSRF */ import express from 'express'; import { createRequire } from 'module'; const require = createRequire(import.meta.url); const pdfmake = require('pdfmake'); const app = express(); app.use(express.json({ limit: '10mb' })); // Configure fonts - use standard PDF fonts (no external TTF needed) pdfmake.setFonts({ Helvetica: { normal: 'Helvetica', bold: 'Helvetica-Bold', italics: 'Helvetica-Oblique', bolditalics: 'Helvetica-BoldOblique' } }); /** * VULNERABLE ENDPOINT * * Accepts user-controlled docDefinition which can contain * arbitrary URLs in images, attachments, or files fields. * * pdfmake will fetch these URLs without any validation! */ app.post('/api/generate-pdf', async (req, res) => { console.log('[SERVER] Received PDF generation request'); try { const docDefinition = req.body; // Log what we received (for demo purposes) if (docDefinition.images) { console.log('[SERVER] Document contains images:', Object.keys(docDefinition.images)); } if (docDefinition.attachments) { console.log('[SERVER] Document contains attachments:', Object.keys(docDefinition.attachments)); } console.log('[SERVER] Creating PDF...'); // Add default font to docDefinition if not specified if (!docDefinition.defaultStyle) { docDefinition.defaultStyle = { font: 'Helvetica' }; } else if (!docDefinition.defaultStyle.font) { docDefinition.defaultStyle.font = 'Helvetica'; } // THIS IS WHERE THE VULNERABILITY IS TRIGGERED // pdfmake.createPdf() will call URLResolver.resolve() for any URLs // in images, attachments, or files - NO VALIDATION const pdfDoc = pdfmake.createPdf(docDefinition); // Get the PDF as buffer (pdfmake 0.3.x API) const buffer = await pdfDoc.getBuffer(); console.log('[SERVER] PDF generated successfully'); res.contentType('application/pdf'); res.send(buffer); } catch (error) { console.error('[SERVER] Error:', error.message); // Note: Even if PDF generation fails, the SSRF has already occurred! res.status(500).json({ error: error.message, note: 'SSRF may have still been triggered before this error' }); } }); // Health check app.get('/health', (req, res) => { res.json({ status: 'ok', vulnerable: true }); }); const PORT = 3000; app.listen(PORT, () => { console.log('='.repeat(50)); console.log('VULNERABLE PDF GENERATION SERVER'); console.log('='.repeat(50)); console.log(`[*] Listening on http://127.0.0.1:${PORT}`); console.log('[*] POST /api/generate-pdf - Accepts docDefinition'); console.log('[*] WARNING: This server is intentionally vulnerable!'); console.log(''); });