mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 10:17:39 +00:00
test(gateway): add hooks bind-host hardening coverage
This commit is contained in:
@@ -1,7 +1,9 @@
|
|||||||
import type { IncomingMessage, ServerResponse } from "node:http";
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
||||||
import { describe, expect, test, vi } from "vitest";
|
import { describe, expect, test, vi } from "vitest";
|
||||||
|
import type { createSubsystemLogger } from "../logging/subsystem.js";
|
||||||
import type { ResolvedGatewayAuth } from "./auth.js";
|
import type { ResolvedGatewayAuth } from "./auth.js";
|
||||||
import { createGatewayHttpServer } from "./server-http.js";
|
import type { HooksConfigResolved } from "./hooks.js";
|
||||||
|
import { createGatewayHttpServer, createHooksRequestHandler } from "./server-http.js";
|
||||||
import { withTempConfig } from "./test-temp-config.js";
|
import { withTempConfig } from "./test-temp-config.js";
|
||||||
|
|
||||||
function createRequest(params: {
|
function createRequest(params: {
|
||||||
@@ -65,6 +67,25 @@ async function dispatchRequest(
|
|||||||
await new Promise((resolve) => setImmediate(resolve));
|
await new Promise((resolve) => setImmediate(resolve));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createHooksConfig(): HooksConfigResolved {
|
||||||
|
return {
|
||||||
|
basePath: "/hooks",
|
||||||
|
token: "hook-secret",
|
||||||
|
maxBodyBytes: 1024,
|
||||||
|
mappings: [],
|
||||||
|
agentPolicy: {
|
||||||
|
defaultAgentId: "main",
|
||||||
|
knownAgentIds: new Set(["main"]),
|
||||||
|
allowedAgentIds: undefined,
|
||||||
|
},
|
||||||
|
sessionPolicy: {
|
||||||
|
allowRequestSessionKey: false,
|
||||||
|
defaultSessionKey: undefined,
|
||||||
|
allowedSessionKeyPrefixes: undefined,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
describe("gateway plugin HTTP auth boundary", () => {
|
describe("gateway plugin HTTP auth boundary", () => {
|
||||||
test("applies default security headers and optional strict transport security", async () => {
|
test("applies default security headers and optional strict transport security", async () => {
|
||||||
const resolvedAuth: ResolvedGatewayAuth = {
|
const resolvedAuth: ResolvedGatewayAuth = {
|
||||||
@@ -220,4 +241,101 @@ describe("gateway plugin HTTP auth boundary", () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.each(["0.0.0.0", "::"])(
|
||||||
|
"returns 404 (not 500) for non-hook routes with hooks enabled and bindHost=%s",
|
||||||
|
async (bindHost) => {
|
||||||
|
const resolvedAuth: ResolvedGatewayAuth = {
|
||||||
|
mode: "none",
|
||||||
|
token: undefined,
|
||||||
|
password: undefined,
|
||||||
|
allowTailscale: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
await withTempConfig({
|
||||||
|
cfg: { gateway: { trustedProxies: [] } },
|
||||||
|
prefix: "openclaw-plugin-http-hooks-bindhost-",
|
||||||
|
run: async () => {
|
||||||
|
const handleHooksRequest = createHooksRequestHandler({
|
||||||
|
getHooksConfig: () => createHooksConfig(),
|
||||||
|
bindHost,
|
||||||
|
port: 18789,
|
||||||
|
logHooks: {
|
||||||
|
warn: vi.fn(),
|
||||||
|
debug: vi.fn(),
|
||||||
|
info: vi.fn(),
|
||||||
|
error: vi.fn(),
|
||||||
|
} as unknown as ReturnType<typeof createSubsystemLogger>,
|
||||||
|
dispatchWakeHook: () => {},
|
||||||
|
dispatchAgentHook: () => "run-1",
|
||||||
|
});
|
||||||
|
const server = createGatewayHttpServer({
|
||||||
|
canvasHost: null,
|
||||||
|
clients: new Set(),
|
||||||
|
controlUiEnabled: false,
|
||||||
|
controlUiBasePath: "/__control__",
|
||||||
|
openAiChatCompletionsEnabled: false,
|
||||||
|
openResponsesEnabled: false,
|
||||||
|
handleHooksRequest,
|
||||||
|
resolvedAuth,
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = createResponse();
|
||||||
|
await dispatchRequest(server, createRequest({ path: "/" }), response.res);
|
||||||
|
|
||||||
|
expect(response.res.statusCode).toBe(404);
|
||||||
|
expect(response.getBody()).toBe("Not Found");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test("rejects query-token hooks requests with bindHost=::", async () => {
|
||||||
|
const resolvedAuth: ResolvedGatewayAuth = {
|
||||||
|
mode: "none",
|
||||||
|
token: undefined,
|
||||||
|
password: undefined,
|
||||||
|
allowTailscale: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
await withTempConfig({
|
||||||
|
cfg: { gateway: { trustedProxies: [] } },
|
||||||
|
prefix: "openclaw-plugin-http-hooks-query-token-",
|
||||||
|
run: async () => {
|
||||||
|
const handleHooksRequest = createHooksRequestHandler({
|
||||||
|
getHooksConfig: () => createHooksConfig(),
|
||||||
|
bindHost: "::",
|
||||||
|
port: 18789,
|
||||||
|
logHooks: {
|
||||||
|
warn: vi.fn(),
|
||||||
|
debug: vi.fn(),
|
||||||
|
info: vi.fn(),
|
||||||
|
error: vi.fn(),
|
||||||
|
} as unknown as ReturnType<typeof createSubsystemLogger>,
|
||||||
|
dispatchWakeHook: () => {},
|
||||||
|
dispatchAgentHook: () => "run-1",
|
||||||
|
});
|
||||||
|
const server = createGatewayHttpServer({
|
||||||
|
canvasHost: null,
|
||||||
|
clients: new Set(),
|
||||||
|
controlUiEnabled: false,
|
||||||
|
controlUiBasePath: "/__control__",
|
||||||
|
openAiChatCompletionsEnabled: false,
|
||||||
|
openResponsesEnabled: false,
|
||||||
|
handleHooksRequest,
|
||||||
|
resolvedAuth,
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = createResponse();
|
||||||
|
await dispatchRequest(
|
||||||
|
server,
|
||||||
|
createRequest({ path: "/hooks/wake?token=bad" }),
|
||||||
|
response.res,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(response.res.statusCode).toBe(400);
|
||||||
|
expect(response.getBody()).toContain("Hook token must be provided");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user