mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 09:27:39 +00:00
security: add baseline security headers to gateway HTTP responses (#10526)
* security: add baseline security headers to gateway HTTP responses All responses from the gateway HTTP server now include X-Content-Type-Options: nosniff and Referrer-Policy: no-referrer. These headers are applied early in handleRequest, before any handler runs, ensuring coverage for every response including error pages and 404s. Headers that restrict framing (X-Frame-Options, CSP frame-ancestors) are intentionally omitted at this global level because the canvas host and A2UI handlers serve content that may be loaded inside frames. * fix: apply security headers before WebSocket upgrade check Move setDefaultSecurityHeaders() above the WebSocket early-return so the headers are set on every HTTP response path including upgrades. --------- Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
This commit is contained in:
@@ -2,6 +2,17 @@ import type { IncomingMessage, ServerResponse } from "node:http";
|
|||||||
import type { GatewayAuthResult } from "./auth.js";
|
import type { GatewayAuthResult } from "./auth.js";
|
||||||
import { readJsonBody } from "./hooks.js";
|
import { readJsonBody } from "./hooks.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply baseline security headers that are safe for all response types (API JSON,
|
||||||
|
* HTML pages, static assets, SSE streams). Headers that restrict framing or set a
|
||||||
|
* Content-Security-Policy are intentionally omitted here because some handlers
|
||||||
|
* (canvas host, A2UI) serve content that may be loaded inside frames.
|
||||||
|
*/
|
||||||
|
export function setDefaultSecurityHeaders(res: ServerResponse) {
|
||||||
|
res.setHeader("X-Content-Type-Options", "nosniff");
|
||||||
|
res.setHeader("Referrer-Policy", "no-referrer");
|
||||||
|
}
|
||||||
|
|
||||||
export function sendJson(res: ServerResponse, status: number, body: unknown) {
|
export function sendJson(res: ServerResponse, status: number, body: unknown) {
|
||||||
res.statusCode = status;
|
res.statusCode = status;
|
||||||
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ import {
|
|||||||
resolveHookChannel,
|
resolveHookChannel,
|
||||||
resolveHookDeliver,
|
resolveHookDeliver,
|
||||||
} from "./hooks.js";
|
} from "./hooks.js";
|
||||||
import { sendGatewayAuthFailure } from "./http-common.js";
|
import { sendGatewayAuthFailure, setDefaultSecurityHeaders } from "./http-common.js";
|
||||||
import { getBearerToken, getHeader } from "./http-utils.js";
|
import { getBearerToken, getHeader } from "./http-utils.js";
|
||||||
import { isPrivateOrLoopbackAddress, resolveGatewayClientIp } from "./net.js";
|
import { isPrivateOrLoopbackAddress, resolveGatewayClientIp } from "./net.js";
|
||||||
import { handleOpenAiHttpRequest } from "./openai-http.js";
|
import { handleOpenAiHttpRequest } from "./openai-http.js";
|
||||||
@@ -474,6 +474,8 @@ export function createGatewayHttpServer(opts: {
|
|||||||
});
|
});
|
||||||
|
|
||||||
async function handleRequest(req: IncomingMessage, res: ServerResponse) {
|
async function handleRequest(req: IncomingMessage, res: ServerResponse) {
|
||||||
|
setDefaultSecurityHeaders(res);
|
||||||
|
|
||||||
// Don't interfere with WebSocket upgrades; ws handles the 'upgrade' event.
|
// Don't interfere with WebSocket upgrades; ws handles the 'upgrade' event.
|
||||||
if (String(req.headers.upgrade ?? "").toLowerCase() === "websocket") {
|
if (String(req.headers.upgrade ?? "").toLowerCase() === "websocket") {
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user