fix: Harden security - CORS, XSS, file uploads, error handling
- Restrict no-origin CORS bypass to development only - Activate xss-clean middleware for input sanitization - Add MIME type whitelist and filename sanitization to file uploads - Reduce project upload limit from 50MB to 20MB - Stop leaking stack traces in error responses Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
43
src/app.js
43
src/app.js
@@ -3,6 +3,7 @@ import morgan from 'morgan';
|
||||
import helmet from 'helmet';
|
||||
import cors from 'cors';
|
||||
import cookieParser from 'cookie-parser';
|
||||
import xssClean from 'xss-clean';
|
||||
|
||||
import { validateBody } from './middlewares/global/validateBody.js';
|
||||
import { notFound } from './middlewares/global/notFound.js';
|
||||
@@ -58,29 +59,38 @@ app.use(
|
||||
})
|
||||
);
|
||||
|
||||
// CORS configuration - allow local network access
|
||||
// CORS configuration
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
const corsOptions = {
|
||||
origin: (origin, callback) => {
|
||||
// Allow requests with no origin (mobile apps, curl, etc.)
|
||||
if (!origin) return callback(null, true);
|
||||
// Requests with no origin (curl, Postman, server-to-server)
|
||||
// Only allow in development - in production this is a CORS bypass vector
|
||||
if (!origin) {
|
||||
if (isProduction) {
|
||||
return callback(new Error('Not allowed by CORS'));
|
||||
}
|
||||
return callback(null, true);
|
||||
}
|
||||
|
||||
// Allow localhost and local network IPs
|
||||
const allowedPatterns = [
|
||||
/^http:\/\/localhost(:\d+)?$/,
|
||||
/^http:\/\/127\.0\.0\.1(:\d+)?$/,
|
||||
/^http:\/\/192\.168\.\d{1,3}\.\d{1,3}(:\d+)?$/,
|
||||
/^http:\/\/10\.\d{1,3}\.\d{1,3}\.\d{1,3}(:\d+)?$/,
|
||||
/^http:\/\/172\.(1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3}(:\d+)?$/,
|
||||
];
|
||||
|
||||
// Check if origin matches allowed patterns or CORS_ORIGIN env
|
||||
// Check CORS_ORIGIN env variable first
|
||||
const corsOrigin = process.env.CORS_ORIGIN;
|
||||
if (corsOrigin && origin === corsOrigin) {
|
||||
return callback(null, true);
|
||||
}
|
||||
|
||||
if (allowedPatterns.some(pattern => pattern.test(origin))) {
|
||||
return callback(null, true);
|
||||
// In development, allow localhost and local network IPs
|
||||
if (!isProduction) {
|
||||
const allowedPatterns = [
|
||||
/^https?:\/\/localhost(:\d+)?$/,
|
||||
/^https?:\/\/127\.0\.0\.1(:\d+)?$/,
|
||||
/^https?:\/\/192\.168\.\d{1,3}\.\d{1,3}(:\d+)?$/,
|
||||
/^https?:\/\/10\.\d{1,3}\.\d{1,3}\.\d{1,3}(:\d+)?$/,
|
||||
/^https?:\/\/172\.(1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3}(:\d+)?$/,
|
||||
];
|
||||
|
||||
if (allowedPatterns.some(pattern => pattern.test(origin))) {
|
||||
return callback(null, true);
|
||||
}
|
||||
}
|
||||
|
||||
callback(new Error('Not allowed by CORS'));
|
||||
@@ -95,6 +105,9 @@ app.use(express.json({ limit: '10mb' }));
|
||||
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
|
||||
app.use(cookieParser());
|
||||
|
||||
// XSS sanitization - clean user input in body, params, and query
|
||||
app.use(xssClean());
|
||||
|
||||
// Custom body validation middleware
|
||||
app.use(validateBody);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user