security(feishu): bound unauthenticated webhook rate-limit state (openclaw#26050) thanks @bmendonca3

Verified:
- pnpm install --frozen-lockfile
- pnpm build
- pnpm check
- pnpm test:macmini

Co-authored-by: bmendonca3 <208517100+bmendonca3@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
This commit is contained in:
bmendonca3
2026-02-27 12:22:24 -07:00
committed by GitHub
parent 3882b8a5be
commit f943c76cde
3 changed files with 72 additions and 2 deletions

View File

@@ -23,7 +23,13 @@ vi.mock("./client.js", () => ({
createEventDispatcher: vi.fn(() => ({ register: vi.fn() })),
}));
import { monitorFeishuProvider, stopFeishuMonitor } from "./monitor.js";
import {
clearFeishuWebhookRateLimitStateForTest,
getFeishuWebhookRateLimitStateSizeForTest,
isWebhookRateLimitedForTest,
monitorFeishuProvider,
stopFeishuMonitor,
} from "./monitor.js";
async function getFreePort(): Promise<number> {
const server = createServer();
@@ -114,6 +120,7 @@ async function withRunningWebhookMonitor(
}
afterEach(() => {
clearFeishuWebhookRateLimitStateForTest();
stopFeishuMonitor();
});
@@ -180,4 +187,23 @@ describe("Feishu webhook security hardening", () => {
},
);
});
it("caps tracked webhook rate-limit keys to prevent unbounded growth", () => {
const now = 1_000_000;
for (let i = 0; i < 4_500; i += 1) {
isWebhookRateLimitedForTest(`/feishu-rate-limit:key-${i}`, now);
}
expect(getFeishuWebhookRateLimitStateSizeForTest()).toBeLessThanOrEqual(4_096);
});
it("prunes stale webhook rate-limit state after window elapses", () => {
const now = 2_000_000;
for (let i = 0; i < 100; i += 1) {
isWebhookRateLimitedForTest(`/feishu-rate-limit-stale:key-${i}`, now);
}
expect(getFeishuWebhookRateLimitStateSizeForTest()).toBe(100);
isWebhookRateLimitedForTest("/feishu-rate-limit-stale:fresh", now + 60_001);
expect(getFeishuWebhookRateLimitStateSizeForTest()).toBe(1);
});
});