From 0bf1481d9ea9a8ef0f86f26a90f551596dd3d943 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sat, 24 Aug 2024 21:45:16 +0200 Subject: [PATCH 01/14] url: handle "unsafe" characters properly in `pathToFileURL` Co-authored-by: EarlyRiser42 --- lib/internal/process/execution.js | 2 +- lib/internal/url.js | 47 +++++-------------------- test/parallel/test-url-pathtofileurl.js | 17 ++++++--- 3 files changed, 23 insertions(+), 43 deletions(-) diff --git a/lib/internal/process/execution.js b/lib/internal/process/execution.js index 7834ce9aa74e8c..68b267b61c39b1 100644 --- a/lib/internal/process/execution.js +++ b/lib/internal/process/execution.js @@ -49,7 +49,7 @@ function tryGetCwd() { let evalIndex = 0; function getEvalModuleUrl() { - return pathToFileURL(`${process.cwd()}/[eval${++evalIndex}]`).href; + return `${pathToFileURL(process.cwd())}/[eval${++evalIndex}]`; } /** diff --git a/lib/internal/url.js b/lib/internal/url.js index b62766b02987d1..bd6edda1ea2dc6 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -12,6 +12,7 @@ const { Int8Array, IteratorPrototype, Number, + NumberPrototypeToString, ObjectDefineProperties, ObjectSetPrototypeOf, ReflectGetOwnPropertyDescriptor, @@ -24,8 +25,10 @@ const { StringPrototypeCodePointAt, StringPrototypeIncludes, StringPrototypeIndexOf, + StringPrototypePadStart, StringPrototypeSlice, StringPrototypeStartsWith, + StringPrototypeToUpperCase, StringPrototypeToWellFormed, Symbol, SymbolIterator, @@ -1498,39 +1501,15 @@ function fileURLToPath(path, options = kEmptyObject) { return (windows ?? isWindows) ? getPathFromURLWin32(path) : getPathFromURLPosix(path); } -// The following characters are percent-encoded when converting from file path -// to URL: -// - %: The percent character is the only character not encoded by the -// `pathname` setter. -// - \: Backslash is encoded on non-windows platforms since it's a valid -// character but the `pathname` setters replaces it by a forward slash. -// - LF: The newline character is stripped out by the `pathname` setter. -// (See whatwg/url#419) -// - CR: The carriage return character is also stripped out by the `pathname` -// setter. -// - TAB: The tab character is also stripped out by the `pathname` setter. -const percentRegEx = /%/g; +// RFC1738 defines the following chars as "unsafe" for URLs +// @see https://www.ietf.org/rfc/rfc1738.txt +// Back-slashes must be special-cased on Windows, where they are treated as path separator. +const unsafeWindowsCharRegex = /[\s<>"#%{}|^[\]`?]/g; +const unsafePosixCharRegex = /[\s<>"#%{}|^[\\\]`?]/g; const backslashRegEx = /\\/g; -const newlineRegEx = /\n/g; -const carriageReturnRegEx = /\r/g; -const tabRegEx = /\t/g; -const questionRegex = /\?/g; -const hashRegex = /#/g; function encodePathChars(filepath, options = kEmptyObject) { - const windows = options?.windows; - if (StringPrototypeIndexOf(filepath, '%') !== -1) - filepath = RegExpPrototypeSymbolReplace(percentRegEx, filepath, '%25'); - // In posix, backslash is a valid character in paths: - if (!(windows ?? isWindows) && StringPrototypeIndexOf(filepath, '\\') !== -1) - filepath = RegExpPrototypeSymbolReplace(backslashRegEx, filepath, '%5C'); - if (StringPrototypeIndexOf(filepath, '\n') !== -1) - filepath = RegExpPrototypeSymbolReplace(newlineRegEx, filepath, '%0A'); - if (StringPrototypeIndexOf(filepath, '\r') !== -1) - filepath = RegExpPrototypeSymbolReplace(carriageReturnRegEx, filepath, '%0D'); - if (StringPrototypeIndexOf(filepath, '\t') !== -1) - filepath = RegExpPrototypeSymbolReplace(tabRegEx, filepath, '%09'); - return filepath; + return RegExpPrototypeSymbolReplace(options.windows ? unsafeWindowsCharRegex : unsafePosixCharRegex, filepath, (c) => `%${StringPrototypeToUpperCase(StringPrototypePadStart(NumberPrototypeToString(StringPrototypeCharCodeAt(c), 16), 2, '0'))}`); } function pathToFileURL(filepath, options = kEmptyObject) { @@ -1571,14 +1550,6 @@ function pathToFileURL(filepath, options = kEmptyObject) { // Call encodePathChars first to avoid encoding % again for ? and #. resolved = encodePathChars(resolved, { windows }); - // Question and hash character should be included in pathname. - // Therefore, encoding is required to eliminate parsing them in different states. - // This is done as an optimization to not creating a URL instance and - // later triggering pathname setter, which impacts performance - if (StringPrototypeIndexOf(resolved, '?') !== -1) - resolved = RegExpPrototypeSymbolReplace(questionRegex, resolved, '%3F'); - if (StringPrototypeIndexOf(resolved, '#') !== -1) - resolved = RegExpPrototypeSymbolReplace(hashRegex, resolved, '%23'); return new URL(`file://${resolved}`); } diff --git a/test/parallel/test-url-pathtofileurl.js b/test/parallel/test-url-pathtofileurl.js index 20609eb0ff5c9f..c059cfe969875d 100644 --- a/test/parallel/test-url-pathtofileurl.js +++ b/test/parallel/test-url-pathtofileurl.js @@ -13,10 +13,7 @@ const url = require('url'); { const fileURL = url.pathToFileURL('test\\').href; assert.ok(fileURL.startsWith('file:///')); - if (isWindows) - assert.ok(fileURL.endsWith('/')); - else - assert.ok(fileURL.endsWith('%5C')); + assert.match(fileURL, isWindows ? /\\$/ : /%5C$/); } { @@ -104,6 +101,12 @@ const windowsTestCases = [ { path: 'C:\\€', expected: 'file:///C:/%E2%82%AC' }, // Rocket emoji (non-BMP code point) { path: 'C:\\🚀', expected: 'file:///C:/%F0%9F%9A%80' }, + // caret + { path: 'C:\\foo^bar', expected: 'file:///C:/foo%5Ebar' }, + // left bracket + { path: 'C:\\foo[bar', expected: 'file:///C:/foo%5Bbar' }, + // right bracket + { path: 'C:\\foo]bar', expected: 'file:///C:/foo%5Dbar' }, // Local extended path { path: '\\\\?\\C:\\path\\to\\file.txt', expected: 'file:///C:/path/to/file.txt' }, // UNC path (see https://docs.microsoft.com/en-us/archive/blogs/ie/file-uris-in-windows) @@ -154,6 +157,12 @@ const posixTestCases = [ { path: '/€', expected: 'file:///%E2%82%AC' }, // Rocket emoji (non-BMP code point) { path: '/🚀', expected: 'file:///%F0%9F%9A%80' }, + // caret + { path: '/foo^bar', expected: 'file:///foo%5Ebar' }, + // left bracket + { path: '/foo[bar', expected: 'file:///foo%5Bbar' }, + // right bracket + { path: '/foo]bar', expected: 'file:///foo%5Dbar' }, ]; for (const { path, expected } of windowsTestCases) { From a962bb81abc3341cc87b538b315c1e51ec80671e Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sun, 25 Aug 2024 11:05:28 +0200 Subject: [PATCH 02/14] fixup! url: handle "unsafe" characters properly in `pathToFileURL` --- lib/internal/url.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/internal/url.js b/lib/internal/url.js index bd6edda1ea2dc6..c9086f1c46cdff 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -1502,14 +1502,18 @@ function fileURLToPath(path, options = kEmptyObject) { } // RFC1738 defines the following chars as "unsafe" for URLs -// @see https://www.ietf.org/rfc/rfc1738.txt +// @see https://www.ietf.org/rfc/rfc1738.txt 2.2. URL Character Encoding Issues // Back-slashes must be special-cased on Windows, where they are treated as path separator. const unsafeWindowsCharRegex = /[\s<>"#%{}|^[\]`?]/g; const unsafePosixCharRegex = /[\s<>"#%{}|^[\\\]`?]/g; const backslashRegEx = /\\/g; function encodePathChars(filepath, options = kEmptyObject) { - return RegExpPrototypeSymbolReplace(options.windows ? unsafeWindowsCharRegex : unsafePosixCharRegex, filepath, (c) => `%${StringPrototypeToUpperCase(StringPrototypePadStart(NumberPrototypeToString(StringPrototypeCharCodeAt(c), 16), 2, '0'))}`); + return RegExpPrototypeSymbolReplace( + options.windows ? unsafeWindowsCharRegex : unsafePosixCharRegex, + filepath, + (c) => `%${StringPrototypePadStart(StringPrototypeToUpperCase(NumberPrototypeToString(StringPrototypeCharCodeAt(c), 16)), 2, '0')}` + ); } function pathToFileURL(filepath, options = kEmptyObject) { @@ -1547,10 +1551,7 @@ function pathToFileURL(filepath, options = kEmptyObject) { } let resolved = (windows ?? isWindows) ? path.win32.resolve(filepath) : path.posix.resolve(filepath); - // Call encodePathChars first to avoid encoding % again for ? and #. - resolved = encodePathChars(resolved, { windows }); - - return new URL(`file://${resolved}`); + return new URL(`file://${encodePathChars(resolved, { windows })}`); } function toPathIfFileURL(fileURLOrPath) { From ff4bc8e89d3c662441456bd994034a698d471657 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sun, 25 Aug 2024 11:11:46 +0200 Subject: [PATCH 03/14] fixup! url: handle "unsafe" characters properly in `pathToFileURL` --- lib/internal/url.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/url.js b/lib/internal/url.js index c9086f1c46cdff..a396245e619eca 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -1512,7 +1512,7 @@ function encodePathChars(filepath, options = kEmptyObject) { return RegExpPrototypeSymbolReplace( options.windows ? unsafeWindowsCharRegex : unsafePosixCharRegex, filepath, - (c) => `%${StringPrototypePadStart(StringPrototypeToUpperCase(NumberPrototypeToString(StringPrototypeCharCodeAt(c), 16)), 2, '0')}` + (c) => `%${StringPrototypePadStart(StringPrototypeToUpperCase(NumberPrototypeToString(StringPrototypeCharCodeAt(c), 16)), 2, '0')}`, ); } From c2e4091b419e5fed4b78fca5544018ca314b210d Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Mon, 26 Aug 2024 00:22:29 +0200 Subject: [PATCH 04/14] fixup! url: handle "unsafe" characters properly in `pathToFileURL` add a hardcode map for most common chars --- lib/internal/url.js | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/internal/url.js b/lib/internal/url.js index a396245e619eca..9161954756793b 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -1508,11 +1508,29 @@ const unsafeWindowsCharRegex = /[\s<>"#%{}|^[\]`?]/g; const unsafePosixCharRegex = /[\s<>"#%{}|^[\\\]`?]/g; const backslashRegEx = /\\/g; +const urlEncodeMap = { + __proto__: null, + ' ': '20', + '"': '22', + '#': '23', + '%': '25', + '<': '3C', + '>': '3E', + '?': '3F', + '[': '5B', + '\\': '5C', + ']': '5D', + '^': '5E', + '`': '60', + '{': '7B', + '|': '7C', + '}': '7D', +} function encodePathChars(filepath, options = kEmptyObject) { return RegExpPrototypeSymbolReplace( options.windows ? unsafeWindowsCharRegex : unsafePosixCharRegex, filepath, - (c) => `%${StringPrototypePadStart(StringPrototypeToUpperCase(NumberPrototypeToString(StringPrototypeCharCodeAt(c), 16)), 2, '0')}`, + (c) => `%${urlEncodeMap[c] ?? StringPrototypePadStart(StringPrototypeToUpperCase(NumberPrototypeToString(StringPrototypeCharCodeAt(c), 16)), 2, '0')}`, ); } From 551907e8bfb1e45a76902f83f5aa8ba6ca2243fa Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Mon, 26 Aug 2024 00:23:26 +0200 Subject: [PATCH 05/14] fixup! fixup! url: handle "unsafe" characters properly in `pathToFileURL` --- lib/internal/url.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/url.js b/lib/internal/url.js index 9161954756793b..b521c7f50f93e7 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -1509,7 +1509,7 @@ const unsafePosixCharRegex = /[\s<>"#%{}|^[\\\]`?]/g; const backslashRegEx = /\\/g; const urlEncodeMap = { - __proto__: null, + '__proto__': null, ' ': '20', '"': '22', '#': '23', @@ -1525,7 +1525,7 @@ const urlEncodeMap = { '{': '7B', '|': '7C', '}': '7D', -} +}; function encodePathChars(filepath, options = kEmptyObject) { return RegExpPrototypeSymbolReplace( options.windows ? unsafeWindowsCharRegex : unsafePosixCharRegex, From 6cf5db1d55aaa573d1bd9d8f4d4393e383203475 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Tue, 27 Aug 2024 00:24:33 +0200 Subject: [PATCH 06/14] fixup! url: handle "unsafe" characters properly in `pathToFileURL` --- lib/internal/url.js | 102 ++++++++++++++++++++++++++++++++------------ 1 file changed, 74 insertions(+), 28 deletions(-) diff --git a/lib/internal/url.js b/lib/internal/url.js index b521c7f50f93e7..30ccf75c6bf54a 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -12,7 +12,6 @@ const { Int8Array, IteratorPrototype, Number, - NumberPrototypeToString, ObjectDefineProperties, ObjectSetPrototypeOf, ReflectGetOwnPropertyDescriptor, @@ -25,10 +24,8 @@ const { StringPrototypeCodePointAt, StringPrototypeIncludes, StringPrototypeIndexOf, - StringPrototypePadStart, StringPrototypeSlice, StringPrototypeStartsWith, - StringPrototypeToUpperCase, StringPrototypeToWellFormed, Symbol, SymbolIterator, @@ -1504,34 +1501,83 @@ function fileURLToPath(path, options = kEmptyObject) { // RFC1738 defines the following chars as "unsafe" for URLs // @see https://www.ietf.org/rfc/rfc1738.txt 2.2. URL Character Encoding Issues // Back-slashes must be special-cased on Windows, where they are treated as path separator. -const unsafeWindowsCharRegex = /[\s<>"#%{}|^[\]`?]/g; -const unsafePosixCharRegex = /[\s<>"#%{}|^[\\\]`?]/g; +const percentRegEx = /%/g; const backslashRegEx = /\\/g; +const newlineRegEx = /\n/g; +const carriageReturnRegEx = /\r/g; +const tabRegEx = /\t/g; +const quoteRegEx = /"/g; +const hashRegex = /#/g; +const spaceRegEx = / /g; +const lowerThanRegEx = //g; +const questionRegex = /\?/g; +const openSquareBracketRegEx = /\[/g; +const closeSquareBracketRegEx = /]/g; +const openCurlyBracketRegEx = /{/g; +const closeCurlyBracketRegEx = /}/g; +const caretRegEx = /\^/g; +const backtickRegEx = /`/g; +const verticalBarRegEx = /\|/g; -const urlEncodeMap = { - '__proto__': null, - ' ': '20', - '"': '22', - '#': '23', - '%': '25', - '<': '3C', - '>': '3E', - '?': '3F', - '[': '5B', - '\\': '5C', - ']': '5D', - '^': '5E', - '`': '60', - '{': '7B', - '|': '7C', - '}': '7D', -}; function encodePathChars(filepath, options = kEmptyObject) { - return RegExpPrototypeSymbolReplace( - options.windows ? unsafeWindowsCharRegex : unsafePosixCharRegex, - filepath, - (c) => `%${urlEncodeMap[c] ?? StringPrototypePadStart(StringPrototypeToUpperCase(NumberPrototypeToString(StringPrototypeCharCodeAt(c), 16)), 2, '0')}`, - ); + if (StringPrototypeIncludes(filepath, '%')) { + filepath = RegExpPrototypeSymbolReplace(percentRegEx, filepath, '%25'); + } + + if (StringPrototypeIncludes(filepath, '\t')) { + filepath = RegExpPrototypeSymbolReplace(tabRegEx, filepath, '%09'); + } + if (StringPrototypeIncludes(filepath, '\n')) { + filepath = RegExpPrototypeSymbolReplace(newlineRegEx, filepath, '%0A'); + } + if (StringPrototypeIncludes(filepath, '\r')) { + filepath = RegExpPrototypeSymbolReplace(carriageReturnRegEx, filepath, '%0D'); + } + if (StringPrototypeIncludes(filepath, ' ')) { + filepath = RegExpPrototypeSymbolReplace(spaceRegEx, filepath, '%20'); + } + if (StringPrototypeIncludes(filepath, '"')) { + filepath = RegExpPrototypeSymbolReplace(quoteRegEx, filepath, '%22'); + } + if (StringPrototypeIncludes(filepath, '#')) { + filepath = RegExpPrototypeSymbolReplace(hashRegex, filepath, '%23'); + } + if (StringPrototypeIncludes(filepath, '<')) { + filepath = RegExpPrototypeSymbolReplace(lowerThanRegEx, filepath, '%3C'); + } + if (StringPrototypeIncludes(filepath, '>')) { + filepath = RegExpPrototypeSymbolReplace(greaterThanRegEx, filepath, '%3E'); + } + if (StringPrototypeIncludes(filepath, '?')) { + filepath = RegExpPrototypeSymbolReplace(questionRegex, filepath, '%3F'); + } + if (StringPrototypeIncludes(filepath, '[')) { + filepath = RegExpPrototypeSymbolReplace(openSquareBracketRegEx, filepath, '%5B'); + } + if (!options.windows && StringPrototypeIncludes(filepath, '\\')) { + filepath = RegExpPrototypeSymbolReplace(backslashRegEx, filepath, '%5C'); + } + if (StringPrototypeIncludes(filepath, ']')) { + filepath = RegExpPrototypeSymbolReplace(closeSquareBracketRegEx, filepath, '%5D'); + } + if (StringPrototypeIncludes(filepath, '^')) { + filepath = RegExpPrototypeSymbolReplace(caretRegEx, filepath, '%5E'); + } + if (StringPrototypeIncludes(filepath, '`')) { + filepath = RegExpPrototypeSymbolReplace(backtickRegEx, filepath, '%60'); + } + if (StringPrototypeIncludes(filepath, '{')) { + filepath = RegExpPrototypeSymbolReplace(openCurlyBracketRegEx, filepath, '%7B'); + } + if (StringPrototypeIncludes(filepath, '|')) { + filepath = RegExpPrototypeSymbolReplace(verticalBarRegEx, filepath, '%7C'); + } + if (StringPrototypeIncludes(filepath, '}')) { + filepath = RegExpPrototypeSymbolReplace(closeCurlyBracketRegEx, filepath, '%7D'); + } + + return filepath; } function pathToFileURL(filepath, options = kEmptyObject) { From 7dd02ee57d276580447e44759cde50b83b776b42 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Tue, 27 Aug 2024 01:14:52 +0200 Subject: [PATCH 07/14] fixup! url: handle "unsafe" characters properly in `pathToFileURL` --- lib/internal/url.js | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/lib/internal/url.js b/lib/internal/url.js index 30ccf75c6bf54a..2c3e2b0848d0a2 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -1500,7 +1500,6 @@ function fileURLToPath(path, options = kEmptyObject) { // RFC1738 defines the following chars as "unsafe" for URLs // @see https://www.ietf.org/rfc/rfc1738.txt 2.2. URL Character Encoding Issues -// Back-slashes must be special-cased on Windows, where they are treated as path separator. const percentRegEx = /%/g; const backslashRegEx = /\\/g; const newlineRegEx = /\n/g; @@ -1521,59 +1520,60 @@ const backtickRegEx = /`/g; const verticalBarRegEx = /\|/g; function encodePathChars(filepath, options = kEmptyObject) { - if (StringPrototypeIncludes(filepath, '%')) { + if (StringPrototypeIndexOf(filepath, '%') !== -1) { filepath = RegExpPrototypeSymbolReplace(percentRegEx, filepath, '%25'); } - if (StringPrototypeIncludes(filepath, '\t')) { + if (StringPrototypeIndexOf(filepath, '\t') !== -1) { filepath = RegExpPrototypeSymbolReplace(tabRegEx, filepath, '%09'); } - if (StringPrototypeIncludes(filepath, '\n')) { + if (StringPrototypeIndexOf(filepath, '\n') !== -1) { filepath = RegExpPrototypeSymbolReplace(newlineRegEx, filepath, '%0A'); } - if (StringPrototypeIncludes(filepath, '\r')) { + if (StringPrototypeIndexOf(filepath, '\r') !== -1) { filepath = RegExpPrototypeSymbolReplace(carriageReturnRegEx, filepath, '%0D'); } - if (StringPrototypeIncludes(filepath, ' ')) { + if (StringPrototypeIndexOf(filepath, ' ') !== -1) { filepath = RegExpPrototypeSymbolReplace(spaceRegEx, filepath, '%20'); } - if (StringPrototypeIncludes(filepath, '"')) { + if (StringPrototypeIndexOf(filepath, '"') !== -1) { filepath = RegExpPrototypeSymbolReplace(quoteRegEx, filepath, '%22'); } - if (StringPrototypeIncludes(filepath, '#')) { + if (StringPrototypeIndexOf(filepath, '#') !== -1) { filepath = RegExpPrototypeSymbolReplace(hashRegex, filepath, '%23'); } - if (StringPrototypeIncludes(filepath, '<')) { + if (StringPrototypeIndexOf(filepath, '<') !== -1) { filepath = RegExpPrototypeSymbolReplace(lowerThanRegEx, filepath, '%3C'); } - if (StringPrototypeIncludes(filepath, '>')) { + if (StringPrototypeIndexOf(filepath, '>') !== -1) { filepath = RegExpPrototypeSymbolReplace(greaterThanRegEx, filepath, '%3E'); } - if (StringPrototypeIncludes(filepath, '?')) { + if (StringPrototypeIndexOf(filepath, '?') !== -1) { filepath = RegExpPrototypeSymbolReplace(questionRegex, filepath, '%3F'); } - if (StringPrototypeIncludes(filepath, '[')) { + if (StringPrototypeIndexOf(filepath, '[') !== -1) { filepath = RegExpPrototypeSymbolReplace(openSquareBracketRegEx, filepath, '%5B'); } - if (!options.windows && StringPrototypeIncludes(filepath, '\\')) { + // Back-slashes must be special-cased on Windows, where they are treated as path separator. + if (!options.windows && StringPrototypeIndexOf(filepath, '\\') !== -1) { filepath = RegExpPrototypeSymbolReplace(backslashRegEx, filepath, '%5C'); } - if (StringPrototypeIncludes(filepath, ']')) { + if (StringPrototypeIndexOf(filepath, ']') !== -1) { filepath = RegExpPrototypeSymbolReplace(closeSquareBracketRegEx, filepath, '%5D'); } - if (StringPrototypeIncludes(filepath, '^')) { + if (StringPrototypeIndexOf(filepath, '^') !== -1) { filepath = RegExpPrototypeSymbolReplace(caretRegEx, filepath, '%5E'); } - if (StringPrototypeIncludes(filepath, '`')) { + if (StringPrototypeIndexOf(filepath, '`') !== -1) { filepath = RegExpPrototypeSymbolReplace(backtickRegEx, filepath, '%60'); } - if (StringPrototypeIncludes(filepath, '{')) { + if (StringPrototypeIndexOf(filepath, '{') !== -1) { filepath = RegExpPrototypeSymbolReplace(openCurlyBracketRegEx, filepath, '%7B'); } - if (StringPrototypeIncludes(filepath, '|')) { + if (StringPrototypeIndexOf(filepath, '|') !== -1) { filepath = RegExpPrototypeSymbolReplace(verticalBarRegEx, filepath, '%7C'); } - if (StringPrototypeIncludes(filepath, '}')) { + if (StringPrototypeIndexOf(filepath, '}') !== -1) { filepath = RegExpPrototypeSymbolReplace(closeCurlyBracketRegEx, filepath, '%7D'); } From 87f2f61b6c618f499bebf9e6ea75c90d357a7584 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Tue, 27 Aug 2024 10:26:10 +0200 Subject: [PATCH 08/14] fixup! url: handle "unsafe" characters properly in `pathToFileURL` --- lib/internal/url.js | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/lib/internal/url.js b/lib/internal/url.js index 2c3e2b0848d0a2..acc6fde5aaf4c4 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -16,6 +16,7 @@ const { ObjectSetPrototypeOf, ReflectGetOwnPropertyDescriptor, ReflectOwnKeys, + RegExpPrototypeExec, RegExpPrototypeSymbolReplace, SafeMap, SafeSet, @@ -1520,60 +1521,60 @@ const backtickRegEx = /`/g; const verticalBarRegEx = /\|/g; function encodePathChars(filepath, options = kEmptyObject) { - if (StringPrototypeIndexOf(filepath, '%') !== -1) { + if (RegExpPrototypeExec(percentRegEx, filepath) !== null) { filepath = RegExpPrototypeSymbolReplace(percentRegEx, filepath, '%25'); } - if (StringPrototypeIndexOf(filepath, '\t') !== -1) { + if (RegExpPrototypeExec(tabRegEx, filepath) !== null) { filepath = RegExpPrototypeSymbolReplace(tabRegEx, filepath, '%09'); } - if (StringPrototypeIndexOf(filepath, '\n') !== -1) { + if (RegExpPrototypeExec(newlineRegEx, filepath) !== null) { filepath = RegExpPrototypeSymbolReplace(newlineRegEx, filepath, '%0A'); } - if (StringPrototypeIndexOf(filepath, '\r') !== -1) { + if (RegExpPrototypeExec(carriageReturnRegEx, filepath) !== null) { filepath = RegExpPrototypeSymbolReplace(carriageReturnRegEx, filepath, '%0D'); } - if (StringPrototypeIndexOf(filepath, ' ') !== -1) { + if (RegExpPrototypeExec(spaceRegEx, filepath) !== null) { filepath = RegExpPrototypeSymbolReplace(spaceRegEx, filepath, '%20'); } - if (StringPrototypeIndexOf(filepath, '"') !== -1) { + if (RegExpPrototypeExec(quoteRegEx, filepath) !== null) { filepath = RegExpPrototypeSymbolReplace(quoteRegEx, filepath, '%22'); } - if (StringPrototypeIndexOf(filepath, '#') !== -1) { + if (RegExpPrototypeExec(hashRegex, filepath) !== null) { filepath = RegExpPrototypeSymbolReplace(hashRegex, filepath, '%23'); } - if (StringPrototypeIndexOf(filepath, '<') !== -1) { + if (RegExpPrototypeExec(lowerThanRegEx, filepath) !== null) { filepath = RegExpPrototypeSymbolReplace(lowerThanRegEx, filepath, '%3C'); } - if (StringPrototypeIndexOf(filepath, '>') !== -1) { + if (RegExpPrototypeExec(greaterThanRegEx, filepath) !== null) { filepath = RegExpPrototypeSymbolReplace(greaterThanRegEx, filepath, '%3E'); } - if (StringPrototypeIndexOf(filepath, '?') !== -1) { + if (RegExpPrototypeExec(questionRegex, filepath) !== null) { filepath = RegExpPrototypeSymbolReplace(questionRegex, filepath, '%3F'); } - if (StringPrototypeIndexOf(filepath, '[') !== -1) { + if (RegExpPrototypeExec(openSquareBracketRegEx, filepath) !== null) { filepath = RegExpPrototypeSymbolReplace(openSquareBracketRegEx, filepath, '%5B'); } // Back-slashes must be special-cased on Windows, where they are treated as path separator. - if (!options.windows && StringPrototypeIndexOf(filepath, '\\') !== -1) { + if (!options.windows && RegExpPrototypeExec(backslashRegEx, filepath) !== null) { filepath = RegExpPrototypeSymbolReplace(backslashRegEx, filepath, '%5C'); } - if (StringPrototypeIndexOf(filepath, ']') !== -1) { + if (RegExpPrototypeExec(closeSquareBracketRegEx, filepath) !== null) { filepath = RegExpPrototypeSymbolReplace(closeSquareBracketRegEx, filepath, '%5D'); } - if (StringPrototypeIndexOf(filepath, '^') !== -1) { + if (RegExpPrototypeExec(caretRegEx, filepath) !== null) { filepath = RegExpPrototypeSymbolReplace(caretRegEx, filepath, '%5E'); } - if (StringPrototypeIndexOf(filepath, '`') !== -1) { + if (RegExpPrototypeExec(backtickRegEx, filepath) !== null) { filepath = RegExpPrototypeSymbolReplace(backtickRegEx, filepath, '%60'); } - if (StringPrototypeIndexOf(filepath, '{') !== -1) { + if (RegExpPrototypeExec(openCurlyBracketRegEx, filepath) !== null) { filepath = RegExpPrototypeSymbolReplace(openCurlyBracketRegEx, filepath, '%7B'); } - if (StringPrototypeIndexOf(filepath, '|') !== -1) { + if (RegExpPrototypeExec(verticalBarRegEx, filepath) !== null) { filepath = RegExpPrototypeSymbolReplace(verticalBarRegEx, filepath, '%7C'); } - if (StringPrototypeIndexOf(filepath, '}') !== -1) { + if (RegExpPrototypeExec(closeCurlyBracketRegEx, filepath) !== null) { filepath = RegExpPrototypeSymbolReplace(closeCurlyBracketRegEx, filepath, '%7D'); } From ce38d50d5113e0249f168b51f07a895da5ca01d3 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Tue, 27 Aug 2024 11:00:52 +0200 Subject: [PATCH 09/14] fixup! url: handle "unsafe" characters properly in `pathToFileURL` --- lib/internal/url.js | 73 ++++++++++++--------------------------------- 1 file changed, 19 insertions(+), 54 deletions(-) diff --git a/lib/internal/url.js b/lib/internal/url.js index acc6fde5aaf4c4..6836a1a90ce107 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -16,7 +16,6 @@ const { ObjectSetPrototypeOf, ReflectGetOwnPropertyDescriptor, ReflectOwnKeys, - RegExpPrototypeExec, RegExpPrototypeSymbolReplace, SafeMap, SafeSet, @@ -1521,62 +1520,28 @@ const backtickRegEx = /`/g; const verticalBarRegEx = /\|/g; function encodePathChars(filepath, options = kEmptyObject) { - if (RegExpPrototypeExec(percentRegEx, filepath) !== null) { - filepath = RegExpPrototypeSymbolReplace(percentRegEx, filepath, '%25'); - } - - if (RegExpPrototypeExec(tabRegEx, filepath) !== null) { - filepath = RegExpPrototypeSymbolReplace(tabRegEx, filepath, '%09'); - } - if (RegExpPrototypeExec(newlineRegEx, filepath) !== null) { - filepath = RegExpPrototypeSymbolReplace(newlineRegEx, filepath, '%0A'); - } - if (RegExpPrototypeExec(carriageReturnRegEx, filepath) !== null) { - filepath = RegExpPrototypeSymbolReplace(carriageReturnRegEx, filepath, '%0D'); - } - if (RegExpPrototypeExec(spaceRegEx, filepath) !== null) { - filepath = RegExpPrototypeSymbolReplace(spaceRegEx, filepath, '%20'); - } - if (RegExpPrototypeExec(quoteRegEx, filepath) !== null) { - filepath = RegExpPrototypeSymbolReplace(quoteRegEx, filepath, '%22'); - } - if (RegExpPrototypeExec(hashRegex, filepath) !== null) { - filepath = RegExpPrototypeSymbolReplace(hashRegex, filepath, '%23'); - } - if (RegExpPrototypeExec(lowerThanRegEx, filepath) !== null) { - filepath = RegExpPrototypeSymbolReplace(lowerThanRegEx, filepath, '%3C'); - } - if (RegExpPrototypeExec(greaterThanRegEx, filepath) !== null) { - filepath = RegExpPrototypeSymbolReplace(greaterThanRegEx, filepath, '%3E'); - } - if (RegExpPrototypeExec(questionRegex, filepath) !== null) { - filepath = RegExpPrototypeSymbolReplace(questionRegex, filepath, '%3F'); - } - if (RegExpPrototypeExec(openSquareBracketRegEx, filepath) !== null) { - filepath = RegExpPrototypeSymbolReplace(openSquareBracketRegEx, filepath, '%5B'); - } + filepath = RegExpPrototypeSymbolReplace(percentRegEx, filepath, '%25'); + + filepath = RegExpPrototypeSymbolReplace(tabRegEx, filepath, '%09'); + filepath = RegExpPrototypeSymbolReplace(newlineRegEx, filepath, '%0A'); + filepath = RegExpPrototypeSymbolReplace(carriageReturnRegEx, filepath, '%0D'); + filepath = RegExpPrototypeSymbolReplace(spaceRegEx, filepath, '%20'); + filepath = RegExpPrototypeSymbolReplace(quoteRegEx, filepath, '%22'); + filepath = RegExpPrototypeSymbolReplace(hashRegex, filepath, '%23'); + filepath = RegExpPrototypeSymbolReplace(lowerThanRegEx, filepath, '%3C'); + filepath = RegExpPrototypeSymbolReplace(greaterThanRegEx, filepath, '%3E'); + filepath = RegExpPrototypeSymbolReplace(questionRegex, filepath, '%3F'); + filepath = RegExpPrototypeSymbolReplace(openSquareBracketRegEx, filepath, '%5B'); // Back-slashes must be special-cased on Windows, where they are treated as path separator. - if (!options.windows && RegExpPrototypeExec(backslashRegEx, filepath) !== null) { + if (!options.windows) { filepath = RegExpPrototypeSymbolReplace(backslashRegEx, filepath, '%5C'); } - if (RegExpPrototypeExec(closeSquareBracketRegEx, filepath) !== null) { - filepath = RegExpPrototypeSymbolReplace(closeSquareBracketRegEx, filepath, '%5D'); - } - if (RegExpPrototypeExec(caretRegEx, filepath) !== null) { - filepath = RegExpPrototypeSymbolReplace(caretRegEx, filepath, '%5E'); - } - if (RegExpPrototypeExec(backtickRegEx, filepath) !== null) { - filepath = RegExpPrototypeSymbolReplace(backtickRegEx, filepath, '%60'); - } - if (RegExpPrototypeExec(openCurlyBracketRegEx, filepath) !== null) { - filepath = RegExpPrototypeSymbolReplace(openCurlyBracketRegEx, filepath, '%7B'); - } - if (RegExpPrototypeExec(verticalBarRegEx, filepath) !== null) { - filepath = RegExpPrototypeSymbolReplace(verticalBarRegEx, filepath, '%7C'); - } - if (RegExpPrototypeExec(closeCurlyBracketRegEx, filepath) !== null) { - filepath = RegExpPrototypeSymbolReplace(closeCurlyBracketRegEx, filepath, '%7D'); - } + filepath = RegExpPrototypeSymbolReplace(closeSquareBracketRegEx, filepath, '%5D'); + filepath = RegExpPrototypeSymbolReplace(caretRegEx, filepath, '%5E'); + filepath = RegExpPrototypeSymbolReplace(backtickRegEx, filepath, '%60'); + filepath = RegExpPrototypeSymbolReplace(openCurlyBracketRegEx, filepath, '%7B'); + filepath = RegExpPrototypeSymbolReplace(verticalBarRegEx, filepath, '%7C'); + filepath = RegExpPrototypeSymbolReplace(closeCurlyBracketRegEx, filepath, '%7D'); return filepath; } From 27b6477a97d838eaee2f2330396365c27fb23c65 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Tue, 27 Aug 2024 11:43:56 +0200 Subject: [PATCH 10/14] fixup! url: handle "unsafe" characters properly in `pathToFileURL` replaceAll --- lib/internal/url.js | 94 +++++++++++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 38 deletions(-) diff --git a/lib/internal/url.js b/lib/internal/url.js index 6836a1a90ce107..7a58d7a42db48e 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -24,6 +24,7 @@ const { StringPrototypeCodePointAt, StringPrototypeIncludes, StringPrototypeIndexOf, + StringPrototypeReplaceAll, StringPrototypeSlice, StringPrototypeStartsWith, StringPrototypeToWellFormed, @@ -1500,48 +1501,65 @@ function fileURLToPath(path, options = kEmptyObject) { // RFC1738 defines the following chars as "unsafe" for URLs // @see https://www.ietf.org/rfc/rfc1738.txt 2.2. URL Character Encoding Issues -const percentRegEx = /%/g; const backslashRegEx = /\\/g; -const newlineRegEx = /\n/g; -const carriageReturnRegEx = /\r/g; -const tabRegEx = /\t/g; -const quoteRegEx = /"/g; -const hashRegex = /#/g; -const spaceRegEx = / /g; -const lowerThanRegEx = //g; -const questionRegex = /\?/g; -const openSquareBracketRegEx = /\[/g; -const closeSquareBracketRegEx = /]/g; -const openCurlyBracketRegEx = /{/g; -const closeCurlyBracketRegEx = /}/g; -const caretRegEx = /\^/g; -const backtickRegEx = /`/g; -const verticalBarRegEx = /\|/g; function encodePathChars(filepath, options = kEmptyObject) { - filepath = RegExpPrototypeSymbolReplace(percentRegEx, filepath, '%25'); - - filepath = RegExpPrototypeSymbolReplace(tabRegEx, filepath, '%09'); - filepath = RegExpPrototypeSymbolReplace(newlineRegEx, filepath, '%0A'); - filepath = RegExpPrototypeSymbolReplace(carriageReturnRegEx, filepath, '%0D'); - filepath = RegExpPrototypeSymbolReplace(spaceRegEx, filepath, '%20'); - filepath = RegExpPrototypeSymbolReplace(quoteRegEx, filepath, '%22'); - filepath = RegExpPrototypeSymbolReplace(hashRegex, filepath, '%23'); - filepath = RegExpPrototypeSymbolReplace(lowerThanRegEx, filepath, '%3C'); - filepath = RegExpPrototypeSymbolReplace(greaterThanRegEx, filepath, '%3E'); - filepath = RegExpPrototypeSymbolReplace(questionRegex, filepath, '%3F'); - filepath = RegExpPrototypeSymbolReplace(openSquareBracketRegEx, filepath, '%5B'); + if (StringPrototypeIncludes(filepath, '%')) { + filepath = StringPrototypeReplaceAll(filepath, '%', '%25'); + } + + if (StringPrototypeIncludes(filepath, '\t')) { + filepath = StringPrototypeReplaceAll(filepath, '\t', '%09'); + } + if (StringPrototypeIncludes(filepath, '\n')) { + filepath = StringPrototypeReplaceAll(filepath, '\n', '%0A'); + } + if (StringPrototypeIncludes(filepath, '\r')) { + filepath = StringPrototypeReplaceAll(filepath, '\r', '%0D'); + } + if (StringPrototypeIncludes(filepath, ' ')) { + filepath = StringPrototypeReplaceAll(filepath, ' ', '%20'); + } + if (StringPrototypeIncludes(filepath, '"')) { + filepath = StringPrototypeReplaceAll(filepath, '"', '%22'); + } + if (StringPrototypeIncludes(filepath, '#')) { + filepath = StringPrototypeReplaceAll(filepath, '#', '%23'); + } + if (StringPrototypeIncludes(filepath, '<')) { + filepath = StringPrototypeReplaceAll(filepath, '<', '%3C'); + } + if (StringPrototypeIncludes(filepath, '>')) { + filepath = StringPrototypeReplaceAll(filepath, '>', '%3E'); + } + if (StringPrototypeIncludes(filepath, '?')) { + filepath = StringPrototypeReplaceAll(filepath, '?', '%3F'); + } + if (StringPrototypeIncludes(filepath, '[')) { + filepath = StringPrototypeReplaceAll(filepath, '[', '%5B'); + } // Back-slashes must be special-cased on Windows, where they are treated as path separator. - if (!options.windows) { - filepath = RegExpPrototypeSymbolReplace(backslashRegEx, filepath, '%5C'); - } - filepath = RegExpPrototypeSymbolReplace(closeSquareBracketRegEx, filepath, '%5D'); - filepath = RegExpPrototypeSymbolReplace(caretRegEx, filepath, '%5E'); - filepath = RegExpPrototypeSymbolReplace(backtickRegEx, filepath, '%60'); - filepath = RegExpPrototypeSymbolReplace(openCurlyBracketRegEx, filepath, '%7B'); - filepath = RegExpPrototypeSymbolReplace(verticalBarRegEx, filepath, '%7C'); - filepath = RegExpPrototypeSymbolReplace(closeCurlyBracketRegEx, filepath, '%7D'); + if (!options.windows && StringPrototypeIncludes(filepath, '\\')) { + filepath = StringPrototypeReplaceAll(filepath, '\\', '%5C'); + } + if (StringPrototypeIncludes(filepath, ']')) { + filepath = StringPrototypeReplaceAll(filepath, ']', '%5D'); + } + if (StringPrototypeIncludes(filepath, '^')) { + filepath = StringPrototypeReplaceAll(filepath, '^', '%5E'); + } + if (StringPrototypeIncludes(filepath, '`')) { + filepath = StringPrototypeReplaceAll(filepath, '`', '%60'); + } + if (StringPrototypeIncludes(filepath, '{')) { + filepath = StringPrototypeReplaceAll(filepath, '{', '%7B'); + } + if (StringPrototypeIncludes(filepath, '|')) { + filepath = StringPrototypeReplaceAll(filepath, '|', '%7C'); + } + if (StringPrototypeIncludes(filepath, '}')) { + filepath = StringPrototypeReplaceAll(filepath, '}', '%7D'); + } return filepath; } From fcfd1f73ab403f866bb0c74e27f24456bd5feb2f Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Tue, 27 Aug 2024 12:56:04 +0200 Subject: [PATCH 11/14] fixup! url: handle "unsafe" characters properly in `pathToFileURL` fastest alternative --- lib/internal/url.js | 56 ++++++++++++------------- test/parallel/test-url-pathtofileurl.js | 8 +--- 2 files changed, 30 insertions(+), 34 deletions(-) diff --git a/lib/internal/url.js b/lib/internal/url.js index 7a58d7a42db48e..da698e91f958c5 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -24,7 +24,6 @@ const { StringPrototypeCodePointAt, StringPrototypeIncludes, StringPrototypeIndexOf, - StringPrototypeReplaceAll, StringPrototypeSlice, StringPrototypeStartsWith, StringPrototypeToWellFormed, @@ -1501,64 +1500,65 @@ function fileURLToPath(path, options = kEmptyObject) { // RFC1738 defines the following chars as "unsafe" for URLs // @see https://www.ietf.org/rfc/rfc1738.txt 2.2. URL Character Encoding Issues +const percentRegEx = /%/g; +const newlineRegEx = /\n/g; +const carriageReturnRegEx = /\r/g; +const tabRegEx = /\t/g; +const quoteRegEx = /"/g; +const hashRegex = /#/g; +const spaceRegEx = / /g; +const questionMarkRegex = /\?/g; +const openSquareBracketRegEx = /\[/g; const backslashRegEx = /\\/g; +const closeSquareBracketRegEx = /]/g; +const caretRegEx = /\^/g; +const verticalBarRegEx = /\|/g; +const tildeRegEx = /~/g; function encodePathChars(filepath, options = kEmptyObject) { if (StringPrototypeIncludes(filepath, '%')) { - filepath = StringPrototypeReplaceAll(filepath, '%', '%25'); + filepath = RegExpPrototypeSymbolReplace(percentRegEx, filepath, '%25'); } if (StringPrototypeIncludes(filepath, '\t')) { - filepath = StringPrototypeReplaceAll(filepath, '\t', '%09'); + filepath = RegExpPrototypeSymbolReplace(tabRegEx, filepath, '%09'); } if (StringPrototypeIncludes(filepath, '\n')) { - filepath = StringPrototypeReplaceAll(filepath, '\n', '%0A'); + filepath = RegExpPrototypeSymbolReplace(newlineRegEx, filepath, '%0A'); } if (StringPrototypeIncludes(filepath, '\r')) { - filepath = StringPrototypeReplaceAll(filepath, '\r', '%0D'); + filepath = RegExpPrototypeSymbolReplace(carriageReturnRegEx, filepath, '%0D'); } if (StringPrototypeIncludes(filepath, ' ')) { - filepath = StringPrototypeReplaceAll(filepath, ' ', '%20'); + filepath = RegExpPrototypeSymbolReplace(spaceRegEx, filepath, '%20'); } if (StringPrototypeIncludes(filepath, '"')) { - filepath = StringPrototypeReplaceAll(filepath, '"', '%22'); + filepath = RegExpPrototypeSymbolReplace(quoteRegEx, filepath, '%22'); } if (StringPrototypeIncludes(filepath, '#')) { - filepath = StringPrototypeReplaceAll(filepath, '#', '%23'); - } - if (StringPrototypeIncludes(filepath, '<')) { - filepath = StringPrototypeReplaceAll(filepath, '<', '%3C'); - } - if (StringPrototypeIncludes(filepath, '>')) { - filepath = StringPrototypeReplaceAll(filepath, '>', '%3E'); + filepath = RegExpPrototypeSymbolReplace(hashRegex, filepath, '%23'); } if (StringPrototypeIncludes(filepath, '?')) { - filepath = StringPrototypeReplaceAll(filepath, '?', '%3F'); + filepath = RegExpPrototypeSymbolReplace(questionMarkRegex, filepath, '%3F'); } if (StringPrototypeIncludes(filepath, '[')) { - filepath = StringPrototypeReplaceAll(filepath, '[', '%5B'); + filepath = RegExpPrototypeSymbolReplace(openSquareBracketRegEx, filepath, '%5B'); } // Back-slashes must be special-cased on Windows, where they are treated as path separator. if (!options.windows && StringPrototypeIncludes(filepath, '\\')) { - filepath = StringPrototypeReplaceAll(filepath, '\\', '%5C'); + filepath = RegExpPrototypeSymbolReplace(backslashRegEx, filepath, '%5C'); } if (StringPrototypeIncludes(filepath, ']')) { - filepath = StringPrototypeReplaceAll(filepath, ']', '%5D'); + filepath = RegExpPrototypeSymbolReplace(closeSquareBracketRegEx, filepath, '%5D'); } if (StringPrototypeIncludes(filepath, '^')) { - filepath = StringPrototypeReplaceAll(filepath, '^', '%5E'); - } - if (StringPrototypeIncludes(filepath, '`')) { - filepath = StringPrototypeReplaceAll(filepath, '`', '%60'); - } - if (StringPrototypeIncludes(filepath, '{')) { - filepath = StringPrototypeReplaceAll(filepath, '{', '%7B'); + filepath = RegExpPrototypeSymbolReplace(caretRegEx, filepath, '%5E'); } if (StringPrototypeIncludes(filepath, '|')) { - filepath = StringPrototypeReplaceAll(filepath, '|', '%7C'); + filepath = RegExpPrototypeSymbolReplace(verticalBarRegEx, filepath, '%7C'); } - if (StringPrototypeIncludes(filepath, '}')) { - filepath = StringPrototypeReplaceAll(filepath, '}', '%7D'); + if (StringPrototypeIncludes(filepath, '~')) { + filepath = RegExpPrototypeSymbolReplace(tildeRegEx, filepath, '%7E'); } return filepath; diff --git a/test/parallel/test-url-pathtofileurl.js b/test/parallel/test-url-pathtofileurl.js index c059cfe969875d..6e5fd4eb7459cc 100644 --- a/test/parallel/test-url-pathtofileurl.js +++ b/test/parallel/test-url-pathtofileurl.js @@ -157,12 +157,8 @@ const posixTestCases = [ { path: '/€', expected: 'file:///%E2%82%AC' }, // Rocket emoji (non-BMP code point) { path: '/🚀', expected: 'file:///%F0%9F%9A%80' }, - // caret - { path: '/foo^bar', expected: 'file:///foo%5Ebar' }, - // left bracket - { path: '/foo[bar', expected: 'file:///foo%5Bbar' }, - // right bracket - { path: '/foo]bar', expected: 'file:///foo%5Dbar' }, + // "unsafe" chars + { path: '/foo\r\n\t<>"#%{}|^[\\~]`?bar', expected: 'file:///foo%0D%0A%09%3C%3E%22%23%25%7B%7D%7C%5E%5B%5C%7E%5D%60%3Fbar' }, ]; for (const { path, expected } of windowsTestCases) { From 94ce4a24b940333c94cfb7f1c6da511870cb6f3b Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Thu, 19 Sep 2024 13:05:02 +0200 Subject: [PATCH 12/14] fixup! fixup! url: handle "unsafe" characters properly in `pathToFileURL` --- lib/internal/url.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/url.js b/lib/internal/url.js index da698e91f958c5..4f20e7fc98a17e 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -1597,7 +1597,7 @@ function pathToFileURL(filepath, options = kEmptyObject) { ); return outURL; } - let resolved = (windows ?? isWindows) ? path.win32.resolve(filepath) : path.posix.resolve(filepath); + const resolved = (windows ?? isWindows) ? path.win32.resolve(filepath) : path.posix.resolve(filepath); return new URL(`file://${encodePathChars(resolved, { windows })}`); } From 8e2ebb9e245b6195325e9d20ae2a9751bd850b67 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Fri, 20 Sep 2024 11:46:44 +0200 Subject: [PATCH 13/14] fixup! url: handle "unsafe" characters properly in `pathToFileURL` --- lib/internal/url.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/internal/url.js b/lib/internal/url.js index 4f20e7fc98a17e..b05b75ac1c809c 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -1565,8 +1565,8 @@ function encodePathChars(filepath, options = kEmptyObject) { } function pathToFileURL(filepath, options = kEmptyObject) { - const windows = options?.windows; - if ((windows ?? isWindows) && StringPrototypeStartsWith(filepath, '\\\\')) { + const windows = options?.windows ?? isWindows; + if (windows && StringPrototypeStartsWith(filepath, '\\\\')) { const outURL = new URL('file://'); // UNC path format: \\server\share\resource // Handle extended UNC path and standard UNC path @@ -1597,7 +1597,7 @@ function pathToFileURL(filepath, options = kEmptyObject) { ); return outURL; } - const resolved = (windows ?? isWindows) ? path.win32.resolve(filepath) : path.posix.resolve(filepath); + const resolved = windows ? path.win32.resolve(filepath) : path.posix.resolve(filepath); return new URL(`file://${encodePathChars(resolved, { windows })}`); } From dcb75faf72cdb1fd8e6a371428228fa16b7e159b Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Fri, 20 Sep 2024 15:37:20 +0200 Subject: [PATCH 14/14] fixup! url: handle "unsafe" characters properly in `pathToFileURL` --- test/parallel/test-url-pathtofileurl.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/parallel/test-url-pathtofileurl.js b/test/parallel/test-url-pathtofileurl.js index 6e5fd4eb7459cc..9c506e353f49e5 100644 --- a/test/parallel/test-url-pathtofileurl.js +++ b/test/parallel/test-url-pathtofileurl.js @@ -13,7 +13,7 @@ const url = require('url'); { const fileURL = url.pathToFileURL('test\\').href; assert.ok(fileURL.startsWith('file:///')); - assert.match(fileURL, isWindows ? /\\$/ : /%5C$/); + assert.match(fileURL, isWindows ? /\/$/ : /%5C$/); } {