eldouma-graphics / server /security.mjs
Moeeldouma's picture
Embed-friendly headers
db56627 verified
Raw
History Blame Contribute Delete
2.09 kB
// Production hardening: security headers + an audit log.
// Helmet-style headers. The always-on ones are safe for any app. A Content-Security-Policy is powerful
// but easy to break (fonts need allow-listed origins), so it's OPT-IN via ENABLE_CSP=1.
export function securityHeaders() {
const cspParts = process.env.ENABLE_CSP === "1" ? [
"default-src 'self'",
"script-src 'self' 'unsafe-inline' 'unsafe-eval'",
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
"font-src 'self' https://fonts.gstatic.com data:",
"img-src 'self' data: blob: https:",
"media-src 'self' blob: https:",
"connect-src 'self'",
"worker-src 'self' blob:",
] : [];
// Allow embedding this app in an <iframe> on specific origins — e.g. a Hugging Face Space renders
// the app inside huggingface.co. Set EMBED_ORIGINS to a space-separated origin list; we then send a
// CSP `frame-ancestors` (the modern, embed-friendly control) and OMIT the blocking X-Frame-Options.
// Unset → the secure default for self-hosting (X-Frame-Options: SAMEORIGIN, no framing).
const embed = (process.env.EMBED_ORIGINS || "").trim();
if (embed) cspParts.push(`frame-ancestors 'self' ${embed}`);
const csp = cspParts.length ? cspParts.join("; ") : null;
return (req, res, next) => {
res.setHeader("X-Content-Type-Options", "nosniff");
if (!embed) res.setHeader("X-Frame-Options", "SAMEORIGIN");
res.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
res.setHeader("X-DNS-Prefetch-Control", "off");
res.setHeader("Permissions-Policy", "geolocation=(), microphone=(self), camera=(self)");
if (process.env.NODE_ENV === "production") res.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
if (csp) res.setHeader("Content-Security-Policy", csp);
next();
};
}
// audit log for sensitive actions (admin actions) — no raw PII
export const auditLog = (event, data = {}) => {
try { console.log(`[audit] ${new Date().toISOString()} ${event} ${JSON.stringify(data)}`); } catch { /* ignore */ }
};