mirror of
https://github.com/openclaw/openclaw.git
synced 2026-02-09 05:19:32 +08:00
fix: Gateway canvas host bypasses auth and serves files unauthenticated
This commit is contained in:
@@ -10,9 +10,15 @@ import { createServer as createHttpsServer } from "node:https";
|
||||
import type { CanvasHostHandler } from "../canvas-host/server.js";
|
||||
import type { createSubsystemLogger } from "../logging/subsystem.js";
|
||||
import { resolveAgentAvatar } from "../agents/identity-avatar.js";
|
||||
import { handleA2uiHttpRequest } from "../canvas-host/a2ui.js";
|
||||
import {
|
||||
A2UI_PATH,
|
||||
CANVAS_HOST_PATH,
|
||||
CANVAS_WS_PATH,
|
||||
handleA2uiHttpRequest,
|
||||
} from "../canvas-host/a2ui.js";
|
||||
import { loadConfig } from "../config/config.js";
|
||||
import { handleSlackHttpRequest } from "../slack/http/index.js";
|
||||
import { authorizeGatewayConnect } from "./auth.js";
|
||||
import {
|
||||
handleControlUiAvatarRequest,
|
||||
handleControlUiHttpRequest,
|
||||
@@ -31,6 +37,8 @@ import {
|
||||
resolveHookChannel,
|
||||
resolveHookDeliver,
|
||||
} from "./hooks.js";
|
||||
import { sendUnauthorized } from "./http-common.js";
|
||||
import { getBearerToken } from "./http-utils.js";
|
||||
import { handleOpenAiHttpRequest } from "./openai-http.js";
|
||||
import { handleOpenResponsesHttpRequest } from "./openresponses-http.js";
|
||||
import { handleToolsInvokeHttpRequest } from "./tools-invoke-http.js";
|
||||
@@ -60,6 +68,16 @@ function sendJson(res: ServerResponse, status: number, body: unknown) {
|
||||
res.end(JSON.stringify(body));
|
||||
}
|
||||
|
||||
function isCanvasPath(pathname: string): boolean {
|
||||
return (
|
||||
pathname === A2UI_PATH ||
|
||||
pathname.startsWith(`${A2UI_PATH}/`) ||
|
||||
pathname === CANVAS_HOST_PATH ||
|
||||
pathname.startsWith(`${CANVAS_HOST_PATH}/`) ||
|
||||
pathname === CANVAS_WS_PATH
|
||||
);
|
||||
}
|
||||
|
||||
export type HooksRequestHandler = (req: IncomingMessage, res: ServerResponse) => Promise<boolean>;
|
||||
|
||||
export function createHooksRequestHandler(
|
||||
@@ -287,6 +305,20 @@ export function createGatewayHttpServer(opts: {
|
||||
}
|
||||
}
|
||||
if (canvasHost) {
|
||||
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
||||
if (isCanvasPath(url.pathname)) {
|
||||
const token = getBearerToken(req);
|
||||
const authResult = await authorizeGatewayConnect({
|
||||
auth: resolvedAuth,
|
||||
connectAuth: token ? { token, password: token } : null,
|
||||
req,
|
||||
trustedProxies,
|
||||
});
|
||||
if (!authResult.ok) {
|
||||
sendUnauthorized(res);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (await handleA2uiHttpRequest(req, res)) {
|
||||
return;
|
||||
}
|
||||
@@ -331,14 +363,41 @@ export function attachGatewayUpgradeHandler(opts: {
|
||||
httpServer: HttpServer;
|
||||
wss: WebSocketServer;
|
||||
canvasHost: CanvasHostHandler | null;
|
||||
resolvedAuth: import("./auth.js").ResolvedGatewayAuth;
|
||||
}) {
|
||||
const { httpServer, wss, canvasHost } = opts;
|
||||
const { httpServer, wss, canvasHost, resolvedAuth } = opts;
|
||||
httpServer.on("upgrade", (req, socket, head) => {
|
||||
if (canvasHost?.handleUpgrade(req, socket, head)) {
|
||||
return;
|
||||
}
|
||||
wss.handleUpgrade(req, socket, head, (ws) => {
|
||||
wss.emit("connection", ws, req);
|
||||
void (async () => {
|
||||
if (canvasHost) {
|
||||
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
||||
if (url.pathname === CANVAS_WS_PATH) {
|
||||
const configSnapshot = loadConfig();
|
||||
const token = getBearerToken(req);
|
||||
const authResult = await authorizeGatewayConnect({
|
||||
auth: resolvedAuth,
|
||||
connectAuth: token ? { token, password: token } : null,
|
||||
req,
|
||||
trustedProxies: configSnapshot.gateway?.trustedProxies ?? [],
|
||||
});
|
||||
if (!authResult.ok) {
|
||||
socket.write("HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n");
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (canvasHost?.handleUpgrade(req, socket, head)) {
|
||||
return;
|
||||
}
|
||||
wss.handleUpgrade(req, socket, head, (ws) => {
|
||||
wss.emit("connection", ws, req);
|
||||
});
|
||||
})().catch(() => {
|
||||
try {
|
||||
socket.destroy();
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -164,7 +164,12 @@ export async function createGatewayRuntimeState(params: {
|
||||
maxPayload: MAX_PAYLOAD_BYTES,
|
||||
});
|
||||
for (const server of httpServers) {
|
||||
attachGatewayUpgradeHandler({ httpServer: server, wss, canvasHost });
|
||||
attachGatewayUpgradeHandler({
|
||||
httpServer: server,
|
||||
wss,
|
||||
canvasHost,
|
||||
resolvedAuth: params.resolvedAuth,
|
||||
});
|
||||
}
|
||||
|
||||
const clients = new Set<GatewayWsClient>();
|
||||
|
||||
Reference in New Issue
Block a user