From f10bad944ffc0466022bcb6be584929eb23e5f06 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 29 May 2026 15:59:27 -0400 Subject: [PATCH] fix(oauth): cap tls preflight timeout --- src/commands/oauth-tls-preflight.test.ts | 18 ++++++++++++++++++ src/plugins/provider-openai-codex-oauth-tls.ts | 3 ++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/commands/oauth-tls-preflight.test.ts b/src/commands/oauth-tls-preflight.test.ts index 10694e998b1..afcbe880634 100644 --- a/src/commands/oauth-tls-preflight.test.ts +++ b/src/commands/oauth-tls-preflight.test.ts @@ -1,4 +1,5 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; +import { MAX_TIMER_TIMEOUT_MS } from "../shared/number-coercion.js"; import { formatOpenAIOAuthTlsPreflightFix, runOpenAIOAuthTlsPreflight, @@ -18,6 +19,23 @@ describe("runOpenAIOAuthTlsPreflight", () => { expect(result).toEqual({ ok: true }); }); + it("caps oversized probe timeouts before creating abort signals", async () => { + const timeoutController = new AbortController(); + const timeoutSpy = vi.spyOn(AbortSignal, "timeout").mockReturnValue(timeoutController.signal); + const fetchImpl = vi.fn(async (_input: RequestInfo | URL, init?: RequestInit) => { + expect(init?.signal).toBe(timeoutController.signal); + return new Response("", { status: 400 }); + }) as unknown as typeof fetch; + + const result = await runOpenAIOAuthTlsPreflight({ + fetchImpl, + timeoutMs: Number.MAX_SAFE_INTEGER, + }); + + expect(result).toEqual({ ok: true }); + expect(timeoutSpy).toHaveBeenCalledWith(MAX_TIMER_TIMEOUT_MS); + }); + it("classifies TLS trust failures from fetch cause code", async () => { const tlsFetchImpl = vi.fn(async () => { const cause = new Error("unable to get local issuer certificate") as Error & { diff --git a/src/plugins/provider-openai-codex-oauth-tls.ts b/src/plugins/provider-openai-codex-oauth-tls.ts index 06a482cd58f..8121a043beb 100644 --- a/src/plugins/provider-openai-codex-oauth-tls.ts +++ b/src/plugins/provider-openai-codex-oauth-tls.ts @@ -1,6 +1,7 @@ import path from "node:path"; import { formatCliCommand } from "../cli/command-format.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; +import { resolveTimerTimeoutMs } from "../shared/number-coercion.js"; import { asNullableObjectRecord } from "../shared/record-coerce.js"; import { note } from "../terminal/note.js"; @@ -100,7 +101,7 @@ export async function runOpenAIOAuthTlsPreflight(options?: { timeoutMs?: number; fetchImpl?: typeof fetch; }): Promise { - const timeoutMs = options?.timeoutMs ?? 5000; + const timeoutMs = resolveTimerTimeoutMs(options?.timeoutMs, 5000); const fetchImpl = options?.fetchImpl ?? fetch; try { await fetchImpl(OPENAI_AUTH_PROBE_URL, {