diff --git a/src/main.js b/src/main.js index f777d4e8a4a..83cd3ade0f8 100644 --- a/src/main.js +++ b/src/main.js @@ -22,6 +22,7 @@ const bootstrap = require('./bootstrap'); const bootstrapNode = require('./bootstrap-node'); const { getUserDataPath } = require('./vs/platform/environment/node/userDataPath'); const { stripComments } = require('./vs/base/common/stripComments'); +const { getUNCHost, addUNCHostToAllowlist } = require('./vs/base/node/unc'); /** @type {Partial} */ const product = require('../product.json'); const { app, protocol, crashReporter, Menu } = require('electron'); @@ -35,6 +36,12 @@ bootstrap.enableASARSupport(); // Set userData path before app 'ready' event const args = parseCLIArgs(); const userDataPath = getUserDataPath(args, product.nameShort ?? 'code-oss-dev'); +if (process.platform === 'win32') { + const userDataUNCHost = getUNCHost(userDataPath); + if (userDataUNCHost) { + addUNCHostToAllowlist(userDataUNCHost); // enables to use UNC paths in userDataPath + } +} app.setPath('userData', userDataPath); // Resolve code cache path diff --git a/src/vs/base/node/unc.d.ts b/src/vs/base/node/unc.d.ts new file mode 100644 index 00000000000..1e78aba97a1 --- /dev/null +++ b/src/vs/base/node/unc.d.ts @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * Helper to get the hostname of a possible UNC path. + */ +export function getUNCHost(maybeUNCPath: string | undefined | null): string | undefined; + +/** + * Returns the current list of allowed UNC hosts as defined by node.js. + */ +export function getUNCHostAllowlist(): string[]; + +/** + * Adds one to many UNC host(s) to the allowed list in node.js. + */ +export function addUNCHostToAllowlist(allowedHost: string | string[]): void; diff --git a/src/vs/base/node/unc.js b/src/vs/base/node/unc.js new file mode 100644 index 00000000000..7b046936764 --- /dev/null +++ b/src/vs/base/node/unc.js @@ -0,0 +1,128 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +//@ts-check + +(function () { + function factory() { + + /** + * @returns {Set | undefined} + */ + function processUNCHostAllowlist() { + + // The property `process.uncHostAllowlist` is not available in official node.js + // releases, only in our own builds, so we have to probe for availability + + return process.uncHostAllowlist; + } + + /** + * @param {unknown} arg0 + * @returns {string[]} + */ + function toSafeStringArray(arg0) { + const allowedUNCHosts = new Set(); + + if (Array.isArray(arg0)) { + for (const host of arg0) { + if (typeof host === 'string') { + allowedUNCHosts.add(host); + } + } + } + + return Array.from(allowedUNCHosts); + } + + /** + * @returns {string[]} + */ + function getUNCHostAllowlist() { + const allowlist = processUNCHostAllowlist(); + if (allowlist) { + return Array.from(allowlist); + } + + return []; + } + + /** + * @param {string | string[]} allowedHost + */ + function addUNCHostToAllowlist(allowedHost) { + if (process.platform !== 'win32') { + return; + } + + const allowlist = processUNCHostAllowlist(); + if (allowlist) { + if (typeof allowedHost === 'string') { + allowlist.add(allowedHost); + } else { + for (const host of toSafeStringArray(allowedHost)) { + allowlist.add(host); + } + } + } + } + + /** + * @param {string | undefined | null} maybeUNCPath + * @returns {string | undefined} + */ + function getUNCHost(maybeUNCPath) { + if (typeof maybeUNCPath !== 'string') { + return undefined; // require a valid string + } + + const uncRoots = [ + '\\\\.\\UNC\\', // DOS Device paths (https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats) + '\\\\?\\UNC\\', + '\\\\' // standard UNC path + ]; + + let host = undefined; + + for (const uncRoot of uncRoots) { + const indexOfUNCRoot = maybeUNCPath.indexOf(uncRoot); + if (indexOfUNCRoot !== 0) { + continue; // not matching any of our expected UNC roots + } + + const indexOfUNCPath = maybeUNCPath.indexOf('\\', uncRoot.length); + if (indexOfUNCPath === -1) { + continue; // no path component found + } + + const hostCandidate = maybeUNCPath.substring(uncRoot.length, indexOfUNCPath); + if (hostCandidate) { + host = hostCandidate; + break; + } + } + + return host; + } + + return { + getUNCHostAllowlist, + addUNCHostToAllowlist, + getUNCHost + }; + } + + if (typeof define === 'function') { + // amd + define([], function () { return factory(); }); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + // commonjs + module.exports = factory(); + } else { + console.trace('vs/base/node/unc defined in UNKNOWN context (neither requirejs or commonjs)'); + } +})(); diff --git a/src/vs/base/node/unc.ts b/src/vs/base/node/unc.ts deleted file mode 100644 index f5a0a1cda5b..00000000000 --- a/src/vs/base/node/unc.ts +++ /dev/null @@ -1,65 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { isWindows } from 'vs/base/common/platform'; - -function processUNCHostAllowlist(): Set | undefined { - - // The property `process.uncHostAllowlist` is not available in official node.js - // releases, only in our own builds, so we have to probe for availability - - const processWithUNCHostAllowlist = process as typeof process & { readonly uncHostAllowlist?: Set }; - - return processWithUNCHostAllowlist.uncHostAllowlist; -} - -export function setUNCHostAllowlist(allowedHosts: string[]): void { - if (!isWindows) { - return; - } - - const allowlist = processUNCHostAllowlist(); - if (allowlist) { - allowlist.clear(); - - for (const allowedHost of allowedHosts) { - allowlist.add(allowedHost); - } - } -} - -export function getUNCHostAllowlist(): string[] { - const allowlist = processUNCHostAllowlist(); - if (allowlist) { - return Array.from(allowlist); - } - - return []; -} - -export function addUNCHostToAllowlist(allowedHost: string): void { - if (!isWindows) { - return; - } - - const allowlist = processUNCHostAllowlist(); - if (allowlist) { - allowlist.add(allowedHost); - } -} - -export function toUNCHostAllowlist(arg0: unknown): string[] { - const allowedUNCHosts = new Set(); - - if (Array.isArray(arg0)) { - for (const host of arg0) { - if (typeof host === 'string') { - allowedUNCHosts.add(host); - } - } - } - - return Array.from(allowedUNCHosts); -} diff --git a/src/vs/base/test/node/unc.test.ts b/src/vs/base/test/node/unc.test.ts new file mode 100644 index 00000000000..82eaf25860d --- /dev/null +++ b/src/vs/base/test/node/unc.test.ts @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { strictEqual } from 'assert'; +import { getUNCHost } from 'vs/base/node/unc'; + +suite('UNC', () => { + + test('getUNCHost', () => { + + strictEqual(getUNCHost(undefined), undefined); + strictEqual(getUNCHost(null), undefined); + + strictEqual(getUNCHost('/'), undefined); + strictEqual(getUNCHost('/foo'), undefined); + + strictEqual(getUNCHost('c:'), undefined); + strictEqual(getUNCHost('c:\\'), undefined); + strictEqual(getUNCHost('c:\\foo'), undefined); + strictEqual(getUNCHost('c:\\foo\\\\server\\path'), undefined); + + strictEqual(getUNCHost('\\'), undefined); + strictEqual(getUNCHost('\\\\'), undefined); + strictEqual(getUNCHost('\\\\localhost'), undefined); + + strictEqual(getUNCHost('\\\\localhost\\'), 'localhost'); + strictEqual(getUNCHost('\\\\localhost\\a'), 'localhost'); + + strictEqual(getUNCHost('\\\\.'), undefined); + strictEqual(getUNCHost('\\\\?'), undefined); + + strictEqual(getUNCHost('\\\\.\\localhost'), '.'); + strictEqual(getUNCHost('\\\\?\\localhost'), '?'); + + strictEqual(getUNCHost('\\\\.\\UNC\\localhost'), '.'); + strictEqual(getUNCHost('\\\\?\\UNC\\localhost'), '?'); + + strictEqual(getUNCHost('\\\\.\\UNC\\localhost\\'), 'localhost'); + strictEqual(getUNCHost('\\\\?\\UNC\\localhost\\'), 'localhost'); + + strictEqual(getUNCHost('\\\\.\\UNC\\localhost\\a'), 'localhost'); + strictEqual(getUNCHost('\\\\?\\UNC\\localhost\\a'), 'localhost'); + }); +}); diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 1f63c26f61e..7e3e2c6d2f2 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { app, BrowserWindow, dialog, protocol, session, Session, systemPreferences, WebFrameMain } from 'electron'; -import { setUNCHostAllowlist, toUNCHostAllowlist } from 'vs/base/node/unc'; +import { addUNCHostToAllowlist } from 'vs/base/node/unc'; import { validatedIpcMain } from 'vs/base/parts/ipc/electron-main/ipcMain'; import { hostname, release } from 'os'; import { VSBuffer } from 'vs/base/common/buffer'; @@ -316,7 +316,7 @@ export class CodeApplication extends Disposable { //#region UNC Host Allowlist (Windows) if (isWindows) { - setUNCHostAllowlist(toUNCHostAllowlist(this.configurationService.getValue('security.allowedUNCHosts'))); + addUNCHostToAllowlist(this.configurationService.getValue('security.allowedUNCHosts')); } //#endregion diff --git a/src/vs/server/node/remoteExtensionHostAgentCli.ts b/src/vs/server/node/remoteExtensionHostAgentCli.ts index b9105f8cda2..561637d13b9 100644 --- a/src/vs/server/node/remoteExtensionHostAgentCli.ts +++ b/src/vs/server/node/remoteExtensionHostAgentCli.ts @@ -49,7 +49,7 @@ import { ExtensionsProfileScannerService } from 'vs/platform/extensionManagement import { LogService } from 'vs/platform/log/common/logService'; import { LoggerService } from 'vs/platform/log/node/loggerService'; import { localize } from 'vs/nls'; -import { setUNCHostAllowlist, toUNCHostAllowlist } from 'vs/base/node/unc'; +import { addUNCHostToAllowlist } from 'vs/base/node/unc'; class CliMain extends Disposable { @@ -71,8 +71,8 @@ class CliMain extends Disposable { const logService = accessor.get(ILogService); // On Windows, configure the UNC allow list based on settings - if (process.platform === 'win32') { - setUNCHostAllowlist(toUNCHostAllowlist(configurationService.getValue('security.allowedUNCHosts'))); + if (isWindows) { + addUNCHostToAllowlist(configurationService.getValue('security.allowedUNCHosts')); } try { diff --git a/src/vs/server/node/remoteExtensionHostAgentServer.ts b/src/vs/server/node/remoteExtensionHostAgentServer.ts index 01c46649d69..743bbd6145a 100644 --- a/src/vs/server/node/remoteExtensionHostAgentServer.ts +++ b/src/vs/server/node/remoteExtensionHostAgentServer.ts @@ -23,7 +23,7 @@ import { createRegExp, escapeRegExpCharacters } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import { findFreePort } from 'vs/base/node/ports'; -import { setUNCHostAllowlist, toUNCHostAllowlist } from 'vs/base/node/unc'; +import { addUNCHostToAllowlist } from 'vs/base/node/unc'; import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net'; import { NodeSocket, WebSocketNodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -718,8 +718,8 @@ export async function createServer(address: string | net.AddressInfo | null, arg instantiationService.invokeFunction((accessor) => { const configurationService = accessor.get(IConfigurationService); - if (process.platform === 'win32') { - setUNCHostAllowlist(toUNCHostAllowlist(configurationService.getValue('security.allowedUNCHosts'))); + if (platform.isWindows) { + addUNCHostToAllowlist(configurationService.getValue('security.allowedUNCHosts')); } }); @@ -730,7 +730,7 @@ export async function createServer(address: string | net.AddressInfo | null, arg instantiationService.invokeFunction((accessor) => { const logService = accessor.get(ILogService); - if (process.platform === 'win32' && process.env.HOMEDRIVE && process.env.HOMEPATH) { + if (platform.isWindows && process.env.HOMEDRIVE && process.env.HOMEPATH) { const homeDirModulesPath = join(process.env.HOMEDRIVE, 'node_modules'); const userDir = dirname(join(process.env.HOMEDRIVE, process.env.HOMEPATH)); const userDirModulesPath = join(userDir, 'node_modules'); diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index d0136cd5d73..b5d00c54dcd 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -697,7 +697,7 @@ const registry = Registry.as(ConfigurationExtensions.Con }, 'default': [], 'markdownDescription': localize('security.allowedUNCHosts', 'A set of UNC host names to allow without user confirmation. If a UNC host is being accessed that is not allowed via this setting or has not been acknowledged via user confirmation, an error will occur and the operation stopped. A restart is required when changing this setting. Find out more about this setting at https://aka.ms/vscode-windows-unc.'), - 'included': isWindows, + 'included': isWeb ? true /* web maybe connected to a windows machine */ : isWindows, 'scope': ConfigurationScope.MACHINE } }