const rateLimit = require('express-rate-limit'); const helmet = require('helmet'); const { body, param, query } = require('express-validator'); // Rate limiting configuration const createRateLimit = (windowMs = 15 * 60 * 1000, max = 100) => rateLimit({ windowMs, max, message: 'Too many requests from this IP, please try again later.', standardHeaders: true, legacyHeaders: false, }); // API rate limits const apiLimiter = createRateLimit( parseInt(process.env.RATE_LIMIT_WINDOW_MS) || 15 * 60 * 1000, // 15 minutes parseInt(process.env.RATE_LIMIT_MAX_REQUESTS) || 100 ); // Stricter rate limit for uploads const uploadLimiter = createRateLimit( 15 * 60 * 1000, // 15 minutes 10 // 10 uploads per window ); // Security headers configuration const securityHeaders = helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], styleSrc: ["'self'", "'unsafe-inline'"], scriptSrc: ["'self'", "https://cdn.jsdelivr.net", "https://unpkg.com"], imgSrc: ["'self'", "data:", "blob:"], connectSrc: ["'self'"], fontSrc: ["'self'"], objectSrc: ["'none'"], mediaSrc: ["'self'"], frameSrc: ["'none'"], }, }, crossOriginEmbedderPolicy: false, // Required for Three.js }); // Input validation schemas const fileValidation = [ body('description').optional().isLength({ max: 500 }).trim().escape(), body('tags').optional().isLength({ max: 200 }).trim(), body('printSettings').optional().isJSON(), body('dimensions').optional().isJSON(), ]; const fileIdValidation = [ param('id').isInt({ min: 1 }).withMessage('Valid file ID required') ]; const searchValidation = [ query('search').optional().isLength({ max: 100 }).trim().escape(), query('limit').optional().isInt({ min: 1, max: 100 }).withMessage('Limit must be between 1 and 100'), query('offset').optional().isInt({ min: 0 }).withMessage('Offset must be non-negative') ]; module.exports = { apiLimiter, uploadLimiter, securityHeaders, fileValidation, fileIdValidation, searchValidation };