From 85529dc98f3f422bf1cbbded911aa2d608085ac5 Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Mon, 14 Sep 2026 13:24:38 -0700 Subject: [PATCH 1/2] chore: clean post-purge stale references --- .oxlintrc.json | 2 +- scripts/ci-timings.json | 5 +---- src/tui/gutter-labels.test.ts | 6 +----- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/.oxlintrc.json b/.oxlintrc.json index eb36056c9..35dc9ed42 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -91,7 +91,7 @@ } }, { - "files": ["src/tui/smoke.ts", "src/tui/demo.ts"], + "files": ["src/tui/smoke.ts"], "rules": { "no-console": "off" } diff --git a/scripts/ci-timings.json b/scripts/ci-timings.json index 782abe35b..1bbe5abca 100644 --- a/scripts/ci-timings.json +++ b/scripts/ci-timings.json @@ -474,7 +474,6 @@ "src/util/control-char-strip.test.ts": 1, "src/util/tool-output-uri.test.ts": 1, "tests/helpers/defined.test.ts": 1, - "tests/unit/codex-usage.test.ts": 1, "tests/unit/context-window.test.ts": 1, "tests/unit/director.test.ts": 1, "tests/unit/faremeter.test.ts": 1, @@ -491,7 +490,6 @@ "tests/unit/tui/theme.test.ts": 1, "tests/unit/tui/tool-formatter-web-brand.test.ts": 1, "tests/unit/tui/url-links.test.ts": 1, - "tests/unit/workflow-kickoff.test.ts": 1, "tests/unit/workflows-capabilities.test.ts": 1, "src/agent/directors/counsel/package.test.ts": 0, "src/inference-abort.test.ts": 0, @@ -510,7 +508,6 @@ "tests/unit/mcp-stdio-env.test.ts": 0, "tests/unit/provider-protocol-flags.test.ts": 0, "tests/unit/run-agent.test.ts": 0, - "tests/unit/session/run-sink-exec-status.test.ts": 0, - "tests/unit/xai-usage.test.ts": 0 + "tests/unit/session/run-sink-exec-status.test.ts": 0 } } diff --git a/src/tui/gutter-labels.test.ts b/src/tui/gutter-labels.test.ts index c27ccb7b0..fab23da65 100644 --- a/src/tui/gutter-labels.test.ts +++ b/src/tui/gutter-labels.test.ts @@ -126,11 +126,7 @@ describe("transcript gutter labels", () => { const captured = new Set(); const unrecognized: string[] = []; for (const relative of files) { - if ( - relative.endsWith(".test.ts") || - relative === "demo.ts" || - relative.endsWith("/demo.ts") - ) { + if (relative.endsWith(".test.ts")) { continue; } const source = await Bun.file(join(tuiDir, relative)).text(); From 02cbf60aa85531cc5c9f42dc9dd6fa12438c5210 Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Mon, 14 Sep 2026 18:46:28 -0700 Subject: [PATCH 2/2] fix(auth): omit empty account id and pin auth header boundaries (#1064) --- src/auth/codex/auth-headers.ts | 5 +- src/auth/oauth-scope-check.test.ts | 100 +++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 2 deletions(-) diff --git a/src/auth/codex/auth-headers.ts b/src/auth/codex/auth-headers.ts index 3e50d63ca..7b02f2e64 100644 --- a/src/auth/codex/auth-headers.ts +++ b/src/auth/codex/auth-headers.ts @@ -12,7 +12,8 @@ export function codexAuthHeadersForToken( originator: CODEX_ORIGINATOR, "user-agent": `${commandName} (${CODEX_ORIGINATOR}/${CODEX_CLIENT_VERSION})`, }; - if (token.accountId !== undefined) - headers["chatgpt-account-id"] = token.accountId; + // An empty account id carries no identity — sending it as a header value + // would label the request with a meaningless id. Only a non-empty id rides. + if (token.accountId) headers["chatgpt-account-id"] = token.accountId; return headers; } diff --git a/src/auth/oauth-scope-check.test.ts b/src/auth/oauth-scope-check.test.ts index 87ca00c32..2f31c6ef7 100644 --- a/src/auth/oauth-scope-check.test.ts +++ b/src/auth/oauth-scope-check.test.ts @@ -1,6 +1,7 @@ import { afterEach, describe, expect, test } from "bun:test"; import { checkOAuthProviderScope } from "./oauth-scope-check.js"; +import { XAI_BASE_URL } from "./xai/constants.js"; const commandName = "test-cli"; @@ -119,6 +120,51 @@ describe("checkOAuthProviderScope", () => { } }); + test("codex: empty account id omits the account header", async () => { + stubFetch((_url, init) => { + const headers = init?.headers as Record; + expect(headers.authorization).toBe("Bearer staged-codex-token"); + expect("chatgpt-account-id" in headers).toBe(false); + return new Response(JSON.stringify({ models: ["gpt-5"] }), { + status: 200, + }); + }); + const result = await checkOAuthProviderScope( + "codex", + { ...codexTokens, accountId: "" }, + commandName, + ); + expect(result.status).toBe("ok"); + }); + + test("codex: fail-closed on empty access (Bearer probe classifies blocked)", async () => { + stubFetch((_url, init) => { + const headers = init?.headers as Record; + expect(headers.authorization).toBe("Bearer "); + return new Response("nope", { status: 401 }); + }); + const result = await checkOAuthProviderScope( + "codex", + { ...codexTokens, access: "" }, + commandName, + ); + expect(result.status).toBe("blocked"); + }); + + test("codex: fail-closed on garbage access (Bearer probe classifies blocked)", async () => { + stubFetch((_url, init) => { + const headers = init?.headers as Record; + expect(headers.authorization).toBe("Bearer !!!not-a-token!!!"); + return new Response("forbidden", { status: 403 }); + }); + const result = await checkOAuthProviderScope( + "codex", + { ...codexTokens, access: "!!!not-a-token!!!" }, + commandName, + ); + expect(result.status).toBe("blocked"); + }); + test("codex: blocks a definitive 401", async () => { stubFetch(() => new Response("nope", { status: 401 })); const result = await checkOAuthProviderScope( @@ -220,6 +266,60 @@ describe("checkOAuthProviderScope", () => { expect(result.status).toBe("blocked"); }); + test("xai: non-JWT token omits the user-id header, keeps authorization", async () => { + stubFetch((_url, init) => { + const headers = init?.headers as Record; + expect(headers.authorization).toBe("Bearer not-a-jwt"); + expect("x-grok-user-id" in headers).toBe(false); + return new Response(JSON.stringify({ data: [] }), { status: 200 }); + }); + const result = await checkOAuthProviderScope( + "xai", + { ...xaiTokens, access: "not-a-jwt" }, + commandName, + ); + expect(result.status).toBe("ok"); + }); + + test("xai: fail-closed on empty access (Bearer probe classifies blocked)", async () => { + stubFetch((_url, init) => { + const headers = init?.headers as Record; + expect(headers.authorization).toBe("Bearer "); + return new Response("nope", { status: 401 }); + }); + const result = await checkOAuthProviderScope( + "xai", + { ...xaiTokens, access: "" }, + commandName, + ); + expect(result.status).toBe("blocked"); + }); + + test("xai: fail-closed on garbage access (Bearer probe classifies blocked)", async () => { + stubFetch((_url, init) => { + const headers = init?.headers as Record; + expect(headers.authorization).toBe("Bearer !!!not-a-token!!!"); + return new Response("forbidden", { status: 403 }); + }); + const result = await checkOAuthProviderScope( + "xai", + { ...xaiTokens, access: "!!!not-a-token!!!" }, + commandName, + ); + expect(result.status).toBe("blocked"); + }); + + test("xai: pins the probe to the fixed models URL", async () => { + const seen: string[] = []; + stubFetch((url) => { + seen.push(url); + return new Response(JSON.stringify({ data: [] }), { status: 200 }); + }); + const result = await checkOAuthProviderScope("xai", xaiTokens, commandName); + expect(result.status).toBe("ok"); + expect(seen).toEqual([`${XAI_BASE_URL}/models`]); + }); + test("xai: unavailable on a timeout-style abort", async () => { stubFetch(() => { throw new DOMException("The operation timed out.", "TimeoutError");