authorgravatar for 73941998+UtkarshUsername@users.noreply.github.comUtkarsh Patil <73941998+UtkarshUsername@users.noreply.github.com> 2026-08-31 03:14:10+05:30
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-08-30 17:44:10-04:00
log12fe2d6d03062c5d7fc3beff168f8c4af1235b50
tree84c8a3bed46e33e01ea2b396a07d6d97e6dad235
parent6e324b9bbfc72e694c3c16abeb59716c37fc02d5
signature Signed by PGP key B5690EEEBB952194

fix(windows): strip quotes from repaired PATH (#8746)


4 files changed, 29 insertions(+), 11 deletions(-)

apps/desktop/src/shell/DesktopShellEnvironment.test.ts+2-1
...@@ -320,7 +320,7 @@ describe("DesktopShellEnvironment", () => {...@@ -320,7 +320,7 @@ describe("DesktopShellEnvironment", () => {
320 FNM_DIR: "C:\\Users\\testuser\\AppData\\Roaming\\fnm",320 FNM_DIR: "C:\\Users\\testuser\\AppData\\Roaming\\fnm",
321 FNM_MULTISHELL_PATH: "C:\\Users\\testuser\\AppData\\Local\\fnm_multishells\\123",321 FNM_MULTISHELL_PATH: "C:\\Users\\testuser\\AppData\\Local\\fnm_multishells\\123",
322 })322 })
323 : envOutput({ PATH: "C:\\Custom\\Bin;C:\\Windows\\System32" });323 : envOutput({ PATH: 'C:\\Custom\\Bin;C:";C:\\Windows\\System32' });
324 },324 },
325 });325 });
326326
...@@ -337,6 +337,7 @@ describe("DesktopShellEnvironment", () => {...@@ -337,6 +337,7 @@ describe("DesktopShellEnvironment", () => {
337 "C:\\Users\\testuser\\.bun\\bin",337 "C:\\Users\\testuser\\.bun\\bin",
338 "C:\\Users\\testuser\\scoop\\shims",338 "C:\\Users\\testuser\\scoop\\shims",
339 "C:\\Custom\\Bin",339 "C:\\Custom\\Bin",
340 "C:",
340 ].join(";"),341 ].join(";"),
341 );342 );
342 assert.equal(env.FNM_DIR, "C:\\Users\\testuser\\AppData\\Roaming\\fnm");343 assert.equal(env.FNM_DIR, "C:\\Users\\testuser\\AppData\\Roaming\\fnm");
apps/desktop/src/shell/DesktopShellEnvironment.ts+7-4
...@@ -151,6 +151,9 @@ const pathComparisonKey = (entry: string, platform: NodeJS.Platform) => {...@@ -151,6 +151,9 @@ const pathComparisonKey = (entry: string, platform: NodeJS.Platform) => {
151 return platform === "win32" ? normalized.toLowerCase() : normalized;151 return platform === "win32" ? normalized.toLowerCase() : normalized;
152};152};
153153
154const sanitizePathEntry = (entry: string, platform: NodeJS.Platform) =>
155 platform === "win32" ? entry.replaceAll('"', "") : entry;
156
154const mergePaths = (157const mergePaths = (
155 platform: NodeJS.Platform,158 platform: NodeJS.Platform,
156 values: ReadonlyArray<Option.Option<string>>,159 values: ReadonlyArray<Option.Option<string>>,
...@@ -163,14 +166,14 @@ const mergePaths = (...@@ -163,14 +166,14 @@ const mergePaths = (
163 if (Option.isNone(value)) continue;166 if (Option.isNone(value)) continue;
164167
165 for (const entry of value.value.split(delimiter)) {168 for (const entry of value.value.split(delimiter)) {
166 const trimmed = entry.trim();169 const sanitized = sanitizePathEntry(entry.trim(), platform);
167 if (trimmed.length === 0) continue;170 if (sanitized.length === 0) continue;
168171
169 const key = pathComparisonKey(trimmed, platform);172 const key = pathComparisonKey(sanitized, platform);
170 if (key.length === 0 || seen.has(key)) continue;173 if (key.length === 0 || seen.has(key)) continue;
171174
172 seen.add(key);175 seen.add(key);
173 entries.push(trimmed);176 entries.push(sanitized);
174 }177 }
175 }178 }
176179
packages/shared/src/shell.test.ts+12-2
...@@ -304,7 +304,7 @@ describe("readEnvironmentFromWindowsShell", () => {...@@ -304,7 +304,7 @@ describe("readEnvironmentFromWindowsShell", () => {
304});304});
305305
306describe("mergePathValues", () => {306describe("mergePathValues", () => {
307 it("dedupes case-insensitively on Windows while preserving preferred order", () => {307 it("sanitizes and dedupes Windows entries while preserving preferred order", () => {
308 expect(308 expect(
309 mergePathValues(309 mergePathValues(
310 'C:\\Users\\testuser\\AppData\\Roaming\\npm;"C:\\Program Files\\nodejs"',310 'C:\\Users\\testuser\\AppData\\Roaming\\npm;"C:\\Program Files\\nodejs"',
...@@ -312,10 +312,20 @@ describe("mergePathValues", () => {...@@ -312,10 +312,20 @@ describe("mergePathValues", () => {
312 "win32",312 "win32",
313 ),313 ),
314 ).toBe(314 ).toBe(
315 'C:\\Users\\testuser\\AppData\\Roaming\\npm;"C:\\Program Files\\nodejs";C:\\Windows\\System32',315 "C:\\Users\\testuser\\AppData\\Roaming\\npm;C:\\Program Files\\nodejs;C:\\Windows\\System32",
316 );316 );
317 });317 });
318318
319 it("removes stray quotes from Windows entries", () => {
320 expect(
321 mergePathValues(
322 'C:\\Windows\\System32;C:\\cloudflared.exe;C:";C:\\Program Files\\nodejs',
323 undefined,
324 "win32",
325 ),
326 ).toBe("C:\\Windows\\System32;C:\\cloudflared.exe;C:;C:\\Program Files\\nodejs");
327 });
328
319 it("dedupes case-sensitively on POSIX", () => {329 it("dedupes case-sensitively on POSIX", () => {
320 expect(mergePathValues("/usr/local/bin:/usr/bin", "/usr/bin:/USR/BIN", "linux")).toBe(330 expect(mergePathValues("/usr/local/bin:/usr/bin", "/usr/bin:/USR/BIN", "linux")).toBe(
321 "/usr/local/bin:/usr/bin:/USR/BIN",331 "/usr/local/bin:/usr/bin:/USR/BIN",
packages/shared/src/shell.ts+8-4
...@@ -414,6 +414,10 @@ function normalizePathEntryForComparison(entry: string, platform: NodeJS.Platfor...@@ -414,6 +414,10 @@ function normalizePathEntryForComparison(entry: string, platform: NodeJS.Platfor
414 return platform === "win32" ? normalized.toLowerCase() : normalized;414 return platform === "win32" ? normalized.toLowerCase() : normalized;
415}415}
416416
417function sanitizePathEntry(entry: string, platform: NodeJS.Platform): string {
418 return platform === "win32" ? entry.replaceAll('"', "") : entry;
419}
420
417export function mergePathValues(421export function mergePathValues(
418 preferredPath: string | undefined,422 preferredPath: string | undefined,
419 inheritedPath: string | undefined,423 inheritedPath: string | undefined,
...@@ -427,14 +431,14 @@ export function mergePathValues(...@@ -427,14 +431,14 @@ export function mergePathValues(
427 if (!rawValue) continue;431 if (!rawValue) continue;
428432
429 for (const entry of rawValue.split(delimiter)) {433 for (const entry of rawValue.split(delimiter)) {
430 const trimmed = entry.trim();434 const sanitized = sanitizePathEntry(entry.trim(), platform);
431 if (trimmed.length === 0) continue;435 if (sanitized.length === 0) continue;
432436
433 const normalized = normalizePathEntryForComparison(trimmed, platform);437 const normalized = normalizePathEntryForComparison(sanitized, platform);
434 if (normalized.length === 0 || seen.has(normalized)) continue;438 if (normalized.length === 0 || seen.has(normalized)) continue;
435439
436 seen.add(normalized);440 seen.add(normalized);
437 merged.push(trimmed);441 merged.push(sanitized);
438 }442 }
439 }443 }
440444