Skip to content

Instantly share code, notes, and snippets.

@bpasero
Created June 13, 2023 17:02
Show Gist options
  • Save bpasero/ad230c2c8921e5d0470a1466b130b24f to your computer and use it in GitHub Desktop.
Save bpasero/ad230c2c8921e5d0470a1466b130b24f to your computer and use it in GitHub Desktop.
VS Code CVE-2023-33144
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