/** * File upload routes module. */ const express = require('express'); const path = require('path'); const fs = require('fs'); const multer = require('multer'); const { authMiddleware } = require('../middleware/auth'); const { uploadLimiter } = require('../middleware/rateLimit'); const { auditLog } = require('../auditLog'); const router = express.Router(); // File upload setup const uploadDir = path.join(__dirname, '..', 'data', 'uploads'); if (!fs.existsSync(uploadDir)) { fs.mkdirSync(uploadDir, { recursive: true }); } // VULN-08/09: Secure file upload const ALLOWED_MIMES = ['application/pdf', 'image/png', 'image/jpeg', 'image/gif', 'text/plain', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document']; const ALLOWED_EXTS = ['.pdf', '.png', '.jpg', '.jpeg', '.gif', '.txt', '.doc', '.docx']; const storage = multer.diskStorage({ destination: (req, file, cb) => cb(null, uploadDir), filename: (req, file, cb) => { const safeName = path.basename(file.originalname).replace(/[^a-zA-Z0-9._-]/g, '_'); const ext = path.extname(safeName).toLowerCase(); const safeExt = ALLOWED_EXTS.includes(ext) ? ext : '.bin'; const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); cb(null, uniqueSuffix + '-' + safeName.replace(/\.[^.]+$/, '') + safeExt); }, }); const upload = multer({ storage, limits: { fileSize: 10 * 1024 * 1024 }, fileFilter: (req, file, cb) => { if (ALLOWED_MIMES.includes(file.mimetype)) { cb(null, true); } else { cb(new Error('Dateityp nicht erlaubt. Erlaubt: PDF, PNG, JPG, GIF, TXT, DOC, DOCX.')); } }, }); // P12: Serve uploads as attachments (prevent XSS) - requires authentication router.use('/uploads', authMiddleware, express.static(uploadDir, { setHeaders: (res) => { res.setHeader('Content-Disposition', 'attachment'); res.setHeader('X-Content-Type-Options', 'nosniff'); }, })); // Upload endpoint - Punkt 23: Rate limited per user router.post('/', authMiddleware, uploadLimiter, upload.single('file'), (req, res) => { if (!req.file) { return res.status(400).json({ error: 'Keine Datei hochgeladen.' }); } const fileUrl = '/uploads/' + req.file.filename; auditLog(req.user?.id, 'file_upload', null, null, `Uploaded: ${req.file.filename} (${req.file.size} bytes)`); res.json({ filename: req.file.filename, originalname: req.file.originalname, url: fileUrl, size: req.file.size }); }); module.exports = router;