From d79fedbd46a83145eb15737e8bc561a6652d999e Mon Sep 17 00:00:00 2001 From: patchwright Date: Fri, 7 Aug 2026 23:18:36 +0200 Subject: [PATCH] fix: decode %3F back to "?" in toFileSystemPath (#427) urlEncodePatterns encodes both # and ? when converting a filesystem path to a URL, but urlDecodePatterns only reversed #, $, &, ,, and @. A local path containing a literal ? (legal on POSIX filesystems) was encoded to %3F on the way in and never decoded back on the way out, so resolution of any such path failed with ENOENT. Adds the missing /%3F/g, "?" pair, in the same hex-ordered position the other pairs already follow. Adds a symmetric test for # alongside the new ? test. --- lib/util/url.ts | 2 +- test/specs/util/url.spec.ts | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/util/url.ts b/lib/util/url.ts index f13966eb..2c63a522 100644 --- a/lib/util/url.ts +++ b/lib/util/url.ts @@ -16,7 +16,7 @@ const urlEncodePatterns = [ ] as [RegExp, string][]; // RegExp patterns to URL-decode special characters for local filesystem paths -const urlDecodePatterns = [/%23/g, "#", /%24/g, "$", /%26/g, "&", /%2C/g, ",", /%40/g, "@"]; +const urlDecodePatterns = [/%23/g, "#", /%24/g, "$", /%26/g, "&", /%2C/g, ",", /%3F/g, "?", /%40/g, "@"]; const unsafeDomainSuffixes = [".localhost", ".local", ".internal", ".intranet", ".corp", ".home", ".lan"]; diff --git a/test/specs/util/url.spec.ts b/test/specs/util/url.spec.ts index 9563c059..65475c2d 100644 --- a/test/specs/util/url.spec.ts +++ b/test/specs/util/url.spec.ts @@ -152,3 +152,19 @@ describe("Handle Linux file paths", () => { expect($url.toFileSystemPath("FILE:///a/random/Path/file.json")).to.equal("/a/random/Path/file.json"); }); }); + +describe("Round-trip special characters in filesystem paths", () => { + it("should round-trip a literal question mark", () => { + const original = "/a/random/Path/defs?1.json"; + const encoded = $url.fromFileSystemPath(original); + expect(encoded).to.equal("/a/random/Path/defs%3F1.json"); + expect($url.toFileSystemPath(encoded)).to.equal(original); + }); + + it("should round-trip a literal hash", () => { + const original = "/a/random/Path/defs#1.json"; + const encoded = $url.fromFileSystemPath(original); + expect(encoded).to.equal("/a/random/Path/defs%231.json"); + expect($url.toFileSystemPath(encoded)).to.equal(original); + }); +});