From 8226a1adf7ffa61dca91c5ba8ae5b4d0b7edd27d Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Tue, 27 Oct 2026 12:22:10 -0700 Subject: [PATCH 1/6] Replace deprecated url.parse in uriToFilePath with WHATWG URL Use `new URL(uri).pathname` instead of `url.parse(uri).path`. Input that URL can't parse still returns undefined. Query strings are no longer included in the returned file path. --- server/src/node/files.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/server/src/node/files.ts b/server/src/node/files.ts index ccefd3166..5b701e465 100644 --- a/server/src/node/files.ts +++ b/server/src/node/files.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. * ------------------------------------------------------------------------------------------ */ -import * as url from 'url'; import * as path from 'path'; import * as fs from 'fs'; import { spawnSync, fork, ChildProcess, SpawnSyncOptionsWithStringEncoding } from 'child_process'; @@ -13,11 +12,16 @@ import { spawnSync, fork, ChildProcess, SpawnSyncOptionsWithStringEncoding } fro * complete implementation of handling VS Code URIs. */ export function uriToFilePath(uri: string): string | undefined { - const parsed = url.parse(uri); - if (parsed.protocol !== 'file:' || !parsed.path) { + let parsed: URL; + try { + parsed = new URL(uri); + } catch { + return undefined; + } + if (parsed.protocol !== 'file:' || !parsed.pathname) { return undefined; } - const segments = parsed.path.split('/'); + const segments = parsed.pathname.split('/'); for (let i = 0, len = segments.length; i < len; i++) { segments[i] = decodeURIComponent(segments[i]); } From cede6981425f85ff9ea86774f2a5c6215fe4518f Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Fri, 30 Oct 2026 09:19:53 -0700 Subject: [PATCH 2/6] Decode URI path segments before normalizing in uriToFilePath URL.pathname resolves '..' segments before percent-decoding, so file:///tmp/one%2Ftwo/../three.ts mapped to /tmp/three.ts instead of /tmp/one/three.ts. Take the raw path from the URI string so segments are decoded before path.normalize, as url.parse did. --- server/src/node/files.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/server/src/node/files.ts b/server/src/node/files.ts index 5b701e465..6684eebc0 100644 --- a/server/src/node/files.ts +++ b/server/src/node/files.ts @@ -18,10 +18,16 @@ export function uriToFilePath(uri: string): string | undefined { } catch { return undefined; } - if (parsed.protocol !== 'file:' || !parsed.pathname) { + if (parsed.protocol !== 'file:') { return undefined; } - const segments = parsed.pathname.split('/'); + // URL.pathname resolves '..' before percent-decoding. Use the raw path so + // segments are decoded before path.normalize, as url.parse did. + const rawPath = /^file:(?:\/\/[^/?#]*)?([^?#]*)/i.exec(uri)?.[1]; + if (!rawPath) { + return undefined; + } + const segments = rawPath.split('/'); for (let i = 0, len = segments.length; i < len; i++) { segments[i] = decodeURIComponent(segments[i]); } From 8d3b410f9ce69ee508954a18fe3b6e48e3f81ad2 Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Fri, 30 Oct 2026 09:35:53 -0700 Subject: [PATCH 3/6] Trim and convert backslashes in URIs passed to uriToFilePath Taking the raw path from the URI string skipped preprocessing that url.parse did: file://localhost\C:\work\a.ts returned undefined, leading whitespace returned undefined, and trailing whitespace stayed in the path. Trim the input and convert backslashes to forward slashes before extracting the path. --- server/src/node/files.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server/src/node/files.ts b/server/src/node/files.ts index 6684eebc0..ca844f511 100644 --- a/server/src/node/files.ts +++ b/server/src/node/files.ts @@ -22,8 +22,10 @@ export function uriToFilePath(uri: string): string | undefined { return undefined; } // URL.pathname resolves '..' before percent-decoding. Use the raw path so - // segments are decoded before path.normalize, as url.parse did. - const rawPath = /^file:(?:\/\/[^/?#]*)?([^?#]*)/i.exec(uri)?.[1]; + // segments are decoded before path.normalize, as url.parse did. Trim and + // convert backslashes first, as url.parse and URL both do. + const normalized = uri.trim().replace(/\\/g, '/'); + const rawPath = /^file:(?:\/\/[^/?#]*)?([^?#]*)/i.exec(normalized)?.[1]; if (!rawPath) { return undefined; } From d39a7c143edf4c95ea1f415ec47c51266ea14758 Mon Sep 17 00:00:00 2001 From: "Kamat, Trivikram" <16024985+trivikr@users.noreply.github.com> Date: Tue, 3 Nov 2026 08:08:46 -0800 Subject: [PATCH 4/6] Preserve root path for authority-only file URIs in uriToFilePath When a file URI has an authority but no path, such as file://localhost or file://localhost#frag, return '/' as url.parse did. Previously the raw path regex captured an empty string and returned undefined. URIs with an empty authority (file://, file:) still return undefined. --- server/src/node/files.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/src/node/files.ts b/server/src/node/files.ts index ca844f511..69dfcb481 100644 --- a/server/src/node/files.ts +++ b/server/src/node/files.ts @@ -25,7 +25,9 @@ export function uriToFilePath(uri: string): string | undefined { // segments are decoded before path.normalize, as url.parse did. Trim and // convert backslashes first, as url.parse and URL both do. const normalized = uri.trim().replace(/\\/g, '/'); - const rawPath = /^file:(?:\/\/[^/?#]*)?([^?#]*)/i.exec(normalized)?.[1]; + const match = /^file:(?:\/\/([^/?#]*))?([^?#]*)/i.exec(normalized); + // An authority with no path (e.g. file://localhost) implies the root path. + const rawPath = match?.[2] || (match?.[1] ? '/' : undefined); if (!rawPath) { return undefined; } From 8c5ed846a393d29332470b6ea52be12cb3ba6644 Mon Sep 17 00:00:00 2001 From: "Kamat, Trivikram" <16024985+trivikr@users.noreply.github.com> Date: Wed, 4 Nov 2026 21:11:39 -0800 Subject: [PATCH 5/6] Trim only legacy url.parse whitespace in uriToFilePath String.prototype.trim() strips Unicode whitespace such as U+2003 that url.parse kept as part of the filename, and leaves C0 control characters that url.parse removed. Trim only C0 controls, space, U+00A0 and U+FEFF so the resolved paths match the old behavior. --- server/src/node/files.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/src/node/files.ts b/server/src/node/files.ts index 69dfcb481..f33f05a8e 100644 --- a/server/src/node/files.ts +++ b/server/src/node/files.ts @@ -22,9 +22,10 @@ export function uriToFilePath(uri: string): string | undefined { return undefined; } // URL.pathname resolves '..' before percent-decoding. Use the raw path so - // segments are decoded before path.normalize, as url.parse did. Trim and + // segments are decoded before path.normalize, as url.parse did. Trim the + // characters url.parse trimmed (C0 controls, space, U+00A0, U+FEFF) and // convert backslashes first, as url.parse and URL both do. - const normalized = uri.trim().replace(/\\/g, '/'); + const normalized = uri.replace(/^[\u0000-  ]+|[\u0000-  ]+$/g, '').replace(/\\/g, '/'); const match = /^file:(?:\/\/([^/?#]*))?([^?#]*)/i.exec(normalized); // An authority with no path (e.g. file://localhost) implies the root path. const rawPath = match?.[2] || (match?.[1] ? '/' : undefined); From 2afaeef5315dbd2807463a4b6e3bbe19a5fc57a7 Mon Sep 17 00:00:00 2001 From: "Kamat, Trivikram" <16024985+trivikr@users.noreply.github.com> Date: Wed, 4 Nov 2026 21:19:57 -0800 Subject: [PATCH 6/6] Normalize legacy whitespace before parsing in uriToFilePath WHATWG URL does not trim leading U+00A0 or U+FEFF, so inputs such as "\u00A0file:///tmp/a.ts" threw and returned undefined, while url.parse returned /tmp/a.ts. Trim and convert backslashes before constructing the URL, and use the same normalized string for the raw path. --- server/src/node/files.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/server/src/node/files.ts b/server/src/node/files.ts index f33f05a8e..c6d95645e 100644 --- a/server/src/node/files.ts +++ b/server/src/node/files.ts @@ -12,9 +12,13 @@ import { spawnSync, fork, ChildProcess, SpawnSyncOptionsWithStringEncoding } fro * complete implementation of handling VS Code URIs. */ export function uriToFilePath(uri: string): string | undefined { + // Trim the characters url.parse trimmed (C0 controls, space, U+00A0, + // U+FEFF) and convert backslashes, as url.parse and URL both do. URL + // itself does not trim U+00A0 or U+FEFF, so normalize before parsing. + const normalized = uri.replace(/^[\u0000-\u0020\u00a0\ufeff]+|[\u0000-\u0020\u00a0\ufeff]+$/g, '').replace(/\\/g, '/'); let parsed: URL; try { - parsed = new URL(uri); + parsed = new URL(normalized); } catch { return undefined; } @@ -22,10 +26,7 @@ export function uriToFilePath(uri: string): string | undefined { return undefined; } // URL.pathname resolves '..' before percent-decoding. Use the raw path so - // segments are decoded before path.normalize, as url.parse did. Trim the - // characters url.parse trimmed (C0 controls, space, U+00A0, U+FEFF) and - // convert backslashes first, as url.parse and URL both do. - const normalized = uri.replace(/^[\u0000-  ]+|[\u0000-  ]+$/g, '').replace(/\\/g, '/'); + // segments are decoded before path.normalize, as url.parse did. const match = /^file:(?:\/\/([^/?#]*))?([^?#]*)/i.exec(normalized); // An authority with no path (e.g. file://localhost) implies the root path. const rawPath = match?.[2] || (match?.[1] ? '/' : undefined);