From a9c8cb4e2a96086af4d11cf07f68be2518a13f46 Mon Sep 17 00:00:00 2001 From: Merge Sim Date: Tue, 1 Sep 2026 13:41:55 -0700 Subject: [PATCH] fix(claude): scrub case-insensitive Windows auth env --- src/main/claude-accounts/environment.ts | 13 ++++++++++--- .../claude-child-process-environment.test.ts | 15 +++++++++++++++ .../claude/claude-child-process-environment.ts | 9 ++++++++- .../claude/claude-structured-launch-resolution.ts | 9 ++++++++- 4 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/main/claude-accounts/environment.ts b/src/main/claude-accounts/environment.ts index 83fe3b40209..101be8995dd 100644 --- a/src/main/claude-accounts/environment.ts +++ b/src/main/claude-accounts/environment.ts @@ -13,14 +13,21 @@ export type ClaudeEnvPatch = { export function applyClaudeEnvPatch( baseEnv: Record, patch: ClaudeEnvPatch, - options?: { stripAuthEnv?: boolean } + options?: { stripAuthEnv?: boolean; platform?: NodeJS.Platform } ): Record { if (options?.stripAuthEnv) { for (const key of CLAUDE_AUTH_ENV_VARS) { delete baseEnv[key] } - if (isAuthLikeCustomHeaders(baseEnv.ANTHROPIC_CUSTOM_HEADERS)) { - delete baseEnv.ANTHROPIC_CUSTOM_HEADERS + const platform = options.platform ?? process.platform + for (const key of Object.keys(baseEnv)) { + const normalized = platform === 'win32' ? key.toUpperCase() : key + if ( + (platform === 'win32' && CLAUDE_AUTH_ENV_VARS.some((authKey) => authKey === normalized)) || + (normalized === 'ANTHROPIC_CUSTOM_HEADERS' && isAuthLikeCustomHeaders(baseEnv[key])) + ) { + delete baseEnv[key] + } } } diff --git a/src/main/claude/claude-child-process-environment.test.ts b/src/main/claude/claude-child-process-environment.test.ts index 9dc7007f07b..5f2ec48fdd2 100644 --- a/src/main/claude/claude-child-process-environment.test.ts +++ b/src/main/claude/claude-child-process-environment.test.ts @@ -1,7 +1,22 @@ import { describe, expect, it } from 'vitest' +import { applyClaudeEnvPatch } from '../claude-accounts/environment' import { buildClaudeChildProcessEnv } from './claude-child-process-environment' describe('Claude child process environment', () => { + it('strips case-insensitive auth headers through the shared env patch on Windows', () => { + expect( + applyClaudeEnvPatch( + { + anthropic_api_key: 'inherited-key', + Anthropic_Custom_Headers: 'Authorization: inherited', + SAFE_VALUE: 'preserved' + }, + {}, + { stripAuthEnv: true, platform: 'win32' } + ) + ).toEqual({ SAFE_VALUE: 'preserved' }) + }) + it('strips case-insensitive inherited auth and session stamps on Windows', () => { const env = buildClaudeChildProcessEnv( { diff --git a/src/main/claude/claude-child-process-environment.ts b/src/main/claude/claude-child-process-environment.ts index ad5e854d4ff..164d9e686fc 100644 --- a/src/main/claude/claude-child-process-environment.ts +++ b/src/main/claude/claude-child-process-environment.ts @@ -22,7 +22,14 @@ export function buildClaudeChildProcessEnv( ): Record { const inheritedEnv = options.inheritedEnv ?? process.env const platform = options.platform ?? process.platform - const env = applyClaudeEnvPatch(cloneProcessEnv(inheritedEnv), {}, { stripAuthEnv: true }) + const env = applyClaudeEnvPatch( + cloneProcessEnv(inheritedEnv), + {}, + { + stripAuthEnv: true, + platform + } + ) if (platform === 'win32') { const authKeys = new Set(CLAUDE_AUTH_ENV_VARS.map((key) => key.toUpperCase())) for (const [key, value] of Object.entries(env)) { diff --git a/src/main/claude/claude-structured-launch-resolution.ts b/src/main/claude/claude-structured-launch-resolution.ts index 202dc1d0244..cbebb703cf1 100644 --- a/src/main/claude/claude-structured-launch-resolution.ts +++ b/src/main/claude/claude-structured-launch-resolution.ts @@ -109,7 +109,14 @@ export function createClaudeStructuredLaunchResolver( const env = withCliRuntimeOnPath( command, { - ...applyClaudeEnvPatch(cloneDefinedEnv(process.env), {}, { stripAuthEnv: true }), + ...applyClaudeEnvPatch( + cloneDefinedEnv(process.env), + {}, + { + stripAuthEnv: true, + platform: process.platform + } + ), ...(overlay ? cloneDefinedEnv(overlay) : {}) }, { platform: process.platform }