Skip to content

Commit 7979639

Browse files
committed
fix(gateway): cap non-finite preauth limits
1 parent 8ada0f4 commit 7979639

2 files changed

Lines changed: 39 additions & 1 deletion

File tree

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { describe, expect, it } from "vitest";
2+
import { createPreauthConnectionBudget } from "./preauth-connection-budget.js";
3+
4+
describe("createPreauthConnectionBudget", () => {
5+
it("caps connections with a finite configured limit", () => {
6+
const budget = createPreauthConnectionBudget(2);
7+
8+
expect(budget.acquire("127.0.0.1")).toBe(true);
9+
expect(budget.acquire("127.0.0.1")).toBe(true);
10+
expect(budget.acquire("127.0.0.1")).toBe(false);
11+
12+
budget.release("127.0.0.1");
13+
expect(budget.acquire("127.0.0.1")).toBe(true);
14+
});
15+
16+
it("uses the default cap for non-finite direct limits", () => {
17+
const budget = createPreauthConnectionBudget(Number.NaN);
18+
19+
for (let i = 0; i < 32; i += 1) {
20+
expect(budget.acquire("127.0.0.1")).toBe(true);
21+
}
22+
expect(budget.acquire("127.0.0.1")).toBe(false);
23+
});
24+
25+
it("shares one capped bucket for missing client IPs", () => {
26+
const budget = createPreauthConnectionBudget(Number.POSITIVE_INFINITY);
27+
28+
for (let i = 0; i < 32; i += 1) {
29+
expect(budget.acquire(i % 2 === 0 ? undefined : " ")).toBe(true);
30+
}
31+
expect(budget.acquire(undefined)).toBe(false);
32+
});
33+
});

src/gateway/server/preauth-connection-budget.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { resolveIntegerOption } from "../../shared/number-coercion.js";
2+
13
const DEFAULT_MAX_PREAUTH_CONNECTIONS_PER_IP = 32;
24
const UNKNOWN_CLIENT_IP_BUDGET_KEY = "__openclaw_unknown_client_ip__";
35

@@ -23,6 +25,9 @@ export type PreauthConnectionBudget = {
2325
export function createPreauthConnectionBudget(
2426
limit = getMaxPreauthConnectionsPerIpFromEnv(),
2527
): PreauthConnectionBudget {
28+
const maxConnectionsPerIp = resolveIntegerOption(limit, getMaxPreauthConnectionsPerIpFromEnv(), {
29+
min: 1,
30+
});
2631
const counts = new Map<string, number>();
2732
const normalizeBudgetKey = (clientIp: string | undefined) => {
2833
const ip = clientIp?.trim();
@@ -36,7 +41,7 @@ export function createPreauthConnectionBudget(
3641
acquire(clientIp) {
3742
const ip = normalizeBudgetKey(clientIp);
3843
const next = (counts.get(ip) ?? 0) + 1;
39-
if (next > limit) {
44+
if (next > maxConnectionsPerIp) {
4045
return false;
4146
}
4247
counts.set(ip, next);

0 commit comments

Comments
 (0)