source dump of claude code
at main 199 lines 7.9 kB view raw
1import { isRemoteManagedSettingsEligible } from '../services/remoteManagedSettings/syncCache.js' 2import { clearCACertsCache } from './caCerts.js' 3import { getGlobalConfig } from './config.js' 4import { isEnvTruthy } from './envUtils.js' 5import { 6 isProviderManagedEnvVar, 7 SAFE_ENV_VARS, 8} from './managedEnvConstants.js' 9import { clearMTLSCache } from './mtls.js' 10import { clearProxyCache, configureGlobalAgents } from './proxy.js' 11import { isSettingSourceEnabled } from './settings/constants.js' 12import { 13 getSettings_DEPRECATED, 14 getSettingsForSource, 15} from './settings/settings.js' 16 17/** 18 * `claude ssh` remote: ANTHROPIC_UNIX_SOCKET routes auth through a -R forwarded 19 * socket to a local proxy, and the launcher sets a handful of placeholder auth 20 * env vars that the remote's ~/.claude settings.env MUST NOT clobber (see 21 * isAnthropicAuthEnabled). Strip them from any settings-sourced env object. 22 */ 23function withoutSSHTunnelVars( 24 env: Record<string, string> | undefined, 25): Record<string, string> { 26 if (!env || !process.env.ANTHROPIC_UNIX_SOCKET) return env || {} 27 const { 28 ANTHROPIC_UNIX_SOCKET: _1, 29 ANTHROPIC_BASE_URL: _2, 30 ANTHROPIC_API_KEY: _3, 31 ANTHROPIC_AUTH_TOKEN: _4, 32 CLAUDE_CODE_OAUTH_TOKEN: _5, 33 ...rest 34 } = env 35 return rest 36} 37 38/** 39 * When the host owns inference routing (sets 40 * CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST in spawn env), strip 41 * provider-selection / model-default vars from settings-sourced env so a 42 * user's ~/.claude/settings.json can't redirect requests away from the 43 * host-configured provider. 44 */ 45function withoutHostManagedProviderVars( 46 env: Record<string, string> | undefined, 47): Record<string, string> { 48 if (!env) return {} 49 if (!isEnvTruthy(process.env.CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST)) { 50 return env 51 } 52 const out: Record<string, string> = {} 53 for (const [key, value] of Object.entries(env)) { 54 if (!isProviderManagedEnvVar(key)) { 55 out[key] = value 56 } 57 } 58 return out 59} 60 61/** 62 * Snapshot of env keys present before any settings.env is applied — for CCD, 63 * these are the keys the desktop host set to orchestrate the subprocess. 64 * Settings must not override them (OTEL_LOGS_EXPORTER=console would corrupt 65 * the stdio JSON-RPC transport). Keys added LATER by user/project settings 66 * are not in this set, so mid-session settings.json changes still apply. 67 * Lazy-captured on first applySafeConfigEnvironmentVariables() call. 68 */ 69let ccdSpawnEnvKeys: Set<string> | null | undefined 70 71function withoutCcdSpawnEnvKeys( 72 env: Record<string, string> | undefined, 73): Record<string, string> { 74 if (!env || !ccdSpawnEnvKeys) return env || {} 75 const out: Record<string, string> = {} 76 for (const [key, value] of Object.entries(env)) { 77 if (!ccdSpawnEnvKeys.has(key)) out[key] = value 78 } 79 return out 80} 81 82/** 83 * Compose the strip filters applied to every settings-sourced env object. 84 */ 85function filterSettingsEnv( 86 env: Record<string, string> | undefined, 87): Record<string, string> { 88 return withoutCcdSpawnEnvKeys( 89 withoutHostManagedProviderVars(withoutSSHTunnelVars(env)), 90 ) 91} 92 93/** 94 * Trusted setting sources whose env vars can be applied before the trust dialog. 95 * 96 * - userSettings (~/.claude/settings.json): controlled by the user, not project-specific 97 * - flagSettings (--settings CLI flag or SDK inline settings): explicitly passed by the user 98 * - policySettings (managed settings from enterprise API or local managed-settings.json): 99 * controlled by IT/admin (highest priority, cannot be overridden) 100 * 101 * Project-scoped sources (projectSettings, localSettings) are excluded because they live 102 * inside the project directory and could be committed by a malicious actor to redirect 103 * traffic (e.g., ANTHROPIC_BASE_URL) to an attacker-controlled server. 104 */ 105const TRUSTED_SETTING_SOURCES = [ 106 'userSettings', 107 'flagSettings', 108 'policySettings', 109] as const 110 111/** 112 * Apply environment variables from trusted sources to process.env. 113 * Called before the trust dialog so that user/enterprise env vars like 114 * ANTHROPIC_BASE_URL take effect during first-run/onboarding. 115 * 116 * For trusted sources (user settings, managed settings, CLI flags), ALL env vars 117 * are applied — including ones like ANTHROPIC_BASE_URL that would be dangerous 118 * from project-scoped settings. 119 * 120 * For project-scoped sources (projectSettings, localSettings), only safe env vars 121 * from the SAFE_ENV_VARS allowlist are applied. These are applied after trust is 122 * fully established via applyConfigEnvironmentVariables(). 123 */ 124export function applySafeConfigEnvironmentVariables(): void { 125 // Capture CCD spawn-env keys before any settings.env is applied (once). 126 if (ccdSpawnEnvKeys === undefined) { 127 ccdSpawnEnvKeys = 128 process.env.CLAUDE_CODE_ENTRYPOINT === 'claude-desktop' 129 ? new Set(Object.keys(process.env)) 130 : null 131 } 132 133 // Global config (~/.claude.json) is user-controlled. In CCD mode, 134 // filterSettingsEnv strips keys that were in the spawn env snapshot so 135 // the desktop host's operational vars (OTEL, etc.) are not overridden. 136 Object.assign(process.env, filterSettingsEnv(getGlobalConfig().env)) 137 138 // Apply ALL env vars from trusted setting sources, policySettings last. 139 // Gate on isSettingSourceEnabled so SDK settingSources: [] (isolation mode) 140 // doesn't get clobbered by ~/.claude/settings.json env (gh#217). policy/flag 141 // sources are always enabled, so this only ever filters userSettings. 142 for (const source of TRUSTED_SETTING_SOURCES) { 143 if (source === 'policySettings') continue 144 if (!isSettingSourceEnabled(source)) continue 145 Object.assign( 146 process.env, 147 filterSettingsEnv(getSettingsForSource(source)?.env), 148 ) 149 } 150 151 // Compute remote-managed-settings eligibility now, with userSettings and 152 // flagSettings env applied. Eligibility reads CLAUDE_CODE_USE_BEDROCK, 153 // ANTHROPIC_BASE_URL — both settable via settings.env. 154 // getSettingsForSource('policySettings') below consults the remote cache, 155 // which guards on this. The two-phase structure makes the ordering 156 // dependency visible: non-policy env → eligibility → policy env. 157 isRemoteManagedSettingsEligible() 158 159 Object.assign( 160 process.env, 161 filterSettingsEnv(getSettingsForSource('policySettings')?.env), 162 ) 163 164 // Apply only safe env vars from the fully-merged settings (which includes 165 // project-scoped sources). For safe vars that also exist in trusted sources, 166 // the merged value (which may come from a higher-priority project source) 167 // will overwrite the trusted value — this is acceptable since these vars are 168 // in the safe allowlist. Only policySettings values are guaranteed to survive 169 // unchanged (it has the highest merge priority in both loops) — except 170 // provider-routing vars, which filterSettingsEnv strips from every source 171 // when CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST is set. 172 const settingsEnv = filterSettingsEnv(getSettings_DEPRECATED()?.env) 173 for (const [key, value] of Object.entries(settingsEnv)) { 174 if (SAFE_ENV_VARS.has(key.toUpperCase())) { 175 process.env[key] = value 176 } 177 } 178} 179 180/** 181 * Apply environment variables from settings to process.env. 182 * This applies ALL environment variables (except provider-routing vars when 183 * CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST is set — see filterSettingsEnv) and 184 * should only be called after trust is established. This applies potentially 185 * dangerous environment variables such as LD_PRELOAD, PATH, etc. 186 */ 187export function applyConfigEnvironmentVariables(): void { 188 Object.assign(process.env, filterSettingsEnv(getGlobalConfig().env)) 189 190 Object.assign(process.env, filterSettingsEnv(getSettings_DEPRECATED()?.env)) 191 192 // Clear caches so agents are rebuilt with the new env vars 193 clearCACertsCache() 194 clearMTLSCache() 195 clearProxyCache() 196 197 // Reconfigure proxy/mTLS agents to pick up any proxy env vars from settings 198 configureGlobalAgents() 199}