mirror of
https://github.com/nodejs/node.git
synced 2024-11-21 10:59:27 +00:00
url: support LF, CR and TAB in pathToFileURL
Fixes: https://github.com/nodejs/node/issues/23696 PR-URL: https://github.com/nodejs/node/pull/23720 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Guy Bedford <guybedford@gmail.com> Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
This commit is contained in:
parent
2a55e7116e
commit
ef0c178c35
@ -1339,11 +1339,22 @@ function fileURLToPath(path) {
|
||||
return isWindows ? getPathFromURLWin32(path) : getPathFromURLPosix(path);
|
||||
}
|
||||
|
||||
// We percent-encode % character when converting from file path to URL,
|
||||
// as this is the only character that won't be percent encoded by
|
||||
// default URL percent encoding when pathname is set.
|
||||
// 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;
|
||||
const backslashRegEx = /\\/g;
|
||||
const newlineRegEx = /\n/g;
|
||||
const carriageReturnRegEx = /\r/g;
|
||||
const tabRegEx = /\t/g;
|
||||
function pathToFileURL(filepath) {
|
||||
let resolved = path.resolve(filepath);
|
||||
// path.resolve strips trailing slashes so we must add them back
|
||||
@ -1358,6 +1369,12 @@ function pathToFileURL(filepath) {
|
||||
// in posix, "/" is a valid character in paths
|
||||
if (!isWindows && resolved.includes('\\'))
|
||||
resolved = resolved.replace(backslashRegEx, '%5C');
|
||||
if (resolved.includes('\n'))
|
||||
resolved = resolved.replace(newlineRegEx, '%0A');
|
||||
if (resolved.includes('\r'))
|
||||
resolved = resolved.replace(carriageReturnRegEx, '%0D');
|
||||
if (resolved.includes('\t'))
|
||||
resolved = resolved.replace(tabRegEx, '%09');
|
||||
outURL.pathname = resolved;
|
||||
return outURL;
|
||||
}
|
||||
|
@ -22,3 +22,103 @@ const url = require('url');
|
||||
const fileURL = url.pathToFileURL('test/%').href;
|
||||
assert.ok(fileURL.includes('%25'));
|
||||
}
|
||||
|
||||
{
|
||||
let testCases;
|
||||
if (isWindows) {
|
||||
testCases = [
|
||||
// lowercase ascii alpha
|
||||
{ path: 'C:\\foo', expected: 'file:///C:/foo' },
|
||||
// uppercase ascii alpha
|
||||
{ path: 'C:\\FOO', expected: 'file:///C:/FOO' },
|
||||
// dir
|
||||
{ path: 'C:\\dir\\foo', expected: 'file:///C:/dir/foo' },
|
||||
// trailing separator
|
||||
{ path: 'C:\\dir\\', expected: 'file:///C:/dir/' },
|
||||
// dot
|
||||
{ path: 'C:\\foo.mjs', expected: 'file:///C:/foo.mjs' },
|
||||
// space
|
||||
{ path: 'C:\\foo bar', expected: 'file:///C:/foo%20bar' },
|
||||
// question mark
|
||||
{ path: 'C:\\foo?bar', expected: 'file:///C:/foo%3Fbar' },
|
||||
// number sign
|
||||
{ path: 'C:\\foo#bar', expected: 'file:///C:/foo%23bar' },
|
||||
// ampersand
|
||||
{ path: 'C:\\foo&bar', expected: 'file:///C:/foo&bar' },
|
||||
// equals
|
||||
{ path: 'C:\\foo=bar', expected: 'file:///C:/foo=bar' },
|
||||
// colon
|
||||
{ path: 'C:\\foo:bar', expected: 'file:///C:/foo:bar' },
|
||||
// semicolon
|
||||
{ path: 'C:\\foo;bar', expected: 'file:///C:/foo;bar' },
|
||||
// percent
|
||||
{ path: 'C:\\foo%bar', expected: 'file:///C:/foo%25bar' },
|
||||
// backslash
|
||||
{ path: 'C:\\foo\\bar', expected: 'file:///C:/foo/bar' },
|
||||
// backspace
|
||||
{ path: 'C:\\foo\bbar', expected: 'file:///C:/foo%08bar' },
|
||||
// tab
|
||||
{ path: 'C:\\foo\tbar', expected: 'file:///C:/foo%09bar' },
|
||||
// newline
|
||||
{ path: 'C:\\foo\nbar', expected: 'file:///C:/foo%0Abar' },
|
||||
// carriage return
|
||||
{ path: 'C:\\foo\rbar', expected: 'file:///C:/foo%0Dbar' },
|
||||
// latin1
|
||||
{ path: 'C:\\fóóbàr', expected: 'file:///C:/f%C3%B3%C3%B3b%C3%A0r' },
|
||||
// euro sign (BMP code point)
|
||||
{ path: 'C:\\€', expected: 'file:///C:/%E2%82%AC' },
|
||||
// rocket emoji (non-BMP code point)
|
||||
{ path: 'C:\\🚀', expected: 'file:///C:/%F0%9F%9A%80' }
|
||||
];
|
||||
} else {
|
||||
testCases = [
|
||||
// lowercase ascii alpha
|
||||
{ path: '/foo', expected: 'file:///foo' },
|
||||
// uppercase ascii alpha
|
||||
{ path: '/FOO', expected: 'file:///FOO' },
|
||||
// dir
|
||||
{ path: '/dir/foo', expected: 'file:///dir/foo' },
|
||||
// trailing separator
|
||||
{ path: '/dir/', expected: 'file:///dir/' },
|
||||
// dot
|
||||
{ path: '/foo.mjs', expected: 'file:///foo.mjs' },
|
||||
// space
|
||||
{ path: '/foo bar', expected: 'file:///foo%20bar' },
|
||||
// question mark
|
||||
{ path: '/foo?bar', expected: 'file:///foo%3Fbar' },
|
||||
// number sign
|
||||
{ path: '/foo#bar', expected: 'file:///foo%23bar' },
|
||||
// ampersand
|
||||
{ path: '/foo&bar', expected: 'file:///foo&bar' },
|
||||
// equals
|
||||
{ path: '/foo=bar', expected: 'file:///foo=bar' },
|
||||
// colon
|
||||
{ path: '/foo:bar', expected: 'file:///foo:bar' },
|
||||
// semicolon
|
||||
{ path: '/foo;bar', expected: 'file:///foo;bar' },
|
||||
// percent
|
||||
{ path: '/foo%bar', expected: 'file:///foo%25bar' },
|
||||
// backslash
|
||||
{ path: '/foo\\bar', expected: 'file:///foo%5Cbar' },
|
||||
// backspace
|
||||
{ path: '/foo\bbar', expected: 'file:///foo%08bar' },
|
||||
// tab
|
||||
{ path: '/foo\tbar', expected: 'file:///foo%09bar' },
|
||||
// newline
|
||||
{ path: '/foo\nbar', expected: 'file:///foo%0Abar' },
|
||||
// carriage return
|
||||
{ path: '/foo\rbar', expected: 'file:///foo%0Dbar' },
|
||||
// latin1
|
||||
{ path: '/fóóbàr', expected: 'file:///f%C3%B3%C3%B3b%C3%A0r' },
|
||||
// euro sign (BMP code point)
|
||||
{ path: '/€', expected: 'file:///%E2%82%AC' },
|
||||
// rocket emoji (non-BMP code point)
|
||||
{ path: '/🚀', expected: 'file:///%F0%9F%9A%80' },
|
||||
];
|
||||
}
|
||||
|
||||
for (const { path, expected } of testCases) {
|
||||
const actual = url.pathToFileURL(path).href;
|
||||
assert.strictEqual(actual, expected);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user