From e500f401eaaf26713463ac145ac7c44dda77d4dc Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 30 May 2026 16:32:39 -0400 Subject: [PATCH] fix(voice-call): clamp pre-start stream timeout --- .../voice-call/src/media-stream.test.ts | 25 +++++++++++++++++++ extensions/voice-call/src/media-stream.ts | 6 ++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/extensions/voice-call/src/media-stream.test.ts b/extensions/voice-call/src/media-stream.test.ts index f264302cc9ca..10eec2e62dd3 100644 --- a/extensions/voice-call/src/media-stream.test.ts +++ b/extensions/voice-call/src/media-stream.test.ts @@ -1,5 +1,6 @@ import type { IncomingMessage } from "node:http"; import net from "node:net"; +import { MAX_TIMER_TIMEOUT_MS } from "openclaw/plugin-sdk/number-runtime"; import type { RealtimeTranscriptionProviderPlugin, RealtimeTranscriptionSession, @@ -435,6 +436,30 @@ describe("MediaStreamHandler security hardening", () => { } }); + it("clamps oversized pre-start connection timeouts", () => { + vi.useFakeTimers(); + try { + const handler = new MediaStreamHandler({ + transcriptionProvider: createStubSttProvider(), + providerConfig: {}, + preStartTimeoutMs: Number.MAX_SAFE_INTEGER, + }); + const setTimeoutSpy = vi.spyOn(globalThis, "setTimeout"); + const ws = { close: vi.fn() } as unknown as WebSocket; + + const registered = ( + handler as unknown as { + registerPendingConnection(ws: WebSocket, ip: string): boolean; + } + ).registerPendingConnection(ws, "203.0.113.10"); + + expect(registered).toBe(true); + expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), MAX_TIMER_TIMEOUT_MS); + } finally { + vi.useRealTimers(); + } + }); + it("enforces pending connection limits", async () => { const handler = new MediaStreamHandler({ transcriptionProvider: createStubSttProvider(), diff --git a/extensions/voice-call/src/media-stream.ts b/extensions/voice-call/src/media-stream.ts index 8bf26a1eed26..471a28f767f1 100644 --- a/extensions/voice-call/src/media-stream.ts +++ b/extensions/voice-call/src/media-stream.ts @@ -10,6 +10,7 @@ import type { IncomingMessage } from "node:http"; import type { Duplex } from "node:stream"; import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts"; +import { resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime"; import type { RealtimeTranscriptionProviderConfig, RealtimeTranscriptionProviderPlugin, @@ -155,7 +156,10 @@ export class MediaStreamHandler { constructor(config: MediaStreamConfig) { this.config = config; - this.preStartTimeoutMs = config.preStartTimeoutMs ?? DEFAULT_PRE_START_TIMEOUT_MS; + this.preStartTimeoutMs = resolveTimerTimeoutMs( + config.preStartTimeoutMs, + DEFAULT_PRE_START_TIMEOUT_MS, + ); this.maxPendingConnections = config.maxPendingConnections ?? DEFAULT_MAX_PENDING_CONNECTIONS; this.maxPendingConnectionsPerIp = config.maxPendingConnectionsPerIp ?? DEFAULT_MAX_PENDING_CONNECTIONS_PER_IP;