fix(security): normalize hook auth rate-limit client keys

This commit is contained in:
Peter Steinberger
2026-02-22 08:40:39 +01:00
parent aab20e58d7
commit 3284d2eb22
5 changed files with 63 additions and 7 deletions

View File

@@ -36,15 +36,18 @@ function createHooksConfig(): HooksConfigResolved {
};
}
function createRequest(): IncomingMessage {
function createRequest(params?: {
authorization?: string;
remoteAddress?: string;
}): IncomingMessage {
return {
method: "POST",
url: "/hooks/wake",
headers: {
host: "127.0.0.1:18789",
authorization: "Bearer hook-secret",
authorization: params?.authorization ?? "Bearer hook-secret",
},
socket: { remoteAddress: "127.0.0.1" },
socket: { remoteAddress: params?.remoteAddress ?? "127.0.0.1" },
} as IncomingMessage;
}
@@ -96,4 +99,42 @@ describe("createHooksRequestHandler timeout status mapping", () => {
expect(dispatchWakeHook).not.toHaveBeenCalled();
expect(dispatchAgentHook).not.toHaveBeenCalled();
});
test("shares hook auth rate-limit bucket across ipv4 and ipv4-mapped ipv6 forms", async () => {
const handler = createHooksRequestHandler({
getHooksConfig: () => createHooksConfig(),
bindHost: "127.0.0.1",
port: 18789,
logHooks: {
warn: vi.fn(),
debug: vi.fn(),
info: vi.fn(),
error: vi.fn(),
} as unknown as ReturnType<typeof createSubsystemLogger>,
dispatchWakeHook: vi.fn(),
dispatchAgentHook: vi.fn(() => "run-1"),
});
for (let i = 0; i < 20; i++) {
const req = createRequest({
authorization: "Bearer wrong",
remoteAddress: "1.2.3.4",
});
const { res } = createResponse();
const handled = await handler(req, res);
expect(handled).toBe(true);
expect(res.statusCode).toBe(401);
}
const mappedReq = createRequest({
authorization: "Bearer wrong",
remoteAddress: "::ffff:1.2.3.4",
});
const { res: mappedRes, setHeader } = createResponse();
const handled = await handler(mappedReq, mappedRes);
expect(handled).toBe(true);
expect(mappedRes.statusCode).toBe(429);
expect(setHeader).toHaveBeenCalledWith("Retry-After", expect.any(String));
});
});