mirror of
https://github.com/zebrajr/node.git
synced 2025-12-06 12:20:27 +01:00
lib: handle windows reserved device names on UNC
We have found that UNC paths weren't covered when .join/.normalize windows reserved device names (COM1, LPT1). PR-URL: https://github.com/nodejs/node/pull/59286 Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
parent
0946c8b28b
commit
a73b575304
37
lib/path.js
37
lib/path.js
|
|
@ -400,6 +400,13 @@ const win32 = {
|
||||||
// We matched a device root (e.g. \\\\.\\PHYSICALDRIVE0)
|
// We matched a device root (e.g. \\\\.\\PHYSICALDRIVE0)
|
||||||
device = `\\\\${firstPart}`;
|
device = `\\\\${firstPart}`;
|
||||||
rootEnd = 4;
|
rootEnd = 4;
|
||||||
|
const colonIndex = StringPrototypeIndexOf(path, ':');
|
||||||
|
// Special case: handle \\?\COM1: or similar reserved device paths
|
||||||
|
const possibleDevice = StringPrototypeSlice(path, 4, colonIndex + 1);
|
||||||
|
if (isWindowsReservedName(possibleDevice, possibleDevice.length - 1)) {
|
||||||
|
device = `\\\\?\\${possibleDevice}`;
|
||||||
|
rootEnd = 4 + possibleDevice.length;
|
||||||
|
}
|
||||||
} else if (j === len) {
|
} else if (j === len) {
|
||||||
// We matched a UNC root only
|
// We matched a UNC root only
|
||||||
// Return the normalized version of the UNC root since there
|
// Return the normalized version of the UNC root since there
|
||||||
|
|
@ -558,6 +565,36 @@ const win32 = {
|
||||||
joined = `\\${StringPrototypeSlice(joined, slashCount)}`;
|
joined = `\\${StringPrototypeSlice(joined, slashCount)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skip normalization when reserved device names are present
|
||||||
|
const parts = [];
|
||||||
|
let part = '';
|
||||||
|
|
||||||
|
for (let i = 0; i < joined.length; i++) {
|
||||||
|
if (joined[i] === '\\') {
|
||||||
|
if (part) parts.push(part);
|
||||||
|
part = '';
|
||||||
|
// Skip consecutive backslashes
|
||||||
|
while (i + 1 < joined.length && joined[i + 1] === '\\') i++;
|
||||||
|
} else {
|
||||||
|
part += joined[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add the final part if any
|
||||||
|
if (part) parts.push(part);
|
||||||
|
|
||||||
|
// Check if any part has a Windows reserved name
|
||||||
|
if (parts.some((p) => {
|
||||||
|
const colonIndex = StringPrototypeIndexOf(p, ':');
|
||||||
|
return colonIndex !== -1 && isWindowsReservedName(p, colonIndex);
|
||||||
|
})) {
|
||||||
|
// Replace forward slashes with backslashes
|
||||||
|
let result = '';
|
||||||
|
for (let i = 0; i < joined.length; i++) {
|
||||||
|
result += joined[i] === '/' ? '\\' : joined[i];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
return win32.normalize(joined);
|
return win32.normalize(joined);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,14 @@ joinTests.push([
|
||||||
[['c:.', 'file'], 'c:file'],
|
[['c:.', 'file'], 'c:file'],
|
||||||
[['c:', '/'], 'c:\\'],
|
[['c:', '/'], 'c:\\'],
|
||||||
[['c:', 'file'], 'c:\\file'],
|
[['c:', 'file'], 'c:\\file'],
|
||||||
|
// UNC path join tests (Windows)
|
||||||
|
[['\\server\\share', 'file.txt'], '\\server\\share\\file.txt'],
|
||||||
|
[['\\server\\share', 'folder', 'another.txt'], '\\server\\share\\folder\\another.txt'],
|
||||||
|
[['\\server\\share', 'COM1:'], '\\server\\share\\COM1:'],
|
||||||
|
[['\\server\\share', 'path', 'LPT1:'], '\\server\\share\\path\\LPT1:'],
|
||||||
|
[['\\fileserver\\public\\uploads', 'CON:..\\..\\..\\private\\db.conf'],
|
||||||
|
'\\fileserver\\public\\uploads\\CON:..\\..\\..\\private\\db.conf'],
|
||||||
|
|
||||||
// Path traversal in previous versions of Node.js.
|
// Path traversal in previous versions of Node.js.
|
||||||
[['./upload', '/../C:/Windows'], '.\\C:\\Windows'],
|
[['./upload', '/../C:/Windows'], '.\\C:\\Windows'],
|
||||||
[['upload', '../', 'C:foo'], '.\\C:foo'],
|
[['upload', '../', 'C:foo'], '.\\C:foo'],
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,19 @@ if (!common.isWindows) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const normalizeDeviceNameTests = [
|
const normalizeDeviceNameTests = [
|
||||||
|
// UNC paths: \\server\share\... is a Windows UNC path, where 'server' is the network server name and 'share'
|
||||||
|
// is the shared folder. These are used for network file access and are subject to reserved device name
|
||||||
|
// checks after the share.
|
||||||
|
{ input: '\\\\server\\share\\COM1:', expected: '\\\\server\\share\\COM1:' },
|
||||||
|
{ input: '\\\\server\\share\\PRN:', expected: '\\\\server\\share\\PRN:' },
|
||||||
|
{ input: '\\\\server\\share\\AUX:', expected: '\\\\server\\share\\AUX:' },
|
||||||
|
{ input: '\\\\server\\share\\LPT1:', expected: '\\\\server\\share\\LPT1:' },
|
||||||
|
{ input: '\\\\server\\share\\COM1:\\foo\\bar', expected: '\\\\server\\share\\COM1:\\foo\\bar' },
|
||||||
|
{ input: '\\\\server\\share\\path\\COM1:', expected: '\\\\server\\share\\path\\COM1:' },
|
||||||
|
{ input: '\\\\server\\share\\COM1:..\\..\\..\\..\\Windows', expected: '\\\\server\\share\\Windows' },
|
||||||
|
{ input: '\\\\server\\share\\path\\to\\LPT9:..\\..\\..\\..\\..\\..\\..\\..\\..\\file.txt',
|
||||||
|
expected: '\\\\server\\share\\file.txt' },
|
||||||
|
|
||||||
{ input: 'CON', expected: 'CON' },
|
{ input: 'CON', expected: 'CON' },
|
||||||
{ input: 'con', expected: 'con' },
|
{ input: 'con', expected: 'con' },
|
||||||
{ input: 'CON:', expected: '.\\CON:.' },
|
{ input: 'CON:', expected: '.\\CON:.' },
|
||||||
|
|
@ -81,6 +94,8 @@ const normalizeDeviceNameTests = [
|
||||||
// Test cases from original vulnerability reports or similar scenarios
|
// Test cases from original vulnerability reports or similar scenarios
|
||||||
{ input: 'COM1:.\\..\\..\\foo.js', expected: '.\\COM1:..\\..\\foo.js' },
|
{ input: 'COM1:.\\..\\..\\foo.js', expected: '.\\COM1:..\\..\\foo.js' },
|
||||||
{ input: 'LPT1:.\\..\\..\\another.txt', expected: '.\\LPT1:..\\..\\another.txt' },
|
{ input: 'LPT1:.\\..\\..\\another.txt', expected: '.\\LPT1:..\\..\\another.txt' },
|
||||||
|
// UNC paths
|
||||||
|
{ input: '\\\\?\\COM1:.\\..\\..\\foo2.js', expected: '\\\\?\\COM1:\\foo2.js' },
|
||||||
|
|
||||||
// Paths with device names not at the beginning
|
// Paths with device names not at the beginning
|
||||||
{ input: 'C:\\CON', expected: 'C:\\CON' },
|
{ input: 'C:\\CON', expected: 'C:\\CON' },
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user