From b608fa0337bc005c15ea4652a51e0c88fb913ccd Mon Sep 17 00:00:00 2001 From: Gronod Date: Sat, 16 May 2026 16:23:47 +0100 Subject: [PATCH] fix(security #12): add helmet security response headers Adds X-DNS-Prefetch-Control, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, X-XSS-Protection, HSTS (in prod) and others. CSP disabled for now as the SPA uses inline scripts/styles; a nonce/hash-based policy is a future hardening step. --- package-lock.json | 16 +++++++++++++++- package.json | 3 ++- server/index.js | 5 +++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 03f21d9..f0963f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,8 @@ "cors": "^2.8.5", "dotenv": "^16.3.1", "express": "^4.18.2", - "express-rate-limit": "^6.7.0" + "express-rate-limit": "^6.7.0", + "helmet": "^4.6.0" }, "devDependencies": { "concurrently": "^7.6.0", @@ -847,6 +848,14 @@ "node": ">= 0.4" } }, + "node_modules/helmet": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-4.6.0.tgz", + "integrity": "sha512-HVqALKZlR95ROkrnesdhbbZJFi/rIVSoNq6f3jA/9u6MIbTsPh3xZwihjeI5+DO/2sOV6HMHooXcEOuwskHpTg==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", @@ -2256,6 +2265,11 @@ "function-bind": "^1.1.2" } }, + "helmet": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-4.6.0.tgz", + "integrity": "sha512-HVqALKZlR95ROkrnesdhbbZJFi/rIVSoNq6f3jA/9u6MIbTsPh3xZwihjeI5+DO/2sOV6HMHooXcEOuwskHpTg==" + }, "http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", diff --git a/package.json b/package.json index 90d4b91..3db372a 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "cors": "^2.8.5", "dotenv": "^16.3.1", "express": "^4.18.2", - "express-rate-limit": "^6.7.0" + "express-rate-limit": "^6.7.0", + "helmet": "^4.6.0" }, "devDependencies": { "concurrently": "^7.6.0", diff --git a/server/index.js b/server/index.js index a93508b..fd59923 100644 --- a/server/index.js +++ b/server/index.js @@ -1,6 +1,7 @@ const express = require('express'); const path = require('path'); const cookieParser = require('cookie-parser'); +const helmet = require('helmet'); const fs = require('fs'); require('dotenv').config(); @@ -58,6 +59,10 @@ const { startPoller, POLL_INTERVAL, POLLING_ENABLED } = require('./utils/poller' const app = express(); const PORT = process.env.PORT || 3001; +app.use(helmet({ + contentSecurityPolicy: false // SPA uses inline scripts; CSP requires a nonce/hash strategy +})); + const cookieSecret = process.env.COOKIE_SECRET; if (!cookieSecret && process.env.NODE_ENV === 'production') { console.error('[Security] COOKIE_SECRET is not set in production — cookies are unsigned and can be tampered with!');