🚀 sandbox - enable by default and remove setting 🚀 (#181638)

* sandbox - enable by default and remove setting

* remove more

* set flags again

* fix lint

* more cleanup
This commit is contained in:
Benjamin Pasero 2023-05-12 13:36:29 +02:00 committed by GitHub
parent 424388032a
commit 8a652f9380
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 65 additions and 388 deletions

22
src/bootstrap-fork.js vendored
View file

@ -235,26 +235,16 @@ function terminateWhenParentTerminates() {
}
}
// TODO@bpasero remove this when sandbox is final
function configureCrashReporter() {
const crashReporterSandboxedHint = process.env['VSCODE_CRASH_REPORTER_SANDBOXED_HINT'];
if (crashReporterSandboxedHint) {
addCrashReporterParameter('_sandboxed', 'true');
}
const crashReporterProcessType = process.env['VSCODE_CRASH_REPORTER_PROCESS_TYPE'];
if (crashReporterProcessType) {
addCrashReporterParameter('processType', crashReporterProcessType);
}
}
function addCrashReporterParameter(key, value) {
try {
if (process['crashReporter'] && typeof process['crashReporter'].addExtraParameter === 'function' /* Electron only */) {
process['crashReporter'].addExtraParameter(key, value);
try {
if (process['crashReporter'] && typeof process['crashReporter'].addExtraParameter === 'function' /* Electron only */) {
process['crashReporter'].addExtraParameter('processType', crashReporterProcessType);
}
} catch (error) {
console.error(error);
}
} catch (error) {
console.error(error);
}
}

View file

@ -43,15 +43,6 @@
* }} [options]
*/
async function load(modulePaths, resultCallback, options) {
const isDev = !!safeProcess.env['VSCODE_DEV'];
// Error handler (node.js enabled renderers only)
let showDevtoolsOnError = isDev;
if (!safeProcess.sandboxed) {
safeProcess.on('uncaughtException', function (/** @type {string | Error} */ error) {
onUnexpectedError(error, showDevtoolsOnError);
});
}
// Await window configuration from preload
const timeout = setTimeout(() => { console.error(`[resolve window config] Could not resolve window configuration within 10 seconds, but will continue to wait...`); }, 10000);
@ -68,28 +59,21 @@
// Developer settings
const {
forceDisableShowDevtoolsOnError,
forceEnableDeveloperKeybindings,
disallowReloadKeybinding,
removeDeveloperKeybindingsAfterLoad
} = typeof options?.configureDeveloperSettings === 'function' ? options.configureDeveloperSettings(configuration) : {
forceDisableShowDevtoolsOnError: false,
forceEnableDeveloperKeybindings: false,
disallowReloadKeybinding: false,
removeDeveloperKeybindingsAfterLoad: false
};
showDevtoolsOnError = isDev && !forceDisableShowDevtoolsOnError;
const isDev = !!safeProcess.env['VSCODE_DEV'];
const enableDeveloperKeybindings = isDev || forceEnableDeveloperKeybindings;
let developerDeveloperKeybindingsDisposable;
if (enableDeveloperKeybindings) {
developerDeveloperKeybindingsDisposable = registerDeveloperKeybindings(disallowReloadKeybinding);
}
// Enable ASAR support (node.js enabled renderers only)
if (!safeProcess.sandboxed) {
globalThis.MonacoBootstrap.enableASARSupport(configuration.appRoot);
}
// Get the nls configuration into the process.env as early as possible
const nlsConfig = globalThis.MonacoBootstrap.setupNLS();
@ -102,29 +86,11 @@
window.document.documentElement.setAttribute('lang', locale);
// Define `fs` as `original-fs` to disable ASAR support
// in fs-operations (node.js enabled renderers only)
if (!safeProcess.sandboxed) {
require.define('fs', [], function () {
return require.__$__nodeRequire('original-fs');
});
}
window['MonacoEnvironment'] = {};
// VSCODE_GLOBALS: node_modules
globalThis._VSCODE_NODE_MODULES = new Proxy(Object.create(null), { get: (_target, mod) => (require.__$__nodeRequire ?? require)(String(mod)) });
if (!safeProcess.sandboxed) {
// VSCODE_GLOBALS: package/product.json
globalThis._VSCODE_PRODUCT_JSON = (require.__$__nodeRequire ?? require)(configuration.appRoot + '/product.json');
if (process.env['VSCODE_DEV']) {
// Patch product overrides when running out of sources
try { globalThis._VSCODE_PRODUCT_JSON = Object.assign(globalThis._VSCODE_PRODUCT_JSON, (require.__$__nodeRequire ?? require)(configuration.appRoot + '/product.overrides.json')); } catch (error) { /* ignore */ }
}
globalThis._VSCODE_PACKAGE_JSON = (require.__$__nodeRequire ?? require)(configuration.appRoot + '/package.json');
}
const loaderConfig = {
baseUrl: `${bootstrapLib.fileUriFromPath(configuration.appRoot, { isWindows: safeProcess.platform === 'win32', scheme: 'vscode-file', fallbackAuthority: 'vscode-app' })}/out`,
'vs/nls': nlsConfig,
@ -160,13 +126,6 @@
'tas-client-umd': `${baseNodeModulesPath}/tas-client-umd/lib/tas-client-umd.js`
};
// Allow to load built-in and other node.js modules via AMD
// which has a fallback to using node.js `require`
// (node.js enabled renderers only)
if (!safeProcess.sandboxed) {
loaderConfig.amdModulesPattern = /(^vs\/)|(^vscode-textmate$)|(^vscode-oniguruma$)|(^xterm$)|(^xterm-addon-canvas$)|(^xterm-addon-search$)|(^xterm-addon-unicode11$)|(^xterm-addon-webgl$)|(^@vscode\/iconv-lite-umd$)|(^jschardet$)|(^@vscode\/vscode-languagedetection$)|(^vscode-regexp-languagedetection$)|(^tas-client-umd$)/;
}
// Signal before require.config()
if (typeof options?.beforeLoaderConfig === 'function') {
options.beforeLoaderConfig(loaderConfig);

42
src/bootstrap.js vendored
View file

@ -50,41 +50,14 @@
//#region Add support for using node_modules.asar
/**
* @param {string=} appRoot
*/
function enableASARSupport(appRoot) {
function enableASARSupport() {
if (!path || !Module || typeof process === 'undefined') {
console.warn('enableASARSupport() is only available in node.js environments');
return;
}
const NODE_MODULES_PATH = appRoot ? path.join(appRoot, 'node_modules') : path.join(__dirname, '../node_modules');
// Windows only:
// use both lowercase and uppercase drive letter
// as a way to ensure we do the right check on
// the node modules path: node.js might internally
// use a different case compared to what we have
/** @type {string | undefined} */
let NODE_MODULES_ALTERNATIVE_PATH;
if (appRoot /* only used from renderer until `sandbox` enabled */ && process.platform === 'win32') {
const driveLetter = appRoot.substr(0, 1);
let alternativeDriveLetter;
if (driveLetter.toLowerCase() !== driveLetter) {
alternativeDriveLetter = driveLetter.toLowerCase();
} else {
alternativeDriveLetter = driveLetter.toUpperCase();
}
NODE_MODULES_ALTERNATIVE_PATH = alternativeDriveLetter + NODE_MODULES_PATH.substr(1);
} else {
NODE_MODULES_ALTERNATIVE_PATH = undefined;
}
const NODE_MODULES_PATH = path.join(__dirname, '../node_modules');
const NODE_MODULES_ASAR_PATH = `${NODE_MODULES_PATH}.asar`;
const NODE_MODULES_ASAR_ALTERNATIVE_PATH = NODE_MODULES_ALTERNATIVE_PATH ? `${NODE_MODULES_ALTERNATIVE_PATH}.asar` : undefined;
// @ts-ignore
const originalResolveLookupPaths = Module._resolveLookupPaths;
@ -93,23 +66,12 @@
Module._resolveLookupPaths = function (request, parent) {
const paths = originalResolveLookupPaths(request, parent);
if (Array.isArray(paths)) {
let asarPathAdded = false;
for (let i = 0, len = paths.length; i < len; i++) {
if (paths[i] === NODE_MODULES_PATH) {
asarPathAdded = true;
paths.splice(i, 0, NODE_MODULES_ASAR_PATH);
break;
} else if (paths[i] === NODE_MODULES_ALTERNATIVE_PATH) {
asarPathAdded = true;
paths.splice(i, 0, NODE_MODULES_ASAR_ALTERNATIVE_PATH);
break;
}
}
if (!asarPathAdded && appRoot) {
// Assuming that adding just `NODE_MODULES_ASAR_PATH` is sufficient
// because nodejs should find it even if it has a different drive letter case
paths.push(NODE_MODULES_ASAR_PATH);
}
}
return paths;

View file

@ -27,6 +27,9 @@ const { getUNCHost, addUNCHostToAllowlist } = require('./vs/base/node/unc');
const product = require('../product.json');
const { app, protocol, crashReporter, Menu } = require('electron');
// Enable sandbox globally
app.enableSandbox();
// Enable portable support
const portable = bootstrapNode.configurePortable(product);

View file

@ -8,7 +8,7 @@ import { ISandboxConfiguration } from 'vs/base/parts/sandbox/common/sandboxTypes
import { IpcRenderer, ProcessMemoryInfo, WebFrame } from 'vs/base/parts/sandbox/electron-sandbox/electronTypes';
/**
* In sandboxed renderers we cannot expose all of the `process` global of node.js
* In Electron renderers we cannot expose all of the `process` global of node.js
*/
export interface ISandboxNodeProcess extends INodeProcess {
@ -29,20 +29,6 @@ export interface ISandboxNodeProcess extends INodeProcess {
*/
readonly type: string;
/**
* Whether the process is sandboxed or not.
*/
readonly sandboxed: boolean;
/**
* The `process.pid` property returns the PID of the process.
*
* @deprecated this property will be removed once sandbox is enabled.
*
* TODO@bpasero remove this property when sandbox is on
*/
readonly pid: number;
/**
* A list of versions for the current node.js/electron configuration.
*/

View file

@ -252,11 +252,9 @@
get platform() { return process.platform; },
get arch() { return process.arch; },
get env() { return { ...process.env }; },
get pid() { return process.pid; },
get versions() { return process.versions; },
get type() { return 'renderer'; },
get execPath() { return process.execPath; },
get sandboxed() { return process.sandboxed; },
/**
* @returns {string}

View file

@ -34,7 +34,6 @@ export interface IssueReporterData {
experimentInfo?: string;
restrictedMode?: boolean;
isUnsupported?: boolean;
isSandboxed?: boolean;
}
export class IssueReporterModel {
@ -78,7 +77,6 @@ ${this.getExtensionVersion()}
VS Code version: ${this._data.versionInfo && this._data.versionInfo.vscodeVersion}
OS version: ${this._data.versionInfo && this._data.versionInfo.os}
Modes:${modes.length ? ' ' + modes.join(', ') : ''}
Sandboxed: ${this._data.isSandboxed ? 'Yes' : 'No'}
${this.getRemoteOSes()}
${this.getInfos()}
<!-- generated by issue reporter -->`;

View file

@ -34,7 +34,6 @@ undefined
VS Code version: undefined
OS version: undefined
Modes:
Sandboxed: No
Extensions: none
<!-- generated by issue reporter -->`);
@ -66,7 +65,6 @@ undefined
VS Code version: undefined
OS version: undefined
Modes:
Sandboxed: No
<details>
<summary>System Info</summary>
@ -111,7 +109,6 @@ undefined
VS Code version: undefined
OS version: undefined
Modes:
Sandboxed: No
<details>
<summary>System Info</summary>
@ -167,7 +164,6 @@ undefined
VS Code version: undefined
OS version: undefined
Modes:
Sandboxed: No
<details>
<summary>System Info</summary>
@ -225,7 +221,6 @@ undefined
VS Code version: undefined
OS version: undefined
Modes:
Sandboxed: No
Remote OS version: Linux x64 4.18.0
<details>
@ -275,7 +270,6 @@ undefined
VS Code version: undefined
OS version: undefined
Modes:
Sandboxed: No
<details>
<summary>System Info</summary>
@ -307,7 +301,6 @@ undefined
VS Code version: undefined
OS version: undefined
Modes: Restricted, Unsupported
Sandboxed: No
Extensions: none
<!-- generated by issue reporter -->`);

View file

@ -64,7 +64,6 @@ export interface IssueReporterData extends WindowData {
experiments?: string;
restrictedMode: boolean;
isUnsupported: boolean;
isSandboxed: boolean; // TODO@bpasero remove me once sandbox is final
githubAccessToken: string;
readonly issueTitle?: string;
readonly issueBody?: string;

View file

@ -438,8 +438,7 @@ export class IssueMainService implements IIssueMainService {
enableWebSQL: false,
spellcheck: false,
zoomFactor: zoomLevelToZoomFactor(options.zoomLevel),
sandbox: true,
contextIsolation: true
sandbox: true
},
alwaysOnTop: options.alwaysOnTop,
experimentalDarkMode: true

View file

@ -163,7 +163,6 @@ export interface ICommonNativeHostService {
openDevTools(options?: OpenDevToolsOptions): Promise<void>;
toggleDevTools(): Promise<void>;
sendInputEvent(event: MouseInputEvent): Promise<void>;
enableSandbox(enabled: boolean): Promise<void>; // TODO@bpasero remove me
// Perf Introspection
profileRenderer(session: string, duration: number): Promise<IV8Profile>;

View file

@ -41,7 +41,6 @@ import { VSBuffer } from 'vs/base/common/buffer';
import { hasWSLFeatureInstalled } from 'vs/platform/remote/node/wsl';
import { WindowProfiler } from 'vs/platform/profiling/electron-main/windowProfiling';
import { IV8Profile } from 'vs/platform/profiling/common/profiling';
import { IStateService } from 'vs/platform/state/node/state';
export interface INativeHostMainService extends AddFirstParameterToFunctions<ICommonNativeHostService, Promise<unknown> /* only methods, not events */, number | undefined /* window ID */> { }
@ -59,7 +58,6 @@ export class NativeHostMainService extends Disposable implements INativeHostMain
@ILogService private readonly logService: ILogService,
@IProductService private readonly productService: IProductService,
@IThemeMainService private readonly themeMainService: IThemeMainService,
@IStateService private readonly stateService: IStateService,
@IWorkspacesManagementMainService private readonly workspacesManagementMainService: IWorkspacesManagementMainService
) {
super();
@ -778,14 +776,6 @@ export class NativeHostMainService extends Disposable implements INativeHostMain
}
}
async enableSandbox(windowId: number | undefined, enabled: boolean): Promise<void> {
if (enabled) {
this.stateService.setItem('window.experimental.useSandbox', true);
} else {
this.stateService.removeItem('window.experimental.useSandbox');
}
}
//#endregion
// #region Performance

View file

@ -206,7 +206,7 @@ export class UtilityProcess extends Disposable {
}
start(configuration: IUtilityProcessConfiguration): boolean {
const started = this.doStart(configuration, false);
const started = this.doStart(configuration);
if (started && configuration.payload) {
this.postMessage(configuration.payload);
@ -215,7 +215,7 @@ export class UtilityProcess extends Disposable {
return started;
}
protected doStart(configuration: IUtilityProcessConfiguration, isWindowSandboxed: boolean): boolean {
protected doStart(configuration: IUtilityProcessConfiguration): boolean {
if (!this.validateCanStart()) {
return false;
}
@ -229,7 +229,7 @@ export class UtilityProcess extends Disposable {
const allowLoadingUnsignedLibraries = this.configuration.allowLoadingUnsignedLibraries;
const forceAllocationsToV8Sandbox = this.configuration.forceAllocationsToV8Sandbox;
const stdio = 'pipe';
const env = this.createEnv(configuration, isWindowSandboxed);
const env = this.createEnv(configuration);
this.log('creating new...', Severity.Info);
@ -244,12 +244,12 @@ export class UtilityProcess extends Disposable {
} as ForkOptions & { forceAllocationsToV8Sandbox?: Boolean });
// Register to events
this.registerListeners(this.process, this.configuration, serviceName, isWindowSandboxed);
this.registerListeners(this.process, this.configuration, serviceName);
return true;
}
private createEnv(configuration: IUtilityProcessConfiguration, isWindowSandboxed: boolean): { [key: string]: any } {
private createEnv(configuration: IUtilityProcessConfiguration): { [key: string]: any } {
const env: { [key: string]: any } = configuration.env ? { ...configuration.env } : { ...deepClone(process.env) };
// Apply supported environment variables from config
@ -257,9 +257,6 @@ export class UtilityProcess extends Disposable {
if (typeof configuration.parentLifecycleBound === 'number') {
env['VSCODE_PARENT_PID'] = String(configuration.parentLifecycleBound);
}
if (isWindowSandboxed) {
env['VSCODE_CRASH_REPORTER_SANDBOXED_HINT'] = '1'; // TODO@bpasero remove me once sandbox is final
}
env['VSCODE_CRASH_REPORTER_PROCESS_TYPE'] = configuration.type;
if (isWindows) {
env['NODE_UNC_HOST_ALLOWLIST'] = getUNCHostAllowlist().join('\\');
@ -276,7 +273,7 @@ export class UtilityProcess extends Disposable {
return env;
}
private registerListeners(process: ElectronUtilityProcess, configuration: IUtilityProcessConfiguration, serviceName: string, isWindowSandboxed: boolean): void {
private registerListeners(process: ElectronUtilityProcess, configuration: IUtilityProcessConfiguration, serviceName: string): void {
// Stdout
if (process.stdout) {
@ -324,7 +321,6 @@ export class UtilityProcess extends Disposable {
type UtilityProcessCrashClassification = {
type: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The type of utility process to understand the origin of the crash better.' };
reason: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The reason of the utility process crash to understand the nature of the crash better.' };
sandboxed: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'If the window for the utility process was sandboxed or not.' };
code: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'The exit code of the utility process to understand the nature of the crash better' };
owner: 'bpasero';
comment: 'Provides insight into reasons the utility process crashed.';
@ -333,13 +329,11 @@ export class UtilityProcess extends Disposable {
type: string;
reason: string;
code: number;
sandboxed: string;
};
this.telemetryService.publicLog2<UtilityProcessCrashEvent, UtilityProcessCrashClassification>('utilityprocesscrash', {
type: configuration.type,
reason: details.reason,
code: details.exitCode,
sandboxed: isWindowSandboxed ? '1' : '0' // TODO@bpasero remove this once sandbox is enabled by default
code: details.exitCode
});
// Event
@ -457,7 +451,7 @@ export class WindowUtilityProcess extends UtilityProcess {
}
// Start utility process
const started = super.doStart(configuration, responseWindow.isSandboxed);
const started = super.doStart(configuration);
if (!started) {
return false;
}

View file

@ -137,7 +137,6 @@ export interface IWindowSettings {
readonly enableMenuBarMnemonics: boolean;
readonly closeWhenEmpty: boolean;
readonly clickThroughInactive: boolean;
readonly experimental?: { useSandbox: boolean };
}
export function getTitleBarStyle(configurationService: IConfigurationService): 'native' | 'custom' {

View file

@ -44,8 +44,6 @@ export interface ICodeWindow extends IDisposable {
ready(): Promise<ICodeWindow>;
setReady(): void;
readonly isSandboxed: boolean;
addTabbedWindow(window: ICodeWindow): void;
load(config: INativeWindowConfiguration, options?: { isReload?: boolean }): void;

View file

@ -42,13 +42,6 @@ import { IPolicyService } from 'vs/platform/policy/common/policy';
import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile';
import { IStateService } from 'vs/platform/state/node/state';
import { IUserDataProfilesMainService } from 'vs/platform/userDataProfile/electron-main/userDataProfile';
import { INativeHostMainService } from 'vs/platform/native/electron-main/nativeHostMainService';
import { OneDataSystemAppender } from 'vs/platform/telemetry/node/1dsAppender';
import { ITelemetryServiceConfig, TelemetryService } from 'vs/platform/telemetry/common/telemetryService';
import { getPiiPathsFromEnvironment, isInternalTelemetry, ITelemetryAppender, supportsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils';
import { resolveCommonProperties } from 'vs/platform/telemetry/common/commonProperties';
import { hostname, release } from 'os';
import { resolveMachineId } from 'vs/platform/telemetry/electron-main/telemetryUtils';
import { ILoggerMainService } from 'vs/platform/log/electron-main/loggerService';
import { firstOrDefault } from 'vs/base/common/arrays';
@ -93,8 +86,6 @@ export class CodeWindow extends Disposable implements ICodeWindow {
private static readonly windowControlHeightStateStorageKey = 'windowControlHeight';
private static sandboxState: boolean | undefined = undefined;
//#region Events
private readonly _onWillLoad = this._register(new Emitter<ILoadEvent>());
@ -126,9 +117,6 @@ export class CodeWindow extends Disposable implements ICodeWindow {
private _lastFocusTime = -1;
get lastFocusTime(): number { return this._lastFocusTime; }
private _isSandboxed = false;
get isSandboxed(): boolean { return this._isSandboxed; }
get backupPath(): string | undefined { return this._config?.backupPath; }
get openedWorkspace(): IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined { return this._config?.workspace; }
@ -204,13 +192,11 @@ export class CodeWindow extends Disposable implements ICodeWindow {
@IProductService private readonly productService: IProductService,
@IProtocolMainService private readonly protocolMainService: IProtocolMainService,
@IWindowsMainService private readonly windowsMainService: IWindowsMainService,
@IStateService private readonly stateService: IStateService,
@INativeHostMainService private readonly nativeHostMainService: INativeHostMainService
@IStateService private readonly stateService: IStateService
) {
super();
//#region create browser window
let useSandbox = false;
{
// Load window state
const [state, hasMultipleDisplays] = this.restoreWindowState(config.state);
@ -221,24 +207,8 @@ export class CodeWindow extends Disposable implements ICodeWindow {
// after the call to maximize/fullscreen (see below)
const isFullscreenOrMaximized = (this.windowState.mode === WindowMode.Maximized || this.windowState.mode === WindowMode.Fullscreen);
if (typeof CodeWindow.sandboxState === 'undefined') {
// we should only check this once so that we do not end up
// with some windows in sandbox mode and some not!
CodeWindow.sandboxState = this.stateService.getItem<boolean>('window.experimental.useSandbox', false);
}
const windowSettings = this.configurationService.getValue<IWindowSettings | undefined>('window');
if (typeof windowSettings?.experimental?.useSandbox === 'boolean') {
useSandbox = windowSettings.experimental.useSandbox;
} else if (this.productService.quality === 'stable' && CodeWindow.sandboxState) {
useSandbox = true;
} else {
useSandbox = this.productService.quality !== 'stable';
}
this._isSandboxed = useSandbox;
const options: BrowserWindowConstructorOptions & { experimentalDarkMode: boolean } = {
width: this.windowState.width,
height: this.windowState.height,
@ -260,18 +230,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
// Enable experimental css highlight api https://chromestatus.com/feature/5436441440026624
// Refs https://github.com/microsoft/vscode/issues/140098
enableBlinkFeatures: 'HighlightAPI',
...useSandbox ?
// Sandbox
{
sandbox: true
} :
// No Sandbox
{
nodeIntegration: true,
contextIsolation: false
}
sandbox: true
},
experimentalDarkMode: true
};
@ -447,7 +406,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
this.createTouchBar();
// Eventing
this.registerListeners(useSandbox);
this.registerListeners();
}
setRepresentedFilename(filename: string): void {
@ -551,12 +510,12 @@ export class CodeWindow extends Disposable implements ICodeWindow {
});
}
private registerListeners(sandboxed: boolean): void {
private registerListeners(): void {
// Window error conditions to handle
this._win.on('unresponsive', () => this.onWindowError(WindowError.UNRESPONSIVE, { sandboxed }));
this._win.webContents.on('render-process-gone', (event, details) => this.onWindowError(WindowError.PROCESS_GONE, { ...details, sandboxed }));
this._win.webContents.on('did-fail-load', (event, exitCode, reason) => this.onWindowError(WindowError.LOAD, { reason, exitCode, sandboxed }));
this._win.on('unresponsive', () => this.onWindowError(WindowError.UNRESPONSIVE));
this._win.webContents.on('render-process-gone', (event, details) => this.onWindowError(WindowError.PROCESS_GONE, { ...details }));
this._win.webContents.on('did-fail-load', (event, exitCode, reason) => this.onWindowError(WindowError.LOAD, { reason, exitCode }));
// Prevent windows/iframes from blocking the unload
// through DOM events. We have our own logic for
@ -653,10 +612,10 @@ export class CodeWindow extends Disposable implements ICodeWindow {
return this.marketplaceHeadersPromise;
}
private async onWindowError(error: WindowError.UNRESPONSIVE, details: { sandboxed: boolean }): Promise<void>;
private async onWindowError(error: WindowError.PROCESS_GONE, details: { reason: string; exitCode: number; sandboxed: boolean }): Promise<void>;
private async onWindowError(error: WindowError.LOAD, details: { reason: string; exitCode: number; sandboxed: boolean }): Promise<void>;
private async onWindowError(type: WindowError, details: { reason?: string; exitCode?: number; sandboxed: boolean }): Promise<void> {
private async onWindowError(error: WindowError.UNRESPONSIVE): Promise<void>;
private async onWindowError(error: WindowError.PROCESS_GONE, details: { reason: string; exitCode: number }): Promise<void>;
private async onWindowError(error: WindowError.LOAD, details: { reason: string; exitCode: number }): Promise<void>;
private async onWindowError(type: WindowError, details?: { reason?: string; exitCode?: number }): Promise<void> {
switch (type) {
case WindowError.PROCESS_GONE:
@ -674,7 +633,6 @@ export class CodeWindow extends Disposable implements ICodeWindow {
type WindowErrorClassification = {
type: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'The type of window error to understand the nature of the error better.' };
reason: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The reason of the window error to understand the nature of the error better.' };
sandboxed: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'If the window was sandboxed or not.' };
code: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'The exit code of the window process to understand the nature of the error better' };
owner: 'bpasero';
comment: 'Provides insight into reasons the vscode window had an error.';
@ -683,13 +641,11 @@ export class CodeWindow extends Disposable implements ICodeWindow {
type: WindowError;
reason: string | undefined;
code: number | undefined;
sandboxed: string;
};
this.telemetryService.publicLog2<WindowErrorEvent, WindowErrorClassification>('windowerror', {
type,
reason: details?.reason,
code: details?.exitCode,
sandboxed: details?.sandboxed ? '1' : '0'
code: details?.exitCode
});
// Inform User if non-recoverable
@ -748,103 +704,35 @@ export class CodeWindow extends Disposable implements ICodeWindow {
// Process gone
else if (type === WindowError.PROCESS_GONE) {
// Windows: running as admin with AppLocker enabled is unsupported
// when sandbox: true.
// we cannot detect AppLocker use currently, but make a
// guess based on the reason and exit code.
if (isWindows && details?.reason === 'launch-failed' && details.exitCode === 18 && await this.nativeHostMainService.isAdmin(undefined)) {
await this.handleWindowsAdminCrash(details);
let message: string;
if (!details) {
message = localize('appGone', "The window terminated unexpectedly");
} else {
message = localize('appGoneDetails', "The window terminated unexpectedly (reason: '{0}', code: '{1}')", details.reason, details.exitCode ?? '<unknown>');
}
// Any other crash: offer to restart
else {
let message: string;
if (!details) {
message = localize('appGone', "The window terminated unexpectedly");
} else {
message = localize('appGoneDetails', "The window terminated unexpectedly (reason: '{0}', code: '{1}')", details.reason, details.exitCode ?? '<unknown>');
}
// Show Dialog
const { response, checkboxChecked } = await this.dialogMainService.showMessageBox({
type: 'warning',
buttons: [
this._config?.workspace ? localize({ key: 'reopen', comment: ['&& denotes a mnemonic'] }, "&&Reopen") : localize({ key: 'newWindow', comment: ['&& denotes a mnemonic'] }, "&&New Window"),
localize({ key: 'close', comment: ['&& denotes a mnemonic'] }, "&&Close")
],
message,
detail: this._config?.workspace ?
localize('appGoneDetailWorkspace', "We are sorry for the inconvenience. You can reopen the window to continue where you left off.") :
localize('appGoneDetailEmptyWindow', "We are sorry for the inconvenience. You can open a new empty window to start again."),
checkboxLabel: this._config?.workspace ? localize('doNotRestoreEditors', "Don't restore editors") : undefined
}, this._win);
// Show Dialog
const { response, checkboxChecked } = await this.dialogMainService.showMessageBox({
type: 'warning',
buttons: [
this._config?.workspace ? localize({ key: 'reopen', comment: ['&& denotes a mnemonic'] }, "&&Reopen") : localize({ key: 'newWindow', comment: ['&& denotes a mnemonic'] }, "&&New Window"),
localize({ key: 'close', comment: ['&& denotes a mnemonic'] }, "&&Close")
],
message,
detail: this._config?.workspace ?
localize('appGoneDetailWorkspace', "We are sorry for the inconvenience. You can reopen the window to continue where you left off.") :
localize('appGoneDetailEmptyWindow', "We are sorry for the inconvenience. You can open a new empty window to start again."),
checkboxLabel: this._config?.workspace ? localize('doNotRestoreEditors', "Don't restore editors") : undefined
}, this._win);
// Handle choice
const reopen = response === 0;
await this.destroyWindow(reopen, checkboxChecked);
}
// Handle choice
const reopen = response === 0;
await this.destroyWindow(reopen, checkboxChecked);
}
break;
}
}
private async handleWindowsAdminCrash(details: { reason?: string; exitCode?: number; sandboxed: boolean }) {
// Prepare telemetry event (TODO@bpasero remove me eventually)
const appenders: ITelemetryAppender[] = [];
const isInternal = isInternalTelemetry(this.productService, this.configurationService);
if (supportsTelemetry(this.productService, this.environmentMainService)) {
if (this.productService.aiConfig && this.productService.aiConfig.ariaKey) {
appenders.push(new OneDataSystemAppender(isInternal, 'monacoworkbench', null, this.productService.aiConfig.ariaKey));
}
const machineId = await resolveMachineId(this.stateService, this.logService);
const config: ITelemetryServiceConfig = {
appenders,
sendErrorTelemetry: false,
commonProperties: resolveCommonProperties(release(), hostname(), process.arch, this.productService.commit, this.productService.version, machineId, isInternal),
piiPaths: getPiiPathsFromEnvironment(this.environmentMainService)
};
const telemetryService = new TelemetryService(config, this.configurationService, this.productService);
type WindowAdminErrorClassification = {
reason: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The reason of the window error to understand the nature of the error better.' };
code: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'The exit code of the window process to understand the nature of the error better' };
owner: 'bpasero';
comment: 'Provides insight into reasons the vscode window had an error when running as admin.';
};
type WindowAdminErrorEvent = {
reason: string | undefined;
code: number | undefined;
};
await telemetryService.publicLog2<WindowAdminErrorEvent, WindowAdminErrorClassification>('windowadminerror', { reason: details.reason, code: details.exitCode });
}
// Inform user
const { response } = await this.dialogMainService.showMessageBox({
type: 'error',
buttons: [
localize({ key: 'learnMore', comment: ['&& denotes a mnemonic'] }, "&&Learn More"),
localize({ key: 'close', comment: ['&& denotes a mnemonic'] }, "&&Close")
],
message: localize('appGoneAdminMessage', "Running as administrator is not supported in your environment"),
detail: localize('appGoneAdminDetail', "We are sorry for the inconvenience. Please try again without administrator privileges.", this.productService.nameLong)
}, this._win);
if (response === 0) {
await this.nativeHostMainService.openExternal(undefined, 'https://go.microsoft.com/fwlink/?linkid=2220179');
}
// Ensure to await flush telemetry
await Promise.all(appenders.map(appender => appender.flush()));
// Exit
await this.destroyWindow(false, false);
}
private async destroyWindow(reopen: boolean, skipRestoreEditors: boolean): Promise<void> {
const workspace = this._config?.workspace;

View file

@ -49,7 +49,6 @@ suite('WindowsFinder', () => {
lastFocusTime = options.lastFocusTime;
isFullScreen = false;
isReady = true;
isSandboxed = false;
ready(): Promise<ICodeWindow> { throw new Error('Method not implemented.'); }
setReady(): void { throw new Error('Method not implemented.'); }

View file

@ -26,7 +26,7 @@ interface IConfiguration extends IWindowsConfiguration {
debug?: { console?: { wordWrap?: boolean } };
editor?: { accessibilitySupport?: 'on' | 'off' | 'auto' };
security?: { workspace?: { trust?: { enabled?: boolean } } };
window: IWindowSettings & { experimental?: { windowControlsOverlay?: { enabled?: boolean }; useSandbox?: boolean } };
window: IWindowSettings & { experimental?: { windowControlsOverlay?: { enabled?: boolean } } };
workbench?: { enableExperiments?: boolean };
_extensionsGallery?: { enablePPE?: boolean };
}
@ -36,7 +36,6 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo
private static SETTINGS = [
'window.titleBarStyle',
'window.experimental.windowControlsOverlay.enabled',
'window.experimental.useSandbox',
'window.nativeTabs',
'window.nativeFullScreen',
'window.clickThroughInactive',
@ -49,7 +48,6 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo
private readonly titleBarStyle = new ChangeObserver<'native' | 'custom'>('string');
private readonly windowControlsOverlayEnabled = new ChangeObserver('boolean');
private readonly windowSandboxEnabled = new ChangeObserver('boolean');
private readonly nativeTabs = new ChangeObserver('boolean');
private readonly nativeFullScreen = new ChangeObserver('boolean');
private readonly clickThroughInactive = new ChangeObserver('boolean');
@ -92,9 +90,6 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo
// Windows: Window Controls Overlay
processChanged(isWindows && this.windowControlsOverlayEnabled.handleChange(config.window?.experimental?.windowControlsOverlay?.enabled));
// Windows: Sandbox
processChanged(this.windowSandboxEnabled.handleChange(config.window?.experimental?.useSandbox));
// macOS: Native tabs
processChanged(isMacintosh && this.nativeTabs.handleChange(config.window?.nativeTabs));

View file

@ -1,53 +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 { Disposable } from 'vs/base/common/lifecycle';
import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { Registry } from 'vs/platform/registry/common/platform';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { INativeHostService } from 'vs/platform/native/common/native';
import { IProductService } from 'vs/platform/product/common/productService';
import { IWindowsConfiguration, IWindowSettings } from 'vs/platform/window/common/window';
interface IConfiguration extends IWindowsConfiguration {
window: IWindowSettings & { experimental?: { useSandbox?: boolean } };
}
export class SandboxExperimentHandler extends Disposable implements IWorkbenchContribution {
private useSandbox: boolean | undefined;
constructor(
@INativeHostService private readonly nativeHostService: INativeHostService,
@IProductService productService: IProductService,
@IConfigurationService configurationService: IConfigurationService
) {
super();
if (productService.quality !== 'stable') {
return; // experiment only applies to stable
}
this.onConfigurationChange(configurationService.getValue<IConfiguration>());
this._register(configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('window.experimental.useSandbox')) {
this.onConfigurationChange(configurationService.getValue<IConfiguration>());
}
}));
}
private onConfigurationChange(configuration: IConfiguration): void {
const useSandbox = configuration.window.experimental?.useSandbox;
if (typeof useSandbox === 'boolean' && useSandbox !== this.useSandbox) {
this.useSandbox = useSandbox;
this.nativeHostService.enableSandbox(Boolean(useSandbox));
}
}
}
const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
workbenchRegistry.registerWorkbenchContribution(SandboxExperimentHandler, LifecyclePhase.Restored);

View file

@ -26,7 +26,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { ShutdownReason } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { NativeWindow } from 'vs/workbench/electron-sandbox/window';
import { ModifierKeyEmitter } from 'vs/base/browser/dom';
import product from 'vs/platform/product/common/product';
import { applicationConfigurationNodeBase } from 'vs/workbench/common/configuration';
// Actions
@ -257,14 +256,6 @@ import { applicationConfigurationNodeBase } from 'vs/workbench/common/configurat
'scope': ConfigurationScope.APPLICATION,
'description': localize('window.clickThroughInactive', "If enabled, clicking on an inactive window will both activate the window and trigger the element under the mouse if it is clickable. If disabled, clicking anywhere on an inactive window will activate it only and a second click is required on the element."),
'included': isMacintosh
},
'window.experimental.useSandbox': { // TODO@bpasero remove me once sandbox is final
type: 'boolean',
description: localize('experimentalUseSandbox', "Experimental: When enabled, the window will have sandbox mode enabled via Electron API."),
default: product.quality !== 'stable', // disabled by default in stable for now
tags: product.quality === 'stable' ? ['experimental'] : undefined,
'scope': ConfigurationScope.APPLICATION,
ignoreSync: true
}
}
});

View file

@ -78,7 +78,7 @@ export class NativeDialogHandler extends AbstractDialogHandler {
const detailString = (useAgo: boolean): string => {
return localize({ key: 'aboutDetail', comment: ['Electron, Chromium, Node.js and V8 are product names that need no translation'] },
"Version: {0}\nCommit: {1}\nDate: {2}\nElectron: {3}\nChromium: {4}\nNode.js: {5}\nV8: {6}\nOS: {7}\nSandboxed: {8}",
"Version: {0}\nCommit: {1}\nDate: {2}\nElectron: {3}\nChromium: {4}\nNode.js: {5}\nV8: {6}\nOS: {7}",
version,
this.productService.commit || 'Unknown',
this.productService.date ? `${this.productService.date}${useAgo ? ' (' + fromNow(new Date(this.productService.date), true) + ')' : ''}` : 'Unknown',
@ -86,8 +86,7 @@ export class NativeDialogHandler extends AbstractDialogHandler {
process.versions['chrome'],
process.versions['node'],
process.versions['v8'],
`${osProps.type} ${osProps.arch} ${osProps.release}${isLinuxSnap ? ' snap' : ''}`,
process.sandboxed ? 'Yes' : 'No' // TODO@bpasero remove me once sandbox is final
`${osProps.type} ${osProps.arch} ${osProps.release}${isLinuxSnap ? ' snap' : ''}`
);
};

View file

@ -18,7 +18,6 @@ import { generateUuid } from 'vs/base/common/uuid';
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc';
import { BufferedEmitter } from 'vs/base/parts/ipc/common/ipc.net';
import { acquirePort } from 'vs/base/parts/ipc/electron-sandbox/ipc.mp';
import { process } from 'vs/base/parts/sandbox/electron-sandbox/globals';
import * as nls from 'vs/nls';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
@ -420,7 +419,7 @@ export class NativeLocalProcessExtensionHost implements IExtensionHost {
return {
commit: this._productService.commit,
version: this._productService.version,
parentPid: process.sandboxed ? 0 : process.pid,
parentPid: 0,
environment: {
isExtensionDevelopmentDebug: this._isExtensionDevDebug,
appRoot: this._environmentService.appRoot ? URI.file(this._environmentService.appRoot) : undefined,

View file

@ -19,7 +19,7 @@ import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/co
import { IAuthenticationService } from 'vs/workbench/services/authentication/common/authentication';
import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust';
import { IIntegrityService } from 'vs/workbench/services/integrity/common/integrity';
import { ipcRenderer, process } from 'vs/base/parts/sandbox/electron-sandbox/globals';
import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals';
import { IDisposable } from 'vs/base/common/lifecycle';
import { CancellationToken } from 'vs/base/common/cancellation';
import { URI } from 'vs/base/common/uri';
@ -111,8 +111,7 @@ export class NativeIssueService implements IWorkbenchIssueService {
experiments: experiments?.join('\n'),
restrictedMode: !this.workspaceTrustManagementService.isWorkspaceTrusted(),
isUnsupported,
githubAccessToken,
isSandboxed: process.sandboxed
githubAccessToken
}, dataOverrides);
return this.issueMainService.openReporter(issueReporterData);
}

View file

@ -18,7 +18,6 @@ export function resolveWorkbenchCommonProperties(
machineId: string,
isInternalTelemetry: boolean,
process: INodeProcess,
sandboxed: boolean,
remoteAuthority?: string
): ICommonProperties {
const result = resolveCommonProperties(release, hostname, process.arch, commit, version, machineId, isInternalTelemetry);
@ -37,8 +36,6 @@ export function resolveWorkbenchCommonProperties(
result['common.isNewSession'] = !lastSessionDate ? '1' : '0';
// __GDPR__COMMON__ "common.remoteAuthority" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }
result['common.remoteAuthority'] = cleanRemoteAuthority(remoteAuthority);
// __GDPR__COMMON__ "common.sandboxed" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
result['common.sandboxed'] = sandboxed ? '1' : '0'; // TODO@bpasero remove this property when sandbox is on
// __GDPR__COMMON__ "common.cli" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
result['common.cli'] = !!process.env['VSCODE_CLI'];

View file

@ -44,7 +44,7 @@ export class TelemetryService extends Disposable implements ITelemetryService {
const channel = sharedProcessService.getChannel('telemetryAppender');
const config: ITelemetryServiceConfig = {
appenders: [new TelemetryAppenderClient(channel)],
commonProperties: resolveWorkbenchCommonProperties(storageService, environmentService.os.release, environmentService.os.hostname, productService.commit, productService.version, environmentService.machineId, isInternal, process, process.sandboxed, environmentService.remoteAuthority),
commonProperties: resolveWorkbenchCommonProperties(storageService, environmentService.os.release, environmentService.os.hostname, productService.commit, productService.version, environmentService.machineId, isInternal, process, environmentService.remoteAuthority),
piiPaths: getPiiPathsFromEnvironment(environmentService),
sendErrorTelemetry: true
};

View file

@ -19,7 +19,7 @@ suite('Telemetry - common properties', function () {
});
test('default', function () {
const props = resolveWorkbenchCommonProperties(testStorageService, release(), hostname(), commit, version, 'someMachineId', false, process, process.sandboxed);
const props = resolveWorkbenchCommonProperties(testStorageService, release(), hostname(), commit, version, 'someMachineId', false, process);
assert.ok('commitHash' in props);
assert.ok('sessionID' in props);
assert.ok('timestamp' in props);
@ -43,14 +43,14 @@ suite('Telemetry - common properties', function () {
testStorageService.store('telemetry.lastSessionDate', new Date().toUTCString(), StorageScope.APPLICATION, StorageTarget.MACHINE);
const props = resolveWorkbenchCommonProperties(testStorageService, release(), hostname(), commit, version, 'someMachineId', false, process, process.sandboxed);
const props = resolveWorkbenchCommonProperties(testStorageService, release(), hostname(), commit, version, 'someMachineId', false, process);
assert.ok('common.lastSessionDate' in props); // conditional, see below
assert.ok('common.isNewSession' in props);
assert.strictEqual(props['common.isNewSession'], '0');
});
test('values chance on ask', async function () {
const props = resolveWorkbenchCommonProperties(testStorageService, release(), hostname(), commit, version, 'someMachineId', false, process, process.sandboxed);
const props = resolveWorkbenchCommonProperties(testStorageService, release(), hostname(), commit, version, 'someMachineId', false, process);
let value1 = props['common.sequence'];
let value2 = props['common.sequence'];
assert.ok(value1 !== value2, 'seq');

View file

@ -146,7 +146,6 @@ export class TestNativeHostService implements INativeHostService {
async sendInputEvent(event: any): Promise<void> { }
async windowsGetStringRegKey(hive: 'HKEY_CURRENT_USER' | 'HKEY_LOCAL_MACHINE' | 'HKEY_CLASSES_ROOT' | 'HKEY_USERS' | 'HKEY_CURRENT_CONFIG', path: string, name: string): Promise<string | undefined> { return undefined; }
async profileRenderer(): Promise<any> { throw new Error(); }
async enableSandbox(enabled: boolean): Promise<void> { throw new Error('Method not implemented.'); }
}
export class TestExtensionTipsService extends AbstractNativeExtensionTipsService {

View file

@ -168,9 +168,6 @@ import 'vs/workbench/contrib/mergeEditor/electron-sandbox/mergeEditor.contributi
// Remote Tunnel
import 'vs/workbench/contrib/remoteTunnel/electron-sandbox/remoteTunnel.contribution';
// Sandbox
import 'vs/workbench/contrib/sandbox/electron-sandbox/sandbox.contribution';
//#endregion