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",