From a2f5ac82d516f88c2345389064cdfd039959336c Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Thu, 18 Jun 2026 20:50:45 +0200 Subject: [PATCH] fix(e2e): reject loose credential numeric limits --- scripts/e2e/telegram-user-credential.ts | 21 ++++++++----- test/scripts/telegram-user-credential.test.ts | 31 ++++++++++++++++++- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/scripts/e2e/telegram-user-credential.ts b/scripts/e2e/telegram-user-credential.ts index 040002ffd601..813ae5507111 100644 --- a/scripts/e2e/telegram-user-credential.ts +++ b/scripts/e2e/telegram-user-credential.ts @@ -23,18 +23,22 @@ const DEFAULT_CHUNKED_PAYLOAD_MAX_CHUNKS = 4096; const COMMAND_TIMEOUT_MS = optionalPositiveInteger( process.env.OPENCLAW_TELEGRAM_USER_CREDENTIAL_COMMAND_TIMEOUT_MS?.trim(), 120_000, + "OPENCLAW_TELEGRAM_USER_CREDENTIAL_COMMAND_TIMEOUT_MS", ); const BROKER_TIMEOUT_MS = optionalPositiveInteger( process.env.OPENCLAW_TELEGRAM_USER_CREDENTIAL_BROKER_TIMEOUT_MS?.trim(), 30_000, + "OPENCLAW_TELEGRAM_USER_CREDENTIAL_BROKER_TIMEOUT_MS", ); const CHUNKED_PAYLOAD_MAX_BYTES = optionalPositiveInteger( process.env.OPENCLAW_QA_CREDENTIAL_PAYLOAD_MAX_BYTES?.trim(), DEFAULT_CHUNKED_PAYLOAD_MAX_BYTES, + "OPENCLAW_QA_CREDENTIAL_PAYLOAD_MAX_BYTES", ); const CHUNKED_PAYLOAD_MAX_CHUNKS = optionalPositiveInteger( process.env.OPENCLAW_QA_CREDENTIAL_PAYLOAD_MAX_CHUNKS?.trim(), DEFAULT_CHUNKED_PAYLOAD_MAX_CHUNKS, + "OPENCLAW_QA_CREDENTIAL_PAYLOAD_MAX_CHUNKS", ); function usage(): never { @@ -156,16 +160,17 @@ function optionalString(source: JsonObject, key: string) { return undefined; } -function optionalPositiveInteger(value: string | undefined, fallback: number) { - if (!value) { +function optionalPositiveInteger(value: string | undefined, fallback: number, label = "value") { + const text = value?.trim(); + if (!text) { return fallback; } - if (!/^\d+$/u.test(value)) { - throw new Error(`Expected positive integer, got ${value}.`); + if (!/^\d+$/u.test(text)) { + throw new Error(`${label} must be a positive integer. Got: ${JSON.stringify(text)}.`); } - const parsed = Number(value); + const parsed = Number(text); if (!Number.isSafeInteger(parsed) || parsed < 1) { - throw new Error(`Expected positive integer, got ${value}.`); + throw new Error(`${label} must be a positive integer. Got: ${JSON.stringify(text)}.`); } return parsed; } @@ -256,12 +261,14 @@ async function resolveConvexLeaseConfig(opts: Map) { process.env.OPENCLAW_QA_CREDENTIAL_LEASE_TTL_MS?.trim() || fileEnv.OPENCLAW_QA_CREDENTIAL_LEASE_TTL_MS, 20 * 60 * 1_000, + "OPENCLAW_QA_CREDENTIAL_LEASE_TTL_MS", ), heartbeatIntervalMs: optionalPositiveInteger( opts.get("heartbeat-interval-ms") || process.env.OPENCLAW_QA_CREDENTIAL_HEARTBEAT_INTERVAL_MS?.trim() || fileEnv.OPENCLAW_QA_CREDENTIAL_HEARTBEAT_INTERVAL_MS, 30_000, + "OPENCLAW_QA_CREDENTIAL_HEARTBEAT_INTERVAL_MS", ), ownerId: opts.get("owner-id") || @@ -633,4 +640,4 @@ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) await main(); } -export { parseChunkedPayloadMarker }; +export { optionalPositiveInteger, parseChunkedPayloadMarker }; diff --git a/test/scripts/telegram-user-credential.test.ts b/test/scripts/telegram-user-credential.test.ts index 3bbb5687c57e..c9acebc41b7c 100644 --- a/test/scripts/telegram-user-credential.test.ts +++ b/test/scripts/telegram-user-credential.test.ts @@ -142,7 +142,9 @@ describe("telegram user credential IO", () => { import( `${new URL("../../scripts/e2e/telegram-user-credential.ts", import.meta.url).href}?case=loose-timeout-${value}-${Date.now()}` ), - ).rejects.toThrow(`Expected positive integer, got ${value}.`); + ).rejects.toThrow( + `OPENCLAW_TELEGRAM_USER_CREDENTIAL_COMMAND_TIMEOUT_MS must be a positive integer. Got: ${JSON.stringify(value)}.`, + ); } } finally { if (previous === undefined) { @@ -176,6 +178,33 @@ describe("telegram user credential IO", () => { ).toThrow("Chunked payload marker exceeds 67108864 bytes."); }); + it("rejects loose numeric credential limits instead of parsing prefixes", async () => { + const credentialModule = (await import( + `${new URL("../../scripts/e2e/telegram-user-credential.ts", import.meta.url).href}?case=limits-${Date.now()}` + )) as { + optionalPositiveInteger(value: string | undefined, fallback: number, label?: string): number; + }; + + expect(credentialModule.optionalPositiveInteger(undefined, 30_000)).toBe(30_000); + expect(credentialModule.optionalPositiveInteger(" 120000 ", 30_000)).toBe(120_000); + expect(() => + credentialModule.optionalPositiveInteger( + "1e3", + 30_000, + "OPENCLAW_QA_CREDENTIAL_LEASE_TTL_MS", + ), + ).toThrow('OPENCLAW_QA_CREDENTIAL_LEASE_TTL_MS must be a positive integer. Got: "1e3".'); + expect(() => + credentialModule.optionalPositiveInteger( + "9007199254740992", + 30_000, + "OPENCLAW_QA_CREDENTIAL_PAYLOAD_MAX_BYTES", + ), + ).toThrow( + 'OPENCLAW_QA_CREDENTIAL_PAYLOAD_MAX_BYTES must be a positive integer. Got: "9007199254740992".', + ); + }); + it("fails hung child processes instead of waiting for the outer proof timeout", async () => { await expect( runCommand(process.execPath, ["-e", "setInterval(() => {}, 1000)"], undefined, {