mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 10:57:40 +00:00
fix(gateway): harden hooks URL parsing (#26864)
This commit is contained in:
@@ -41,10 +41,11 @@ function createHooksConfig(): HooksConfigResolved {
|
|||||||
function createRequest(params?: {
|
function createRequest(params?: {
|
||||||
authorization?: string;
|
authorization?: string;
|
||||||
remoteAddress?: string;
|
remoteAddress?: string;
|
||||||
|
url?: string;
|
||||||
}): IncomingMessage {
|
}): IncomingMessage {
|
||||||
return {
|
return {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: "/hooks/wake",
|
url: params?.url ?? "/hooks/wake",
|
||||||
headers: {
|
headers: {
|
||||||
host: "127.0.0.1:18789",
|
host: "127.0.0.1:18789",
|
||||||
authorization: params?.authorization ?? "Bearer hook-secret",
|
authorization: params?.authorization ?? "Bearer hook-secret",
|
||||||
@@ -71,10 +72,11 @@ function createResponse(): {
|
|||||||
function createHandler(params?: {
|
function createHandler(params?: {
|
||||||
dispatchWakeHook?: HooksHandlerDeps["dispatchWakeHook"];
|
dispatchWakeHook?: HooksHandlerDeps["dispatchWakeHook"];
|
||||||
dispatchAgentHook?: HooksHandlerDeps["dispatchAgentHook"];
|
dispatchAgentHook?: HooksHandlerDeps["dispatchAgentHook"];
|
||||||
|
bindHost?: string;
|
||||||
}) {
|
}) {
|
||||||
return createHooksRequestHandler({
|
return createHooksRequestHandler({
|
||||||
getHooksConfig: () => createHooksConfig(),
|
getHooksConfig: () => createHooksConfig(),
|
||||||
bindHost: "127.0.0.1",
|
bindHost: params?.bindHost ?? "127.0.0.1",
|
||||||
port: 18789,
|
port: 18789,
|
||||||
logHooks: {
|
logHooks: {
|
||||||
warn: vi.fn(),
|
warn: vi.fn(),
|
||||||
@@ -139,4 +141,18 @@ describe("createHooksRequestHandler timeout status mapping", () => {
|
|||||||
expect(mappedRes.statusCode).toBe(429);
|
expect(mappedRes.statusCode).toBe(429);
|
||||||
expect(setHeader).toHaveBeenCalledWith("Retry-After", expect.any(String));
|
expect(setHeader).toHaveBeenCalledWith("Retry-After", expect.any(String));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.each(["0.0.0.0", "::"])(
|
||||||
|
"does not throw when bindHost=%s while parsing non-hook request URL",
|
||||||
|
async (bindHost) => {
|
||||||
|
const handler = createHandler({ bindHost });
|
||||||
|
const req = createRequest({ url: "/" });
|
||||||
|
const { res, end } = createResponse();
|
||||||
|
|
||||||
|
const handled = await handler(req, res);
|
||||||
|
|
||||||
|
expect(handled).toBe(false);
|
||||||
|
expect(end).not.toHaveBeenCalled();
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ export function createHooksRequestHandler(
|
|||||||
logHooks: SubsystemLogger;
|
logHooks: SubsystemLogger;
|
||||||
} & HookDispatchers,
|
} & HookDispatchers,
|
||||||
): HooksRequestHandler {
|
): HooksRequestHandler {
|
||||||
const { getHooksConfig, bindHost, port, logHooks, dispatchAgentHook, dispatchWakeHook } = opts;
|
const { getHooksConfig, logHooks, dispatchAgentHook, dispatchWakeHook } = opts;
|
||||||
const hookAuthLimiter = createAuthRateLimiter({
|
const hookAuthLimiter = createAuthRateLimiter({
|
||||||
maxAttempts: HOOK_AUTH_FAILURE_LIMIT,
|
maxAttempts: HOOK_AUTH_FAILURE_LIMIT,
|
||||||
windowMs: HOOK_AUTH_FAILURE_WINDOW_MS,
|
windowMs: HOOK_AUTH_FAILURE_WINDOW_MS,
|
||||||
@@ -227,7 +227,9 @@ export function createHooksRequestHandler(
|
|||||||
if (!hooksConfig) {
|
if (!hooksConfig) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const url = new URL(req.url ?? "/", `http://${bindHost}:${port}`);
|
// Only pathname/search are used here; keep the base host fixed so bind-host
|
||||||
|
// representation (e.g. IPv6 wildcards) cannot break request parsing.
|
||||||
|
const url = new URL(req.url ?? "/", "http://localhost");
|
||||||
const basePath = hooksConfig.basePath;
|
const basePath = hooksConfig.basePath;
|
||||||
if (url.pathname !== basePath && !url.pathname.startsWith(`${basePath}/`)) {
|
if (url.pathname !== basePath && !url.pathname.startsWith(`${basePath}/`)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
Reference in New Issue
Block a user