/** * BetterDesk Console - Security Middleware * Configures Helmet and custom security headers */ const crypto = require('crypto'); const helmet = require('helmet'); const config = require('../config/config'); /** * Build CSP connect-src based on HTTPS mode * Allow WebSocket connections (ws:// or wss:// depending on mode) */ const connectSources = config.httpsEnabled ? ["'self'", "wss:", "https://cdn.jsdelivr.net"] : ["'self'", "ws:", "https://cdn.jsdelivr.net"]; /** * HSTS is disabled for self-signed / LAN installs so HTTP↔HTTPS toggles do not * leave a year-long browser enforcement (#219). Set HSTS_ENABLED=true to force it. */ function shouldSendStrictTransportSecurity() { if (!config.httpsEnabled) return false; const hstsEnv = String(process.env.HSTS_ENABLED || '').toLowerCase(); if (hstsEnv === 'false' || hstsEnv === '0' || hstsEnv === 'off') return false; if (hstsEnv === 'true' || hstsEnv === '1' || hstsEnv === 'on') return true; return !config.allowSelfSignedCerts; } function buildHelmetMiddleware(req, res) { const nonce = crypto.randomBytes(16).toString('base64'); const isRemoteViewerPage = req.path.startsWith('/remote'); res.locals.cspNonce = nonce; const scriptSources = ["'self'", `'nonce-${nonce}'`, "https://cdn.jsdelivr.net"]; if (isRemoteViewerPage) { // The remote viewer still depends on protobuf.js runtime code generation. scriptSources.push("'unsafe-eval'"); } return helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], scriptSrc: scriptSources, // Allow inline event handlers (onclick=, onchange=, etc.) used by // several admin panel pages.