From a8f1a2d98ca9531c8ca1ddd6fee2a4e7f7652619 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Mon, 24 Aug 2026 19:21:52 -0700 Subject: [PATCH] fix(startup): drop the other-cased PATH key from the Windows probe env MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Caught by running the suite on a real Windows machine, not a mocked platform. The spread of process.env is a plain, case-sensitive object, while Windows resolves env names case-insensitively. Writing the captured `Path` back onto it left the seeded value still live under `PATH`, so the probe shell could read either one — the exact duplicate-key hazard the win32 branch was supposed to prevent. Drop any other-cased variant of the key before writing. --- src/main/startup/hydrate-shell-path.test.ts | 2 ++ src/main/startup/hydrate-shell-path.ts | 18 ++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/main/startup/hydrate-shell-path.test.ts b/src/main/startup/hydrate-shell-path.test.ts index d70441f6f5b..60c8dc0bd17 100644 --- a/src/main/startup/hydrate-shell-path.test.ts +++ b/src/main/startup/hydrate-shell-path.test.ts @@ -211,8 +211,10 @@ describe('hydrateShellPath', () => { }) it('overwrites the captured key in place so Windows never carries both Path and PATH', async () => { + vi.spyOn(process, 'platform', 'get').mockReturnValue('win32') const originalWindowsPath = process.env.Path delete process.env.PATH + process.env.PATH = 'C:\\Users\\u\\.nvm\\versions\\node\\v22.9.0\\bin;C:\\Windows' process.env.Path = 'C:\\Users\\u\\.nvm\\versions\\node\\v22.9.0\\bin;C:\\Windows' _setLaunchPathForTests('C:\\Windows', 'Path') diff --git a/src/main/startup/hydrate-shell-path.ts b/src/main/startup/hydrate-shell-path.ts index af1f7fbdb95..7804ee99743 100644 --- a/src/main/startup/hydrate-shell-path.ts +++ b/src/main/startup/hydrate-shell-path.ts @@ -121,11 +121,21 @@ function shellProbeEnv(): NodeJS.ProcessEnv { const key = launchPathOverride?.key ?? LAUNCH_PATH_KEY const value = launchPathOverride?.value ?? LAUNCH_PATH const env: NodeJS.ProcessEnv = { ...process.env, [PROBE_MARKER_ENV_VAR]: '1' } - if (value !== null) { - // Why the captured key, not a literal 'PATH': overwrite in place so Windows - // never ends up carrying both `Path` and `PATH` on the spawn env. - env[key] = value + if (value === null) { + return env } + if (process.platform === 'win32') { + // Why: the spread is a plain, case-sensitive object, but Windows resolves env + // names case-insensitively. Writing our key back without dropping the other + // casing leaves the seeded value live under `PATH` beside a clean `Path`, and + // the shell may read either one. + for (const name of Object.keys(env)) { + if (name !== key && name.toLowerCase() === key.toLowerCase()) { + delete env[name] + } + } + } + env[key] = value return env }