From 367b12e9c5a039a52a7c5fbae9cceb17089bdb63 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 23 Jan 2026 05:37:19 +0000 Subject: [PATCH] feat(security): add HTTP security headers middleware Implemented a new middleware `createSecurityHeadersMiddleware` to set standard security headers: - X-Content-Type-Options: nosniff - X-Frame-Options: SAMEORIGIN - Referrer-Policy: strict-origin-when-cross-origin - X-XSS-Protection: 1; mode=block Registered this middleware in `WebUIManager` to protect the WebUI server. Verified manually with a test script. --- .jules/sentinel.md | 5 ++++ src/main/webui/server/WebUIManager.ts | 4 +++ src/main/webui/server/security-middleware.ts | 31 ++++++++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 src/main/webui/server/security-middleware.ts diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 501ee8f..30ce871 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -7,3 +7,8 @@ **Vulnerability:** The application used a hardcoded string ('ffui-webui-2025') as a salt for HMAC signatures on session tokens. This makes session tokens predictable if the password is known or weak, and allows rainbow table attacks if the source code is public. **Learning:** Hardcoded salts in open-source projects defeat the purpose of salting. Even if combined with a password, they don't provide per-installation uniqueness. **Prevention:** Use a random secret generated at runtime (or installation time) and persist it in the application configuration. + +## 2026-01-23 - Missing Security Headers +**Vulnerability:** The WebUI server was missing standard HTTP security headers (X-Frame-Options, X-Content-Type-Options, etc.), potentially exposing it to clickjacking and MIME-type confusion attacks. +**Learning:** Express.js does not include security headers by default. Explicit middleware is required to set them. +**Prevention:** Always include the `createSecurityHeadersMiddleware` (or `helmet`) in the Express app setup pipeline. diff --git a/src/main/webui/server/WebUIManager.ts b/src/main/webui/server/WebUIManager.ts index 44db9c2..56ae312 100644 --- a/src/main/webui/server/WebUIManager.ts +++ b/src/main/webui/server/WebUIManager.ts @@ -43,6 +43,7 @@ import { createLoginRateLimiter, createRequestLogger, } from './auth-middleware.js'; +import { createSecurityHeadersMiddleware } from './security-middleware.js'; import { registerPublicThemeRoutes } from './routes/theme-routes.js'; import { getWebSocketManager } from './WebSocketManager.js'; @@ -156,6 +157,9 @@ export class WebUIManager extends EventEmitter { // Request logging this.expressApp.use(createRequestLogger()); + // Security headers + this.expressApp.use(createSecurityHeadersMiddleware()); + // JSON body parsing this.expressApp.use(express.json()); diff --git a/src/main/webui/server/security-middleware.ts b/src/main/webui/server/security-middleware.ts new file mode 100644 index 0000000..3e32206 --- /dev/null +++ b/src/main/webui/server/security-middleware.ts @@ -0,0 +1,31 @@ +/** + * @fileoverview Middleware for adding security headers to HTTP responses. + * + * Implements "Defense in Depth" by adding standard security headers to all WebUI responses. + * These headers help prevent common attacks such as XSS, clickjacking, and MIME sniffing. + */ + +import { NextFunction, Request, Response } from 'express'; + +/** + * Creates middleware that adds security headers to the response. + */ +export function createSecurityHeadersMiddleware() { + return (_req: Request, res: Response, next: NextFunction): void => { + // Prevent MIME sniffing + res.setHeader('X-Content-Type-Options', 'nosniff'); + + // Prevent clickjacking by denying framing (or allowing sameorigin) + // We use SAMEORIGIN to allow the UI to potentially frame itself if needed, + // though typically it's better to verify if this is needed. + res.setHeader('X-Frame-Options', 'SAMEORIGIN'); + + // Control referrer information + res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin'); + + // Enable XSS filtering in browsers that support it (mostly legacy, but good for depth) + res.setHeader('X-XSS-Protection', '1; mode=block'); + + next(); + }; +}