From 1d1ca238c9c513e62b21915bdf147337a80ce0ff Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 18 May 2026 13:42:21 +0000 Subject: [PATCH 1/2] Initial plan From f70c716d1169b7d744270508ae8b29e51c51dec1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 18 May 2026 13:58:17 +0000 Subject: [PATCH 2/2] fix: normalize copilot auth token placeholders --- containers/api-proxy/providers/copilot.js | 9 ++++++--- containers/api-proxy/server.auth.test.js | 24 ++++++++++++++++++++++- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/containers/api-proxy/providers/copilot.js b/containers/api-proxy/providers/copilot.js index 308608225..bb938ff16 100644 --- a/containers/api-proxy/providers/copilot.js +++ b/containers/api-proxy/providers/copilot.js @@ -32,9 +32,11 @@ const { URL } = require('url'); // it means the sidecar received a dummy key (not a real BYOK credential) and should // fall back to COPILOT_GITHUB_TOKEN as the sole auth source. const COPILOT_PLACEHOLDER_TOKEN = 'ghu_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; +const COPILOT_DUMMY_BYOK_KEY = 'dummy-byok-key-for-offline-mode'; /** - * Strip any accidental "Bearer " prefix from a raw credential value and trim + * Strip any accidental "Bearer " or "token " prefix from a raw credential + * value and trim * surrounding whitespace. Returns undefined when the result is empty so that * callers can use `|| undefined` fall-through cleanly. * @@ -45,7 +47,7 @@ const COPILOT_PLACEHOLDER_TOKEN = 'ghu_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; * @returns {string|undefined} */ function stripBearerPrefix(value) { - return ((value || '').replace(/^\s*Bearer\s+/i, '').trim()) || undefined; + return ((value || '').replace(/^\s*(?:Bearer|token)\s+/i, '').trim()) || undefined; } /** @@ -60,7 +62,7 @@ function stripBearerPrefix(value) { */ function resolveApiKey(env) { const key = stripBearerPrefix(env.COPILOT_API_KEY); - return key === COPILOT_PLACEHOLDER_TOKEN ? undefined : key; + return key === COPILOT_PLACEHOLDER_TOKEN || key === COPILOT_DUMMY_BYOK_KEY ? undefined : key; } /** @@ -367,5 +369,6 @@ module.exports = { deriveGitHubApiTarget, deriveGitHubApiBasePath, COPILOT_PLACEHOLDER_TOKEN, + COPILOT_DUMMY_BYOK_KEY, }, }; diff --git a/containers/api-proxy/server.auth.test.js b/containers/api-proxy/server.auth.test.js index 4a51ae1a6..e599d7d14 100644 --- a/containers/api-proxy/server.auth.test.js +++ b/containers/api-proxy/server.auth.test.js @@ -6,7 +6,13 @@ const { shouldStripHeader } = require('./proxy-utils'); const { - _testing: { resolveCopilotAuthToken, resolveApiKey, stripBearerPrefix, COPILOT_PLACEHOLDER_TOKEN }, + _testing: { + resolveCopilotAuthToken, + resolveApiKey, + stripBearerPrefix, + COPILOT_PLACEHOLDER_TOKEN, + COPILOT_DUMMY_BYOK_KEY, + }, createCopilotAdapter, } = require('./providers/copilot'); const { sanitizeNullToolCallTypes } = require('./body-transform'); @@ -55,6 +61,11 @@ describe('stripBearerPrefix', () => { expect(stripBearerPrefix('BEARER sk-or-v1-abc')).toBe('sk-or-v1-abc'); }); + it('strips "token " prefix case-insensitively', () => { + expect(stripBearerPrefix('token sk-or-v1-abc')).toBe('sk-or-v1-abc'); + expect(stripBearerPrefix('TOKEN sk-or-v1-abc')).toBe('sk-or-v1-abc'); + }); + it('strips leading whitespace before "Bearer "', () => { expect(stripBearerPrefix(' Bearer sk-or-v1-abc')).toBe('sk-or-v1-abc'); }); @@ -155,6 +166,13 @@ describe('resolveCopilotAuthToken', () => { COPILOT_API_KEY: COPILOT_PLACEHOLDER_TOKEN, })).toBe('gho_real_token'); }); + + it('uses COPILOT_GITHUB_TOKEN when COPILOT_API_KEY is the offline BYOK dummy key', () => { + expect(resolveCopilotAuthToken({ + COPILOT_GITHUB_TOKEN: 'gho_real_token', + COPILOT_API_KEY: COPILOT_DUMMY_BYOK_KEY, + })).toBe('gho_real_token'); + }); }); describe('resolveApiKey', () => { @@ -166,6 +184,10 @@ describe('resolveApiKey', () => { expect(resolveApiKey({ COPILOT_API_KEY: COPILOT_PLACEHOLDER_TOKEN })).toBeUndefined(); }); + it('returns undefined when COPILOT_API_KEY is the offline BYOK dummy key', () => { + expect(resolveApiKey({ COPILOT_API_KEY: COPILOT_DUMMY_BYOK_KEY })).toBeUndefined(); + }); + it('returns undefined when COPILOT_API_KEY is not set', () => { expect(resolveApiKey({})).toBeUndefined(); });