sandbox - allow to enable vscode-file protocol via argv or environment (#98682)

This commit is contained in:
Benjamin Pasero 2021-01-06 15:30:33 +01:00
parent b1659198f6
commit 169a0ec047
9 changed files with 91 additions and 56 deletions

View file

@ -27,6 +27,7 @@
const webFrame = preloadGlobals.webFrame;
const safeProcess = preloadGlobals.process;
const configuration = parseWindowConfiguration();
const useCustomProtocol = sandbox || typeof safeProcess.env['ENABLE_VSCODE_BROWSER_CODE_LOADING'] === 'string';
// Start to resolve process.env before anything gets load
// so that we can run loading and resolving in parallel
@ -77,25 +78,25 @@
window.document.documentElement.setAttribute('lang', locale);
// do not advertise AMD to avoid confusing UMD modules loaded with nodejs
if (!sandbox) {
if (!useCustomProtocol) {
window['define'] = undefined;
}
// replace the patched electron fs with the original node fs for all AMD code (TODO@sandbox non-sandboxed only)
if (!sandbox) {
require.define('fs', ['original-fs'], function (originalFS) { return originalFS; });
require.define('fs', [], function () { return require.__$__nodeRequire('original-fs'); });
}
window['MonacoEnvironment'] = {};
const baseUrl = sandbox ?
const baseUrl = useCustomProtocol ?
`${bootstrapLib.fileUriFromPath(configuration.appRoot, { isWindows: safeProcess.platform === 'win32', scheme: 'vscode-file', fallbackAuthority: 'vscode-app' })}/out` :
`${bootstrapLib.fileUriFromPath(configuration.appRoot, { isWindows: safeProcess.platform === 'win32' })}/out`;
const loaderConfig = {
baseUrl,
'vs/nls': nlsConfig,
preferScriptTags: sandbox
preferScriptTags: useCustomProtocol
};
// Enable loading of node modules:

View file

@ -236,7 +236,11 @@ function configureCommandlineSwitchesSync(cliArgs) {
const SUPPORTED_MAIN_PROCESS_SWITCHES = [
// Persistently enable proposed api via argv.json: https://github.com/microsoft/vscode/issues/99775
'enable-proposed-api'
'enable-proposed-api',
// TODO@bpasero remove me once testing is done on `vscode-file` protocol
// (all traces of `enable-browser-code-loading` and `ENABLE_VSCODE_BROWSER_CODE_LOADING`)
'enable-browser-code-loading'
];
// Read argv config
@ -267,12 +271,20 @@ function configureCommandlineSwitchesSync(cliArgs) {
// Append main process flags to process.argv
else if (SUPPORTED_MAIN_PROCESS_SWITCHES.indexOf(argvKey) !== -1) {
if (argvKey === 'enable-proposed-api') {
if (Array.isArray(argvValue)) {
argvValue.forEach(id => id && typeof id === 'string' && process.argv.push('--enable-proposed-api', id));
} else {
console.error(`Unexpected value for \`enable-proposed-api\` in argv.json. Expected array of extension ids.`);
}
switch (argvKey) {
case 'enable-proposed-api':
if (Array.isArray(argvValue)) {
argvValue.forEach(id => id && typeof id === 'string' && process.argv.push('--enable-proposed-api', id));
} else {
console.error(`Unexpected value for \`enable-proposed-api\` in argv.json. Expected array of extension ids.`);
}
break;
case 'enable-browser-code-loading':
if (typeof argvValue === 'string') {
process.env['ENABLE_VSCODE_BROWSER_CODE_LOADING'] = argvValue;
}
break;
}
}
});
@ -283,6 +295,11 @@ function configureCommandlineSwitchesSync(cliArgs) {
app.commandLine.appendSwitch('js-flags', jsFlags);
}
// Support __sandbox flag
if (cliArgs.__sandbox) {
process.env['ENABLE_VSCODE_BROWSER_CODE_LOADING'] = 'bypassHeatCheck';
}
return argvConfig;
}

View file

@ -147,8 +147,8 @@ class FileAccessImpl {
* **Note:** use `dom.ts#asCSSUrl` whenever the URL is to be used in CSS context.
*/
asBrowserUri(uri: URI): URI;
asBrowserUri(moduleId: string, moduleIdToUrl: { toUrl(moduleId: string): string }): URI;
asBrowserUri(uriOrModule: URI | string, moduleIdToUrl?: { toUrl(moduleId: string): string }): URI {
asBrowserUri(moduleId: string, moduleIdToUrl: { toUrl(moduleId: string): string }, __forceCodeFileUri?: boolean): URI;
asBrowserUri(uriOrModule: URI | string, moduleIdToUrl?: { toUrl(moduleId: string): string }, __forceCodeFileUri?: boolean): URI {
const uri = this.toUri(uriOrModule, moduleIdToUrl);
// Handle remote URIs via `RemoteAuthorities`
@ -157,37 +157,23 @@ class FileAccessImpl {
}
// Only convert the URI if we are in a native context and it has `file:` scheme
if (platform.isElectronSandboxed && platform.isNative && uri.scheme === Schemas.file) {
return this.toCodeFileUri(uri);
// and we have explicitly enabled the conversion (sandbox, or ENABLE_VSCODE_BROWSER_CODE_LOADING)
if (platform.isNative && (__forceCodeFileUri || platform.isPreferringBrowserCodeLoad) && uri.scheme === Schemas.file) {
return uri.with({
scheme: Schemas.vscodeFileResource,
// We need to provide an authority here so that it can serve
// as origin for network and loading matters in chromium.
// If the URI is not coming with an authority already, we
// add our own
authority: uri.authority || this.FALLBACK_AUTHORITY,
query: null,
fragment: null
});
}
return uri;
}
/**
* TODO@bpasero remove me eventually when vscode-file is adopted everywhere
*/
_asCodeFileUri(uri: URI): URI;
_asCodeFileUri(moduleId: string, moduleIdToUrl: { toUrl(moduleId: string): string }): URI;
_asCodeFileUri(uriOrModule: URI | string, moduleIdToUrl?: { toUrl(moduleId: string): string }): URI {
const uri = this.toUri(uriOrModule, moduleIdToUrl);
return this.toCodeFileUri(uri);
}
private toCodeFileUri(uri: URI): URI {
return uri.with({
scheme: Schemas.vscodeFileResource,
// We need to provide an authority here so that it can serve
// as origin for network and loading matters in chromium.
// If the URI is not coming with an authority already, we
// add our own
authority: uri.authority || this.FALLBACK_AUTHORITY,
query: null,
fragment: null
});
}
/**
* Returns the `file` URI to use in contexts where node.js
* is responsible for loading.

View file

@ -62,6 +62,26 @@ if (typeof process !== 'undefined') {
const isElectronRenderer = typeof nodeProcess?.versions?.electron === 'string' && nodeProcess.type === 'renderer';
export const isElectronSandboxed = isElectronRenderer && nodeProcess?.sandboxed;
export const browserCodeLoadingCacheStrategy: 'none' | 'code' | 'bypassHeatCheck' | 'bypassHeatCheckAndEagerCompile' | undefined = (() => {
// Always enabled when sandbox is enabled
if (isElectronSandboxed) {
return 'bypassHeatCheck';
}
// Otherwise, only enabled conditionally
const env = nodeProcess?.env['ENABLE_VSCODE_BROWSER_CODE_LOADING'];
if (typeof env === 'string') {
if (env === 'none' || env === 'code' || env === 'bypassHeatCheck' || env === 'bypassHeatCheckAndEagerCompile') {
return env;
}
return 'bypassHeatCheck';
}
return undefined;
})();
export const isPreferringBrowserCodeLoad = typeof browserCodeLoadingCacheStrategy === 'string';
// Web environment
if (typeof navigator === 'object' && !isElectronRenderer) {

View file

@ -7,10 +7,10 @@ import * as assert from 'assert';
import { URI } from 'vs/base/common/uri';
import { FileAccess, Schemas } from 'vs/base/common/network';
import { isEqual } from 'vs/base/common/resources';
import { isElectronSandboxed } from 'vs/base/common/platform';
import { isPreferringBrowserCodeLoad } from 'vs/base/common/platform';
suite('network', () => {
const enableTest = isElectronSandboxed;
const enableTest = isPreferringBrowserCodeLoad;
(!enableTest ? test.skip : test)('FileAccess: URI (native)', () => {

View file

@ -11,7 +11,7 @@ import { INativeEnvironmentService } from 'vs/platform/environment/common/enviro
import { session } from 'electron';
import { ILogService } from 'vs/platform/log/common/log';
import { TernarySearchTree } from 'vs/base/common/map';
import { isLinux } from 'vs/base/common/platform';
import { isLinux, isPreferringBrowserCodeLoad } from 'vs/base/common/platform';
import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows';
type ProtocolCallback = { (result: string | Electron.FilePathWithHeaders | { error: number }): void };
@ -37,15 +37,17 @@ export class FileProtocolHandler extends Disposable {
// Register vscode-file:// handler
defaultSession.protocol.registerFileProtocol(Schemas.vscodeFileResource, (request, callback) => this.handleResourceRequest(request, callback as unknown as ProtocolCallback));
// Block any file:// access (sandbox only)
if (environmentService.args.__sandbox) {
// Block any file:// access (explicitly enabled only)
if (isPreferringBrowserCodeLoad) {
this.logService.info(`Intercepting ${Schemas.file}: protocol and blocking it`);
defaultSession.protocol.interceptFileProtocol(Schemas.file, (request, callback) => this.handleFileRequest(request, callback as unknown as ProtocolCallback));
}
// Cleanup
this._register(toDisposable(() => {
defaultSession.protocol.unregisterProtocol(Schemas.vscodeFileResource);
if (environmentService.args.__sandbox) {
if (isPreferringBrowserCodeLoad) {
defaultSession.protocol.uninterceptProtocol(Schemas.file);
}
}));

View file

@ -14,6 +14,7 @@ import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainServ
import { toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { Event } from 'vs/base/common/event';
import { FileAccess } from 'vs/base/common/network';
import { browserCodeLoadingCacheStrategy } from 'vs/base/common/platform';
export class SharedProcess implements ISharedProcess {
@ -41,6 +42,7 @@ export class SharedProcess implements ISharedProcess {
backgroundColor: this.themeMainService.getBackgroundColor(),
webPreferences: {
preload: FileAccess.asFileUri('vs/base/parts/sandbox/electron-browser/preload.js', require).fsPath,
v8CacheOptions: browserCodeLoadingCacheStrategy,
nodeIntegration: true,
enableWebSQL: false,
enableRemoteModule: false,
@ -60,11 +62,11 @@ export class SharedProcess implements ISharedProcess {
windowId: this.window.id
};
const windowUrl = (this.environmentService.sandbox ?
FileAccess._asCodeFileUri('vs/code/electron-browser/sharedProcess/sharedProcess.html', require) :
FileAccess.asBrowserUri('vs/code/electron-browser/sharedProcess/sharedProcess.html', require))
.with({ query: `config=${encodeURIComponent(JSON.stringify(config))}` });
this.window.loadURL(windowUrl.toString(true));
this.window.loadURL(FileAccess
.asBrowserUri('vs/code/electron-browser/sharedProcess/sharedProcess.html', require)
.with({ query: `config=${encodeURIComponent(JSON.stringify(config))}` })
.toString(true)
);
// Prevent the window from dying
const onClose = (e: ElectronEvent) => {

View file

@ -18,7 +18,7 @@ import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
import product from 'vs/platform/product/common/product';
import { WindowMinimumSize, IWindowSettings, MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility, zoomLevelToZoomFactor, INativeWindowConfiguration } from 'vs/platform/windows/common/windows';
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform';
import { browserCodeLoadingCacheStrategy, isLinux, isMacintosh, isWindows } from 'vs/base/common/platform';
import { ICodeWindow, IWindowState, WindowMode } from 'vs/platform/windows/electron-main/windows';
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { IWorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService';
@ -164,6 +164,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
title: product.nameLong,
webPreferences: {
preload: FileAccess.asFileUri('vs/base/parts/sandbox/electron-browser/preload.js', require).fsPath,
v8CacheOptions: browserCodeLoadingCacheStrategy,
enableWebSQL: false,
enableRemoteModule: false,
spellcheck: false,
@ -185,6 +186,10 @@ export class CodeWindow extends Disposable implements ICodeWindow {
}
};
if (browserCodeLoadingCacheStrategy) {
this.logService.info(`window#ctor: using vscode-file protocol and V8 cache options: ${browserCodeLoadingCacheStrategy}`);
}
// Apply icon to window
// Linux: always
// Windows: only when running out of sources, otherwise an icon is set by us on the executable
@ -850,10 +855,10 @@ export class CodeWindow extends Disposable implements ICodeWindow {
workbench = 'vs/code/electron-browser/workbench/workbench.html';
}
return (this.environmentService.sandbox ?
FileAccess._asCodeFileUri(workbench, require) :
FileAccess.asBrowserUri(workbench, require))
.with({ query: `config=${encodeURIComponent(JSON.stringify(config))}` }).toString(true);
return FileAccess
.asBrowserUri(workbench, require)
.with({ query: `config=${encodeURIComponent(JSON.stringify(config))}` })
.toString(true);
}
serializeWindowState(): IWindowState {

View file

@ -203,6 +203,7 @@ export class IssueMainService implements ICommonIssueService {
backgroundColor: data.styles.backgroundColor || DEFAULT_BACKGROUND_COLOR,
webPreferences: {
preload: FileAccess.asFileUri('vs/base/parts/sandbox/electron-browser/preload.js', require).fsPath,
v8CacheOptions: 'bypassHeatCheck',
enableWebSQL: false,
enableRemoteModule: false,
spellcheck: false,
@ -257,6 +258,7 @@ export class IssueMainService implements ICommonIssueService {
title: localize('processExplorer', "Process Explorer"),
webPreferences: {
preload: FileAccess.asFileUri('vs/base/parts/sandbox/electron-browser/preload.js', require).fsPath,
v8CacheOptions: 'bypassHeatCheck',
enableWebSQL: false,
enableRemoteModule: false,
spellcheck: false,
@ -432,7 +434,7 @@ function toWindowUrl<T>(modulePathToHtml: string, windowConfiguration: T): strin
}
return FileAccess
._asCodeFileUri(modulePathToHtml, require)
.asBrowserUri(modulePathToHtml, require, true)
.with({ query: `config=${encodeURIComponent(JSON.stringify(config))}` })
.toString(true);
}