Agromind-backend / backend /middleware /securityMiddleware.js
gh-action-hf-auto
auto: sync backend from github@32fb9685
8a6248c
/**
* Security headers middleware
* Adds security-related HTTP headers to all responses.
*/
export function securityHeaders(req, res, next) {
// Prevent MIME type sniffing
res.setHeader('X-Content-Type-Options', 'nosniff');
// Do not set X-Frame-Options to DENY because Hugging Face Spaces
// renders the app inside an iframe.
// XSS protection
res.setHeader('X-XSS-Protection', '1; mode=block');
// Referrer policy
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
// Content Security Policy (basic)
res.setHeader(
'Content-Security-Policy',
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; frame-ancestors 'self' https://huggingface.co https://*.hf.space;"
);
// Surface request latency via Server-Timing to aid production diagnostics.
// Wrap res.end so the elapsed time is known before headers are flushed.
const startMs = Date.now();
const originalEnd = res.end.bind(res);
res.end = function (chunk, encoding, callback) {
if (!res.headersSent) {
res.setHeader('Server-Timing', `total;dur=${Date.now() - startMs}`);
}
return originalEnd(chunk, encoding, callback);
};
// Remove server identification
res.removeHeader('X-Powered-By');
next();
}
/**
* Global error handler that sanitizes error output in production
*/
export function errorHandler(err, _req, res, _next) {
const statusCode = err.statusCode || err.status || 500;
// Never expose stack traces in production
const isProduction = process.env.NODE_ENV === 'production';
if (statusCode >= 500) {
console.error('Server error:', err.message, isProduction ? '' : err.stack);
}
res.status(statusCode).json({
success: false,
message: isProduction ? 'An internal error occurred' : err.message,
...(isProduction ? {} : { stack: err.stack }),
});
}
export default { securityHeaders, errorHandler };