diff --git a/apps/frontend/electron.main.js b/apps/frontend/electron.main.js index f542ba85..388131ed 100644 --- a/apps/frontend/electron.main.js +++ b/apps/frontend/electron.main.js @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-require-imports */ -const { app, BrowserWindow, ipcMain, safeStorage, shell, powerMonitor } = require("electron") +const { app, BrowserWindow, ipcMain, safeStorage, shell, powerMonitor, session } = require("electron") const path = require("path") const { fork } = require("child_process") const { createApplicationMenu } = require("./menu") @@ -57,6 +57,22 @@ function createWindow() { } app.whenReady().then(() => { + // Enforce Content Security Policy for the renderer + session.defaultSession.webRequest.onHeadersReceived((details, callback) => { + callback({ + responseHeaders: { + ...details.responseHeaders, + "Content-Security-Policy": [ + "default-src 'self'; script-src 'self'; " + + "style-src 'self' 'unsafe-inline'; " + + "connect-src 'self'; " + + "img-src 'self' data:; font-src 'self'; " + + "object-src 'none'; base-uri 'self'; form-action 'self';", + ], + }, + }) + }) + createApplicationMenu() startServer() if (serverProcess) { diff --git a/apps/frontend/index.html b/apps/frontend/index.html index 0867878f..cf3ca124 100644 --- a/apps/frontend/index.html +++ b/apps/frontend/index.html @@ -2,6 +2,7 @@ + Valkey Admin diff --git a/apps/server/package.json b/apps/server/package.json index 326f905d..6fa6e050 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -16,6 +16,7 @@ "@valkey/valkey-glide": "^2.4.0", "express": "^4.21.2", "express-rate-limit": "^8.3.1", + "helmet": "^8.2.0", "p-limit": "^6.1.0", "ramda": "^0.31.3", "valkey-common": "*", diff --git a/apps/server/src/__integration__/monitoring.integration.test.ts b/apps/server/src/__integration__/monitoring.integration.test.ts index 05f2626a..b36540dd 100644 --- a/apps/server/src/__integration__/monitoring.integration.test.ts +++ b/apps/server/src/__integration__/monitoring.integration.test.ts @@ -47,7 +47,7 @@ describe("integration / monitoring (cluster fan-out)", async () => { }) const startResponses = await ws.collectFor( VALKEY.MONITOR.monitorFulfilled, - 5000, + 15000, ) assert.ok( startResponses.length >= 1, diff --git a/apps/server/src/index.ts b/apps/server/src/index.ts index 74dd0dba..840a71eb 100644 --- a/apps/server/src/index.ts +++ b/apps/server/src/index.ts @@ -1,5 +1,6 @@ import { WebSocket, WebSocketServer } from "ws" import express from "express" +import helmet from "helmet" import path from "path" import http from "http" import { VALKEY, CONNECTION_TEARDOWN_DELAY_MS } from "valkey-common" @@ -76,6 +77,24 @@ app.use((req, res, next) => { if (req.path.startsWith("/orchestrator")) return next() return limiter(req, res, next) }) + +// Content Security Policy via helmet +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + scriptSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'"], + connectSrc: ["'self'"], + imgSrc: ["'self'", "data:"], + fontSrc: ["'self'"], + objectSrc: ["'none'"], + baseUri: ["'self'"], + formAction: ["'self'"], + }, + }, +})) + app.use(express.static(frontendDist)) app.use(express.json()) const metricsRouter = createMetricsOrchestratorRouter() diff --git a/package-lock.json b/package-lock.json index 7dcc951b..b2b3b831 100644 --- a/package-lock.json +++ b/package-lock.json @@ -104,6 +104,7 @@ "@valkey/valkey-glide": "^2.4.0", "express": "^4.21.2", "express-rate-limit": "^8.3.1", + "helmet": "^8.2.0", "p-limit": "^6.1.0", "ramda": "^0.31.3", "valkey-common": "*", @@ -10220,6 +10221,18 @@ "node": ">=10.0.0" } }, + "node_modules/helmet": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-8.2.0.tgz", + "integrity": "sha512-DRgTIUgnWcJ62KyarxxziuqYxKGnR6Rgg19BlbucN/dpmJbl1XOit6qvoOX0ZT+HhWe5OUVhU/a1zpGyc1xA0Q==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/EvanHahn" + } + }, "node_modules/hosted-git-info": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz",