diff --git a/src/server/responses/core.ts b/src/server/responses/core.ts index 9b85c9c025..7ed37bda2b 100644 --- a/src/server/responses/core.ts +++ b/src/server/responses/core.ts @@ -3,7 +3,6 @@ import { bridgeToResponsesSSE, buildResponseJSON, formatErrorResponse, type Resp import { formatPassthroughUpstreamError } from "./passthrough-error"; import { describeUpstreamConnectFailure } from "./upstream-error"; import { - getConfigPath, multiAgentGuidanceEnabled, resolveEnvValue, } from "../../config"; @@ -1688,7 +1687,7 @@ async function handleResponsesInner( return formatErrorResponse( 400, "invalid_request_error", - `${err.message}. Remove or reconfigure provider '${route.providerName}' in ${getConfigPath()}.`, + `${err.message}. Remove or reconfigure provider '${route.providerName}' in the OpenCodex configuration.`, ); } return formatErrorResponse(401, "authentication_error", err instanceof Error ? err.message : String(err)); diff --git a/tests/oauth-status-privacy.test.ts b/tests/oauth-status-privacy.test.ts index 57880d93c1..2a4637b49e 100644 --- a/tests/oauth-status-privacy.test.ts +++ b/tests/oauth-status-privacy.test.ts @@ -3,6 +3,8 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync import { join } from "node:path"; import { getLoginStatus, getValidAccessToken, UnsupportedOAuthProviderError } from "../src/oauth"; import { saveCredential } from "../src/oauth/store"; +import { handleResponses } from "../src/server/responses"; +import type { OcxConfig } from "../src/types"; const TEST_DIR = join(import.meta.dir, ".tmp-oauth-status-privacy-test"); let previousOpencodexHome: string | undefined; @@ -106,6 +108,37 @@ describe("OAuth status privacy", () => { await expect(getValidAccessToken("removed-provider")).rejects.toBeInstanceOf(UnsupportedOAuthProviderError); }); + test("stale OAuth provider responses do not disclose the config path", async () => { + await saveCredential("removed-provider", { + access: "access-token", + refresh: "refresh-token", + expires: Date.now() + 60_000, + }); + const config = { + defaultProvider: "removed-provider", + providers: { + "removed-provider": { + adapter: "openai-responses", + authMode: "oauth", + baseUrl: "https://provider.example/v1", + }, + }, + } as OcxConfig; + + const response = await handleResponses(new Request("http://localhost/v1/responses", { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ model: "test-model", input: "hello", stream: false }), + }), config, { model: "", provider: "" }); + const body = await response.text(); + + expect(body).toContain("Unsupported OAuth provider"); + expect(response.status).toBe(400); + expect(body).toContain("Remove or reconfigure provider 'removed-provider'"); + expect(body).not.toContain(TEST_DIR); + expect(body).not.toContain("config.json"); + }); + test("malformed oauth token store is backed up before a new credential save overwrites it", async () => { const authPath = join(TEST_DIR, "auth.json"); writeFileSync(authPath, "{not valid json", "utf8");