fix(gateway): default non-finite auth guard limits · openclaw/openclaw@8ada0f4
unauthorized-flood-guard.test.ts
·
2026-05-29
·
via Recent Commits to openclaw:main
File tree
src/gateway/server/ws-connection
| Original file line number | Diff line number | Diff line change |
|---|
@@ -47,6 +47,31 @@ describe("HandshakeAuthLogLimiter", () => {
|
47 | 47 | }); |
48 | 48 | }); |
49 | 49 | |
| 50 | +it("uses default limits for non-finite options", () => { |
| 51 | +const limiter = new HandshakeAuthLogLimiter({ |
| 52 | +intervalMs: Number.NaN, |
| 53 | +maxEntries: Number.POSITIVE_INFINITY, |
| 54 | +}); |
| 55 | + |
| 56 | +expect(limiter.register("first", 0)).toEqual({ |
| 57 | +shouldLog: true, |
| 58 | +suppressedSinceLastLog: 0, |
| 59 | +}); |
| 60 | +expect(limiter.register("first", 1_000)).toEqual({ |
| 61 | +shouldLog: false, |
| 62 | +suppressedSinceLastLog: 0, |
| 63 | +}); |
| 64 | + |
| 65 | +for (let i = 0; i < 256; i += 1) { |
| 66 | +limiter.register(`key-${i}`, i); |
| 67 | +} |
| 68 | + |
| 69 | +expect(limiter.register("first", 2_000)).toEqual({ |
| 70 | +shouldLog: true, |
| 71 | +suppressedSinceLastLog: 0, |
| 72 | +}); |
| 73 | +}); |
| 74 | + |
50 | 75 | it("only rate-limits benign missing-credential startup retries", () => { |
51 | 76 | expect( |
52 | 77 | shouldLimitMissingCredentialAuthLog({ |
|
| Original file line number | Diff line number | Diff line change |
|---|
|
| 1 | +import { resolveIntegerOption } from "../../../shared/number-coercion.js"; |
| 2 | + |
1 | 3 | export type HandshakeAuthLogDecision = { |
2 | 4 | shouldLog: boolean; |
3 | 5 | suppressedSinceLastLog: number; |
@@ -14,8 +16,8 @@ export class HandshakeAuthLogLimiter {
|
14 | 16 | private readonly entries = new Map<string, HandshakeAuthLogState>(); |
15 | 17 | |
16 | 18 | constructor(options?: { intervalMs?: number; maxEntries?: number }) { |
17 | | -this.intervalMs = Math.max(1, Math.floor(options?.intervalMs ?? 30_000)); |
18 | | -this.maxEntries = Math.max(1, Math.floor(options?.maxEntries ?? 256)); |
| 19 | +this.intervalMs = resolveIntegerOption(options?.intervalMs, 30_000, { min: 1 }); |
| 20 | +this.maxEntries = resolveIntegerOption(options?.maxEntries, 256, { min: 1 }); |
19 | 21 | } |
20 | 22 | |
21 | 23 | register(key: string, nowMs = Date.now()): HandshakeAuthLogDecision { |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -31,6 +31,25 @@ describe("UnauthorizedFloodGuard", () => {
|
31 | 31 | }); |
32 | 32 | }); |
33 | 33 | |
| 34 | +it("uses default thresholds for non-finite options", () => { |
| 35 | +const guard = new UnauthorizedFloodGuard({ |
| 36 | +closeAfter: Number.NaN, |
| 37 | +logEvery: Number.POSITIVE_INFINITY, |
| 38 | +}); |
| 39 | + |
| 40 | +for (let i = 0; i < 10; i += 1) { |
| 41 | +expect(guard.registerUnauthorized().shouldClose).toBe(false); |
| 42 | +} |
| 43 | + |
| 44 | +const eleventh = guard.registerUnauthorized(); |
| 45 | +expect(eleventh).toMatchObject({ |
| 46 | +shouldClose: true, |
| 47 | +shouldLog: true, |
| 48 | +count: 11, |
| 49 | +suppressedSinceLastLog: 9, |
| 50 | +}); |
| 51 | +}); |
| 52 | + |
34 | 53 | it("resets counters", () => { |
35 | 54 | const guard = new UnauthorizedFloodGuard({ closeAfter: 10, logEvery: 50 }); |
36 | 55 | guard.registerUnauthorized(); |
|
| Original file line number | Diff line number | Diff line change |
|---|
|
1 | 1 | import { ErrorCodes, type ErrorShape } from "../../../../packages/gateway-protocol/src/index.js"; |
| 2 | +import { resolveIntegerOption } from "../../../shared/number-coercion.js"; |
2 | 3 | |
3 | 4 | export type UnauthorizedFloodGuardOptions = { |
4 | 5 | closeAfter?: number; |
@@ -22,8 +23,8 @@ export class UnauthorizedFloodGuard {
|
22 | 23 | private suppressedSinceLastLog = 0; |
23 | 24 | |
24 | 25 | constructor(options?: UnauthorizedFloodGuardOptions) { |
25 | | -this.closeAfter = Math.max(1, Math.floor(options?.closeAfter ?? DEFAULT_CLOSE_AFTER)); |
26 | | -this.logEvery = Math.max(1, Math.floor(options?.logEvery ?? DEFAULT_LOG_EVERY)); |
| 26 | +this.closeAfter = resolveIntegerOption(options?.closeAfter, DEFAULT_CLOSE_AFTER, { min: 1 }); |
| 27 | +this.logEvery = resolveIntegerOption(options?.logEvery, DEFAULT_LOG_EVERY, { min: 1 }); |
27 | 28 | } |
28 | 29 | |
29 | 30 | registerUnauthorized(): UnauthorizedFloodDecision { |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。