From 63bba283a675b4cc9cde4e7c24fcee9909091fcd Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 11 Aug 2026 03:59:58 -0700 Subject: [PATCH] test(doctor): use fixed OAuth sidecar vectors (#121986) * test(doctor): use fixed OAuth sidecar vectors * test(doctor): preserve Linux warning assertion --- .../doctor-auth-oauth-sidecar.test-support.ts | 26 ------ .../doctor-auth-oauth-sidecar.test.ts | 81 +++++-------------- src/commands/doctor-auth-oauth-sidecar.ts | 12 --- .../legacy-oauth-sidecar.test-support.ts | 15 ---- .../shared/legacy-oauth-sidecar.test.ts | 72 ++++++----------- .../doctor/shared/legacy-oauth-sidecar.ts | 52 +----------- 6 files changed, 46 insertions(+), 212 deletions(-) delete mode 100644 src/commands/doctor-auth-oauth-sidecar.test-support.ts delete mode 100644 src/commands/doctor/shared/legacy-oauth-sidecar.test-support.ts diff --git a/src/commands/doctor-auth-oauth-sidecar.test-support.ts b/src/commands/doctor-auth-oauth-sidecar.test-support.ts deleted file mode 100644 index 772f52034eb5..000000000000 --- a/src/commands/doctor-auth-oauth-sidecar.test-support.ts +++ /dev/null @@ -1,26 +0,0 @@ -import type { LegacyOAuthRef } from "./doctor/shared/legacy-oauth-sidecar.js"; -import "./doctor-auth-oauth-sidecar.js"; - -type DoctorAuthOAuthSidecarTestApi = { - buildLegacyOAuthSecretAad(params: { - ref: LegacyOAuthRef; - profileId: string; - provider: string; - }): Buffer; - buildLegacyOAuthSecretKey(seed: string): Buffer; -}; - -function getTestApi(): DoctorAuthOAuthSidecarTestApi { - return (globalThis as Record)[ - Symbol.for("openclaw.doctorAuthOAuthSidecarTestApi") - ] as DoctorAuthOAuthSidecarTestApi; -} - -export const testing: DoctorAuthOAuthSidecarTestApi = { - buildLegacyOAuthSecretAad(params) { - return getTestApi().buildLegacyOAuthSecretAad(params); - }, - buildLegacyOAuthSecretKey(seed) { - return getTestApi().buildLegacyOAuthSecretKey(seed); - }, -}; diff --git a/src/commands/doctor-auth-oauth-sidecar.test.ts b/src/commands/doctor-auth-oauth-sidecar.test.ts index 8edd015afe28..3e9025aa9680 100644 --- a/src/commands/doctor-auth-oauth-sidecar.test.ts +++ b/src/commands/doctor-auth-oauth-sidecar.test.ts @@ -1,5 +1,4 @@ // Doctor OAuth sidecar tests cover encrypted sidecar detection and auth repair guidance. -import { createCipheriv } from "node:crypto"; import fs from "node:fs"; import path from "node:path"; import { afterEach, describe, expect, it, vi } from "vitest"; @@ -9,7 +8,6 @@ import { type OpenClawTestState, } from "../test-utils/openclaw-test-state.js"; import { maybeRepairLegacyOAuthSidecarProfiles } from "./doctor-auth-oauth-sidecar.js"; -import { testing } from "./doctor-auth-oauth-sidecar.test-support.js"; import type { DoctorPrompter } from "./doctor-prompter.js"; const states: OpenClawTestState[] = []; @@ -54,34 +52,6 @@ function writeLegacyAuthProfiles( return state.writeJson(path.join("agents", agentId, "agent", "auth-profiles.json"), store); } -function encryptLegacySidecarMaterial(params: { - ref: { source: "openclaw-credentials"; provider: "openai-codex"; id: string }; - profileId: string; - provider: string; - seed: string; - material: Record; -}) { - const iv = Buffer.alloc(12, 7); - const cipher = createCipheriv("aes-256-gcm", testing.buildLegacyOAuthSecretKey(params.seed), iv); - cipher.setAAD( - testing.buildLegacyOAuthSecretAad({ - ref: params.ref, - profileId: params.profileId, - provider: params.provider, - }), - ); - const ciphertext = Buffer.concat([ - cipher.update(JSON.stringify(params.material), "utf8"), - cipher.final(), - ]); - return { - algorithm: "aes-256-gcm", - iv: iv.toString("base64url"), - tag: cipher.getAuthTag().toString("base64url"), - ciphertext: ciphertext.toString("base64url"), - }; -} - afterEach(async () => { clearRuntimeAuthProfileStoreSnapshots(); for (const state of states.splice(0)) { @@ -126,17 +96,13 @@ describe("maybeRepairLegacyOAuthSidecarProfiles", () => { version: 1, profileId, provider: "openai-codex", - encrypted: encryptLegacySidecarMaterial({ - ref, - profileId, - provider: "openai-codex", - seed, - material: { - access: "access-token", - refresh: "refresh-token", - idToken: "id-token", - }, - }), + encrypted: { + algorithm: "aes-256-gcm", + iv: "BwcHBwcHBwcHBwcH", + tag: "gSm_Lg58EVO-5wZGQlWHEA", + ciphertext: + "4qrZ4-zdgUdttB3gTUNORWdtO4gqLiFgTsilUX3-9RZiN2MLkCDdxQXQ2GfeqN1zi1qb9iURwK0sO0TJZfxO3zULMKNlRgUT", + }, }, ); @@ -231,16 +197,12 @@ describe("maybeRepairLegacyOAuthSidecarProfiles", () => { version: 1, profileId, provider: "openai-codex", - encrypted: encryptLegacySidecarMaterial({ - ref, - profileId, - provider: "openai-codex", - seed: "right-seed", - material: { - access: "access-token", - refresh: "refresh-token", - }, - }), + encrypted: { + algorithm: "aes-256-gcm", + iv: "BwcHBwcHBwcHBwcH", + tag: "ZHGhT2cekYFZCOxu8pP0KA", + ciphertext: "OQPDJez2jSRH4FPxFNNkwEw7PDbClF6Ty6T2l4TLGvr6bdJfhK6VA6ccWwC1xlrR4ENA", + }, }, ); @@ -450,16 +412,13 @@ describe("maybeRepairLegacyOAuthSidecarProfiles", () => { version: 1, profileId, provider: "openai-codex", - encrypted: encryptLegacySidecarMaterial({ - ref, - profileId, - provider: "openai-codex", - seed, - material: { - access: "shared-access-token", - refresh: "shared-refresh-token", - }, - }), + encrypted: { + algorithm: "aes-256-gcm", + iv: "BwcHBwcHBwcHBwcH", + tag: "91XpNgcMQ-AVeo7NDnv11Q", + ciphertext: + "fVMsIFtJ0LX1ayciusBnyS7KulJU2dCAdkKU4yMLGYTVB-Gq0X_SvUqPTkAX_a1ZBIjGIC6nFH_3HvhWHMIX7Cs", + }, }, ); diff --git a/src/commands/doctor-auth-oauth-sidecar.ts b/src/commands/doctor-auth-oauth-sidecar.ts index 653ea1fa4ca9..5a20da547c29 100644 --- a/src/commands/doctor-auth-oauth-sidecar.ts +++ b/src/commands/doctor-auth-oauth-sidecar.ts @@ -16,7 +16,6 @@ import type { DoctorPrompter } from "./doctor-prompter.js"; import { isLegacyOAuthRef, isLegacyOAuthSidecarPayload, - legacyOAuthSidecarTestUtils, loadLegacyOAuthSidecarMaterial, resolveLegacyOAuthSidecarPath, type LegacyOAuthRef, @@ -329,14 +328,3 @@ export async function maybeRepairLegacyOAuthSidecarProfiles(params: { } return result; } - -const testing = { - buildLegacyOAuthSecretAad: legacyOAuthSidecarTestUtils.buildLegacyOAuthSecretAad, - buildLegacyOAuthSecretKey: legacyOAuthSidecarTestUtils.buildLegacyOAuthSecretKey, -}; - -if (process.env.VITEST || process.env.NODE_ENV === "test") { - (globalThis as Record)[ - Symbol.for("openclaw.doctorAuthOAuthSidecarTestApi") - ] = testing; -} diff --git a/src/commands/doctor/shared/legacy-oauth-sidecar.test-support.ts b/src/commands/doctor/shared/legacy-oauth-sidecar.test-support.ts deleted file mode 100644 index b9646f7e0cb4..000000000000 --- a/src/commands/doctor/shared/legacy-oauth-sidecar.test-support.ts +++ /dev/null @@ -1,15 +0,0 @@ -import "./legacy-oauth-sidecar.js"; - -type TestApi = { resetKeychainOnlyMigrationHint(): void }; - -function getTestApi(): TestApi { - return (globalThis as Record)[ - Symbol.for("openclaw.legacyOAuthSidecarInternalTestApi") - ] as TestApi; -} - -export const legacyOAuthSidecarInternalTestUtils: TestApi = { - resetKeychainOnlyMigrationHint(): void { - getTestApi().resetKeychainOnlyMigrationHint(); - }, -}; diff --git a/src/commands/doctor/shared/legacy-oauth-sidecar.test.ts b/src/commands/doctor/shared/legacy-oauth-sidecar.test.ts index 5eed0c2c9da5..081627c99e26 100644 --- a/src/commands/doctor/shared/legacy-oauth-sidecar.test.ts +++ b/src/commands/doctor/shared/legacy-oauth-sidecar.test.ts @@ -6,11 +6,7 @@ import { createOpenClawTestState, type OpenClawTestState, } from "../../../test-utils/openclaw-test-state.js"; -import { - legacyOAuthSidecarTestUtils, - loadLegacyOAuthSidecarMaterial, -} from "./legacy-oauth-sidecar.js"; -import { legacyOAuthSidecarInternalTestUtils } from "./legacy-oauth-sidecar.test-support.js"; +import { loadLegacyOAuthSidecarMaterial } from "./legacy-oauth-sidecar.js"; const states: OpenClawTestState[] = []; @@ -48,13 +44,12 @@ async function writeLegacySidecarThatNeedsKeychain(): Promise<{ version: 1, profileId, provider: "openai-codex", - encrypted: legacyOAuthSidecarTestUtils.encryptLegacyOAuthMaterial({ - ref, - profileId, - provider: "openai-codex", - seed: "only-in-keychain", - material: { access: "a", refresh: "b", idToken: "c" }, - }), + encrypted: { + algorithm: "aes-256-gcm", + iv: "AQIDBAUGBwgJCgsM", + tag: "G1t3MG1wjsZq17LOSqvu8w", + ciphertext: "nkPkvPO-ZilcU9XIoVzMfskmxKVmknxIjFkNw3yLMhiP3d5--KdbiMub", + }, }); return { state, ref, profileId }; } @@ -63,7 +58,6 @@ afterEach(async () => { for (const state of states.splice(0)) { await state.cleanup(); } - legacyOAuthSidecarInternalTestUtils.resetKeychainOnlyMigrationHint(); }); describe("loadLegacyOAuthSidecarMaterial keychain-only headless warning", () => { @@ -96,48 +90,32 @@ describe("loadLegacyOAuthSidecarMaterial keychain-only headless warning", () => return env; } - it("emits a single doctor-pointer warning when only Keychain can decrypt and prompts are disabled", async () => { + it("emits one doctor-pointer warning only on Darwin", async () => { const { state, ref, profileId } = await writeLegacySidecarThatNeedsKeychain(); const env = envWithoutVitestSignals(state); + const load = () => + loadLegacyOAuthSidecarMaterial({ + ref, + profileId, + provider: "openai-codex", + allowKeychainPrompt: false, + env, + }); - const firstAttempt = loadLegacyOAuthSidecarMaterial({ - ref, - profileId, - provider: "openai-codex", - allowKeychainPrompt: false, - env, - }); - expect(firstAttempt).toBeNull(); + restorePlatform(); + restorePlatform = setPlatform("linux"); + expect(load()).toBeNull(); + expect(warnSpy).not.toHaveBeenCalled(); + + restorePlatform(); + restorePlatform = setPlatform("darwin"); + expect(load()).toBeNull(); expect(warnSpy).toHaveBeenCalledTimes(1); const [firstMessage] = warnSpy.mock.calls[0] as [unknown]; expect(String(firstMessage)).toContain("openclaw doctor --fix"); expect(String(firstMessage)).toContain("macOS Keychain"); - const secondAttempt = loadLegacyOAuthSidecarMaterial({ - ref, - profileId, - provider: "openai-codex", - allowKeychainPrompt: false, - env, - }); - expect(secondAttempt).toBeNull(); + expect(load()).toBeNull(); expect(warnSpy).toHaveBeenCalledTimes(1); }); - - it("does not emit the doctor-pointer warning on non-darwin platforms", async () => { - restorePlatform(); - restorePlatform = setPlatform("linux"); - const { state, ref, profileId } = await writeLegacySidecarThatNeedsKeychain(); - const env = envWithoutVitestSignals(state); - - const attempt = loadLegacyOAuthSidecarMaterial({ - ref, - profileId, - provider: "openai-codex", - allowKeychainPrompt: false, - env, - }); - expect(attempt).toBeNull(); - expect(warnSpy).not.toHaveBeenCalled(); - }); }); diff --git a/src/commands/doctor/shared/legacy-oauth-sidecar.ts b/src/commands/doctor/shared/legacy-oauth-sidecar.ts index 940497238d41..fa8bb9ef8cf8 100644 --- a/src/commands/doctor/shared/legacy-oauth-sidecar.ts +++ b/src/commands/doctor/shared/legacy-oauth-sidecar.ts @@ -1,6 +1,6 @@ // Legacy OAuth sidecar reader for migrating encrypted auth-profile secret material. import * as childProcess from "node:child_process"; -import { createCipheriv, createDecipheriv, hash } from "node:crypto"; +import { createDecipheriv, hash } from "node:crypto"; import fs from "node:fs"; import os from "node:os"; import path from "node:path"; @@ -113,38 +113,6 @@ function buildLegacyOAuthSecretKey(seed: string): Buffer { return hash("sha256", `openclaw:auth-profile-oauth:${seed}`, "buffer"); } -function encryptLegacyOAuthMaterialForTest(params: { - ref: LegacyOAuthRef; - profileId: string; - provider: string; - seed: string; - material: Record; -}): LegacyOAuthEncryptedPayload { - const iv = Buffer.from("0102030405060708090a0b0c", "hex"); - const cipher = createCipheriv( - LEGACY_OAUTH_SECRET_ALGORITHM, - buildLegacyOAuthSecretKey(params.seed), - iv, - ); - cipher.setAAD( - buildLegacyOAuthSecretAad({ - ref: params.ref, - profileId: params.profileId, - provider: params.provider, - }), - ); - const ciphertext = Buffer.concat([ - cipher.update(JSON.stringify(params.material), "utf8"), - cipher.final(), - ]); - return { - algorithm: LEGACY_OAUTH_SECRET_ALGORITHM, - iv: iv.toString("base64url"), - tag: cipher.getAuthTag().toString("base64url"), - ciphertext: ciphertext.toString("base64url"), - }; -} - function uniquePaths(paths: Array): string[] { return uniqueStrings(paths.filter((entry): entry is string => Boolean(entry))); } @@ -342,18 +310,6 @@ function emitKeychainOnlyMigrationHintOnce(profileId: string): void { ); } -const legacyOAuthSidecarInternalTestUtils = { - resetKeychainOnlyMigrationHint(): void { - keychainOnlyMigrationHintEmitted = false; - }, -}; - -if (process.env.VITEST || process.env.NODE_ENV === "test") { - (globalThis as Record)[ - Symbol.for("openclaw.legacyOAuthSidecarInternalTestApi") - ] = legacyOAuthSidecarInternalTestUtils; -} - export function loadLegacyOAuthSidecarMaterial(params: { ref: LegacyOAuthRef; profileId: string; @@ -386,9 +342,3 @@ export function loadLegacyOAuthSidecarMaterial(params: { } return normalizeLegacyOAuthSecretMaterial(raw); } - -export const legacyOAuthSidecarTestUtils = { - buildLegacyOAuthSecretAad, - buildLegacyOAuthSecretKey, - encryptLegacyOAuthMaterial: encryptLegacyOAuthMaterialForTest, -};