From 24b4a314a9e97b6c640ca29067e95028a23a8973 Mon Sep 17 00:00:00 2001 From: paul Date: Tue, 9 Sep 2025 20:00:27 +0200 Subject: [PATCH] fix(native/http): disable CSP upgrade-insecure-requests and HSTS unless ENABLE_HSTS=true; prevents HTTPS upgrades on HTTP installs --- backend/server.js | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/backend/server.js b/backend/server.js index d5665a7..245784f 100644 --- a/backend/server.js +++ b/backend/server.js @@ -43,25 +43,36 @@ const PORT = process.env.PORT || 3000; app.set('trust proxy', 'loopback, linklocal, uniquelocal'); // Security middleware with custom CSP +// In native HTTP installs, do NOT force HTTPS for subresources. +const enableHsts = process.env.ENABLE_HSTS === 'true'; +const cspDirectives = { + defaultSrc: ["'self'"], + scriptSrc: ["'self'", "'unsafe-inline'"], // Required for React + styleSrc: ["'self'", "'unsafe-inline'", "https:"], // Required for styled components + imgSrc: ["'self'", "data:", "https:", "blob:"], // Allow data URLs and external images + connectSrc: ["'self'"], // API connections + fontSrc: ["'self'", "https:", "data:"], // Web fonts + objectSrc: ["'none'"], // Disable plugins + mediaSrc: ["'self'"], // Audio/video + frameSrc: ["'none'"], // Disable iframes +}; +// Only upgrade insecure requests when HSTS explicitly enabled (HTTPS deployment) +if (enableHsts) { + // In helmet, an empty array enables the directive + cspDirectives.upgradeInsecureRequests = []; +} + app.use(helmet({ contentSecurityPolicy: { - directives: { - defaultSrc: ["'self'"], - scriptSrc: ["'self'", "'unsafe-inline'"], // Required for React - styleSrc: ["'self'", "'unsafe-inline'", "https:"], // Required for styled components - imgSrc: ["'self'", "data:", "https:", "blob:"], // Allow data URLs and external images - connectSrc: ["'self'"], // API connections - fontSrc: ["'self'", "https:", "data:"], // Web fonts - objectSrc: ["'none'"], // Disable plugins - mediaSrc: ["'self'"], // Audio/video - frameSrc: ["'none'"], // Disable iframes - }, + // Avoid helmet adding defaults like upgrade-insecure-requests when not desired + useDefaults: false, + directives: cspDirectives, }, - hsts: { + hsts: enableHsts ? { maxAge: 31536000, // 1 year includeSubDomains: true, preload: true - }, + } : false, permittedCrossDomainPolicies: false, referrerPolicy: { policy: "strict-origin-when-cross-origin" } }));