diff --git a/sdk/typescript/src/auth.ts b/sdk/typescript/src/auth.ts index e45a0ea7..107c0306 100644 --- a/sdk/typescript/src/auth.ts +++ b/sdk/typescript/src/auth.ts @@ -243,7 +243,9 @@ function preferredAuthUrl(value: string): string | null { )) { const url = match[0].replace(/[.,;:!?)\]}]+$/, ""); try { - const hostname = new URL(url).hostname.toLowerCase().replace(/\.$/, ""); + const parsed = new URL(url); + if (parsed.protocol !== "https:") continue; + const hostname = parsed.hostname.toLowerCase().replace(/\.$/, ""); if ( hostname !== "localhost" && !hostname.endsWith(".localhost") && diff --git a/sdk/typescript/tests-ts/auth-http-url.test.ts b/sdk/typescript/tests-ts/auth-http-url.test.ts new file mode 100644 index 00000000..91831d4a --- /dev/null +++ b/sdk/typescript/tests-ts/auth-http-url.test.ts @@ -0,0 +1,33 @@ +import { mkdtemp, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { expect, test } from "bun:test"; +import { CodexLoginHandle } from "../src/auth.js"; + +test("skips external plaintext HTTP authentication URLs", async () => { + const root = await mkdtemp(join(tmpdir(), "codex-security-auth-http-")); + const script = join(root, "login.mjs"); + try { + await writeFile( + script, + ` +console.error("Open http://auth.example.test/insecure"); +console.error("Open https://auth.example.test/device"); +console.error("User code: ABCD-EFGH"); +process.exit(0); +`, + ); + const handle = new CodexLoginHandle( + { command: process.execPath }, + [script], + process.env, + () => {}, + ); + + await expect(handle.wait()).resolves.toMatchObject({ success: true }); + expect(handle.verificationUrl).toBe("https://auth.example.test/device"); + expect(handle.userCode).toBe("ABCD-EFGH"); + } finally { + await rm(root, { recursive: true, force: true }); + } +});