-
-
Save bpasero/ad230c2c8921e5d0470a1466b130b24f to your computer and use it in GitHub Desktop.
VS Code CVE-2023-33144
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/lib/internal/fs/utils.js b/lib/internal/fs/utils.js | |
index 96717447d1..55d9a54216 100644 | |
--- a/lib/internal/fs/utils.js | |
+++ b/lib/internal/fs/utils.js | |
@@ -700,7 +700,7 @@ const validatePath = hideStackFrames((path, propName = 'path') => { | |
if (isWindows && | |
process.enableUNCAccessChecks) { | |
- const hostname = getUNCHostname(path); | |
+ const hostname = getUNCHostname(path, true); | |
if (hostname && !process.uncHostAllowlist.has(hostname)) { | |
throw new ERR_UNC_HOST_NOT_ALLOWED(hostname); | |
} | |
@@ -722,17 +722,22 @@ const startsWithWindowsDeviceRoot = hideStackFrames(path => { | |
isPathSeparator(StringPrototypeCharCodeAt(path, 2)); | |
}); | |
-const getUNCHostname = hideStackFrames(path => { | |
+function normalizeTrailingDot(segment) { | |
+ if (segment.length > 1 && StringPrototypeCharCodeAt(segment, segment.length - 1) === CHAR_DOT && StringPrototypeCharCodeAt(segment, segment.length - 2) !== CHAR_DOT) { | |
+ return StringPrototypeSlice(segment, 0, -1); | |
+ } | |
+ return segment; | |
+} | |
+ | |
+const getUNCHostname = hideStackFrames((path, throwOnGlobalRoot = false) => { | |
if (isUint8Array(path)) { | |
path = '' + path; | |
} | |
const pathIsString = typeof path === 'string'; | |
- let hostname = ''; | |
- | |
if (isWindows && | |
- pathIsString && | |
- isPathSeparator(StringPrototypeCharCodeAt(path, 0))) { | |
- const len = path.length; | |
+ pathIsString && | |
+ isPathSeparator(StringPrototypeCharCodeAt(path, 0))) { | |
+ let len = path.length; | |
// Possible UNC root | |
if (isPathSeparator(StringPrototypeCharCodeAt(path, 1))) { | |
// Matched double path separator at beginning | |
@@ -744,7 +749,7 @@ const getUNCHostname = hideStackFrames(path => { | |
j++; | |
} | |
if (j === len || j !== last) { | |
- hostname = StringPrototypeToLowerCase(StringPrototypeSlice(path, last, j)); | |
+ let hostname = StringPrototypeToLowerCase(StringPrototypeSlice(path, last, j)); | |
if (hostname.length === 1 && | |
(StringPrototypeCharCodeAt(hostname, 0) === CHAR_QUESTION_MARK || | |
StringPrototypeCharCodeAt(hostname, 0) === CHAR_DOT)) { | |
@@ -758,12 +763,20 @@ const getUNCHostname = hideStackFrames(path => { | |
if (j < len && j !== last) { | |
// Possible long UNC path! | |
last = j; | |
+ // In the next step we are looking for 'unc' as segment | |
+ // but we have to take any '..' into account so path | |
+ // normalization is required. We explicitly pass in an | |
+ // absolute path with a leading path separator (hence 'j -1') | |
+ // to make sure that normalization does not preserve any leading '..'. | |
+ path = `${StringPrototypeSlice(path, 0, j - 1)}${pathModule.win32.normalize(StringPrototypeSlice(path, j - 1, path.length))}`; | |
+ len = path.length; | |
// Match 1 or more non-path separators | |
while (j < len && | |
!isPathSeparator(StringPrototypeCharCodeAt(path, j))) { | |
j++; | |
} | |
- if (StringPrototypeToLowerCase(StringPrototypeSlice(path, last, j)) === 'unc') { | |
+ const device = normalizeTrailingDot(StringPrototypeToLowerCase(StringPrototypeSlice(path, last, j))); | |
+ if (device === 'unc') { | |
// UNC path! | |
last = j; | |
// Match 1 or more path separators | |
@@ -779,9 +792,15 @@ const getUNCHostname = hideStackFrames(path => { | |
j++; | |
} | |
if (j === len || j !== last) { | |
- return StringPrototypeToLowerCase(StringPrototypeSlice(path, last, j)); | |
+ return normalizeTrailingDot(StringPrototypeToLowerCase(StringPrototypeSlice(path, last, j))); | |
} | |
} | |
+ } else if (throwOnGlobalRoot && (device === 'global' || device === 'globalroot')) { | |
+ throw new ERR_INVALID_ARG_VALUE( | |
+ 'path', | |
+ path, | |
+ 'uses an unsupported prefix of "\\\\?\\globalroot" or "\\\\?\\global"' | |
+ ); | |
} | |
} | |
} else { | |
@@ -790,7 +809,6 @@ const getUNCHostname = hideStackFrames(path => { | |
} | |
} | |
} | |
- | |
return undefined; | |
}); | |
diff --git a/test/parallel/test-fs-utils-get-unc-hostname.js b/test/parallel/test-fs-utils-get-unc-hostname.js | |
index d6fb6cace8..05873cb548 100644 | |
--- a/test/parallel/test-fs-utils-get-unc-hostname.js | |
+++ b/test/parallel/test-fs-utils-get-unc-hostname.js | |
@@ -13,7 +13,10 @@ assert.strictEqual(getUNCHostname('\\\\.'), undefined); | |
assert.strictEqual(getUNCHostname('\\\\?'), undefined); | |
assert.strictEqual(getUNCHostname('\\\\?\\share'), undefined); | |
assert.strictEqual(getUNCHostname('\\\\.\\share'), undefined); | |
-assert.strictEqual(getUNCHostname(Buffer.from('\\\\server\\share\\dir\\file.ext')), 'server'); | |
+ | |
+assert.strictEqual(getUNCHostname('\\\\.foo\\share'), '.foo'); | |
+assert.strictEqual(getUNCHostname('\\\\?foo\\share'), '?foo'); | |
+ | |
assert.strictEqual(getUNCHostname('\\\\server\\share\\dir\\file.ext'), 'server'); | |
assert.strictEqual(getUNCHostname('\\\\server'), 'server'); | |
assert.strictEqual(getUNCHostname('\\\\SERVER\\share\\dir\\file.ext'), 'server'); | |
@@ -31,4 +34,98 @@ assert.strictEqual(getUNCHostname('\\\\.\\UNC\\localhost\\'), 'localhost'); | |
assert.strictEqual(getUNCHostname(Buffer.from('\\\\.\\UNC\\localhost\\')), 'localhost'); | |
assert.strictEqual(getUNCHostname('\\\\?\\UNC\\localhost\\'), 'localhost'); | |
assert.strictEqual(getUNCHostname('\\\\.\\UNC\\localhost\\a'), 'localhost'); | |
-assert.strictEqual(getUNCHostname('\\\\?\\UNC\\localhost\\a'), 'localhost'); | |
\ No newline at end of file | |
+assert.strictEqual(getUNCHostname('\\\\?\\UNC\\localhost\\a'), 'localhost'); | |
+ | |
+assert.strictEqual(getUNCHostname('\\\\?\\foo\\..\\unc\\share\\dir\\file.ext'), 'share'); | |
+assert.strictEqual(getUNCHostname('\\\\?\\..\\unc\\share\\dir\\file.ext'), 'share'); | |
+ | |
+assert.strictEqual(getUNCHostname('\\\\.\\unc'), undefined); | |
+assert.strictEqual(getUNCHostname('\\\\.\\unc\\'), undefined); | |
+assert.strictEqual(getUNCHostname('\\\\.\\unc\\share'), 'share'); | |
+assert.strictEqual(getUNCHostname('\\\\.\\unc\\\\\\share'), 'share'); | |
+ | |
+assert.strictEqual(getUNCHostname('\\\\.\\unc\\localhost.\\c$'), 'localhost'); | |
+assert.strictEqual(getUNCHostname('\\\\.\\unc\\localhost..\\c$'), 'localhost..'); | |
+ | |
+assert.strictEqual(getUNCHostname('\\\\?\\unc\\localhost\\..\\share\\c$'), 'share'); | |
+assert.strictEqual(getUNCHostname('\\\\?\\unc\\localhost\\..\\share\\c$'), 'share'); | |
+ | |
+assert.strictEqual(getUNCHostname('\\\\.\\.\\unc\\share\\dir\\file.ext'), 'share'); | |
+assert.strictEqual(getUNCHostname('\\\\.\\..\\unc\\share\\dir\\file.ext'), 'share'); | |
+assert.strictEqual(getUNCHostname('\\\\.\\..\\.\\..\\unc\\share\\dir\\file.ext'), 'share'); | |
+ | |
+assert.strictEqual(getUNCHostname('\\\\.\\unc\\.\\share\\dir\\file.ext'), 'share'); | |
+assert.strictEqual(getUNCHostname('\\\\.\\unc\\..\\share\\dir\\file.ext'), undefined); | |
+assert.strictEqual(getUNCHostname('\\\\.\\unc\\..\\unc\\share\\dir\\file.ext'), 'share'); | |
+assert.strictEqual(getUNCHostname('\\\\.\\unc\\..\\.\\unc\\share\\dir\\file.ext'), 'share'); | |
+assert.strictEqual(getUNCHostname('\\\\.\\unc\\.\\..\\unc\\share\\dir\\file.ext'), 'share'); | |
+ | |
+assert.strictEqual(getUNCHostname('\\\\.\\foo\\..\\unc\\share\\dir\\file.ext'), 'share'); | |
+assert.strictEqual(getUNCHostname('\\\\.\\foo\\.\\..\\unc\\share\\dir\\file.ext'), 'share'); | |
+assert.strictEqual(getUNCHostname('\\\\.\\foo\\..\\.\\unc\\share\\dir\\file.ext'), 'share'); | |
+assert.strictEqual(getUNCHostname('\\\\.\\foo\\..\\..\\unc\\share\\dir\\file.ext'), 'share'); | |
+assert.strictEqual(getUNCHostname('\\\\.\\foo\\..\\unc\\attacker.example\\zzzz3'), 'attacker.example'); | |
+ | |
+assert.strictEqual(getUNCHostname('\\\\.\\\\\\foo\\\\\\..\\\\\\unc\\\\\\share\\dir\\file.ext'), 'share'); | |
+assert.strictEqual(getUNCHostname('\\\\.\\\\\\foo\\\\\\.\\\\\\..\\\\\\unc\\\\\\share\\dir\\file.ext'), 'share'); | |
+assert.strictEqual(getUNCHostname('\\\\.\\\\\\foo\\\\\\..\\\\\\.\\\\\\unc\\\\\\share\\dir\\file.ext'), 'share'); | |
+assert.strictEqual(getUNCHostname('\\\\.\\\\\\foo\\\\\\..\\\\\\..\\\\\\unc\\\\\\share\\dir\\file.ext'), 'share'); | |
+ | |
+assert.strictEqual(getUNCHostname('\\\\.\\foo/..\\unc/attacker.example\\zzzz3'), 'attacker.example'); | |
+assert.strictEqual(getUNCHostname('//./foo/../unc/attacker.example/zzzz3'), 'attacker.example'); | |
+assert.strictEqual(getUNCHostname('\\\\.\\foo/...\\unc/attacker.example\\zzzz3'), undefined); | |
+assert.strictEqual(getUNCHostname('//./foo/.../unc/attacker.example/zzzz3'), undefined); | |
+assert.strictEqual(getUNCHostname('\\\\.\\foo\\.\\..\\.\\unc\\attacker.example\\zzzz3'), 'attacker.example'); | |
+assert.strictEqual(getUNCHostname('\\\\.\\foo/.\\..\\./unc\\attacker.example\\zzzz3'), 'attacker.example'); | |
+ | |
+assert.strictEqual(getUNCHostname('\\\\.\\..\\foo\\..\\unc\\localhost\\c$'), 'localhost'); | |
+assert.strictEqual(getUNCHostname('\\\\.\\..\\..\\..\\foo\\..\\unc\\localhost\\c$'), 'localhost'); | |
+assert.strictEqual(getUNCHostname('\\\\.\\bar\\..\\foo\\..\\unc\\localhost\\c$'), 'localhost'); | |
+assert.strictEqual(getUNCHostname('\\\\.\\.\\foo\\..\\unc\\localhost\\c$'), 'localhost'); | |
+ | |
+assert.strictEqual(getUNCHostname('\\\\.\\unc.\\.\\share\\dir\\file.ext'), 'share'); | |
+assert.strictEqual(getUNCHostname('\\\\.\\foo\\..\\unc.\\attacker.example\\zzzz3'), 'attacker.example'); | |
+ | |
+assert.strictEqual(getUNCHostname("//././unc./localhost\\c$"), "localhost"); | |
+ | |
+assert.strictEqual(getUNCHostname("\\\\.\\global\\Device\\MailslotRedirector\\localhost\\c$"), undefined); | |
+assert.strictEqual(getUNCHostname("\\\\?\\globalroot\\Device\\MailslotRedirector\\localhost\\c$"), undefined); | |
+ | |
+assert.throws(() => getUNCHostname("\\\\.\\global\\Device\\MailslotRedirector\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\?\\global\\Device\\MailslotRedirector\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\.\\global\\Device\\Csc\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\?\\global\\Device\\Csc\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\.\\global\\Device\\P9Rdr\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\?\\global\\Device\\P9Rdr\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\.\\global\\Device\\PrlMiniRdr\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\?\\global\\Device\\PrlMiniRdr\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\.\\globalroot\\Device\\MailslotRedirector\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\?\\globalroot\\Device\\MailslotRedirector\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\.\\globalroot\\Device\\Csc\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\?\\globalroot\\Device\\Csc\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\.\\globalroot\\Device\\P9Rdr\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\?\\globalroot\\Device\\P9Rdr\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\.\\globalroot\\Device\\PrlMiniRdr\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\?\\globalroot\\Device\\PrlMiniRdr\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\?\\global\\unc\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\.\\global\\unc\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\?\\globalroot\\??\\unc\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\.\\globalroot\\??\\unc\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\?\\globalroot\\Device\\Mup\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\.\\globalroot\\Device\\Mup\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\?\\globalroot\\Device\\LanManRedirector\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\.\\globalroot\\Device\\LanManRedirector\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\?\\globalroot\\DosDevices\\unc\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\.\\globalroot\\DosDevices\\unc\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\?/foo\\..\\global\\unc\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\?/foo\\..\\global.\\unc.\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\?/foo\\..\\global.\\unc\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("//?/foo/../global/unc/localhost/c$", true)); | |
+assert.throws(() => getUNCHostname("//?/foo/../global./unc./localhost/c$", true)); | |
+assert.throws(() => getUNCHostname("//?/foo/../global./unc/localhost/c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\.\\foo\\..\\global\\unc\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\./foo\\..\\global.\\unc.\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("\\\\./foo\\..\\global.\\unc\\localhost\\c$", true)); | |
+assert.throws(() => getUNCHostname("//./foo/../global/unc/localhost/c$", true)); | |
+assert.throws(() => getUNCHostname("//./foo/../global./unc./localhost/c$", true)); | |
+assert.throws(() => getUNCHostname("//./foo/../global./unc/localhost/c$", true)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment