From 99fc323675d2cab228ea5d83064365bd75842166 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Wed, 15 Apr 2026 22:15:42 +0200 Subject: [PATCH] =?utf8?q?fix(cli):=20validate=20connect=20timeout=20budge?= =?utf8?q?t=20=E2=80=94=20reject=20NaN/0/negative=20before=20race?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- ui/cli/src/client/lifecycle.ts | 3 +++ ui/cli/tests/lifecycle.test.ts | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/ui/cli/src/client/lifecycle.ts b/ui/cli/src/client/lifecycle.ts index bf1f69e7..00fc1093 100644 --- a/ui/cli/src/client/lifecycle.ts +++ b/ui/cli/src/client/lifecycle.ts @@ -46,6 +46,9 @@ export const executeCommand = async (options: ExecuteOptions): Promise => activeClient = client const budget = timeoutMs ?? UI_WEBSOCKET_REQUEST_TIMEOUT_MS + if (!Number.isFinite(budget) || budget <= 0) { + throw new Error(`Invalid timeout: ${String(budget)}ms (must be > 0)`) + } const startTime = Date.now() let connectTimeoutId: ReturnType | undefined diff --git a/ui/cli/tests/lifecycle.test.ts b/ui/cli/tests/lifecycle.test.ts index a8ca7aa4..d5f4a4ad 100644 --- a/ui/cli/tests/lifecycle.test.ts +++ b/ui/cli/tests/lifecycle.test.ts @@ -34,4 +34,21 @@ await describe('lifecycle', async () => { await it('should export executeCommand function', () => { assert.strictEqual(typeof executeCommand, 'function') }) + + await it('should reject executeCommand with NaN timeout', async () => { + await assert.rejects( + executeCommand({ + config: { host: 'localhost', port: 8080, protocol: 'ui', version: '0.0.1' }, + formatter: { error: () => undefined, output: () => undefined }, + payload: {}, + procedureName: 'listChargingStations' as never, + timeoutMs: Number.NaN, + }), + (error: Error) => { + assert.ok(error.message.includes('Invalid timeout')) + assert.ok(error.message.includes('NaN')) + return true + } + ) + }) }) -- 2.53.0