const path = require('path'); const express = require('express'); const { safePathJoin, isPathSafe } = require('../utils/fileSecurityUtils'); const logger = require('../utils/logger'); /** * Create a secure static file serving middleware that prevents path traversal attacks * @param {string} basePath - The base directory to serve files from * @param {Object} options - Express static options * @returns {Function} - Express middleware */ function secureStatic(basePath, options = {}) { const normalizedBase = path.resolve(basePath); return (req, res, next) => { // Get the requested file path - remove leading slash for validation const requestedPath = req.path.startsWith('/') ? req.path.substring(1) : req.path; // Validate the path doesn't contain dangerous patterns if (!isPathSafe(requestedPath)) { logger.warn(`Potential path traversal attempt blocked: ${requestedPath}`); return res.status(403).json({ error: 'Access denied' }); } try { // Validate the full path is within the base directory const fullPath = safePathJoin(normalizedBase, requestedPath); // If validation passes, use express.static const staticMiddleware = express.static(normalizedBase, { ...options, // Disable directory listing for security index: false, // Don't allow dotfiles dotfiles: 'deny', setHeaders: (resp, filePath) => { // Preserve any caller-provided header logic (e.g. font caching). if (typeof options.setHeaders === 'function') options.setHeaders(resp, filePath); // SVGs are admin-uploadable (logos, favicon). Served from our // own origin, a malicious SVG opened directly could run embedded //