From be376cfef0200eec49dbc9543918b308e0ec3f50 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 12 Apr 2022 07:46:17 +0200 Subject: [PATCH] debt - improve ipc validation code --- build/lib/layersChecker.js | 3 + build/lib/layersChecker.ts | 5 +- .../contextmenu/electron-main/contextmenu.ts | 5 +- .../parts/ipc/electron-main/ipc.electron.ts | 7 +- src/vs/base/parts/ipc/electron-main/ipc.mp.ts | 5 +- .../base/parts/ipc/electron-main/ipcMain.ts | 150 ++++++++++++++++++ src/vs/code/electron-main/app.ts | 15 +- .../electron-main/diagnosticsMainService.ts | 5 +- .../issue/electron-main/issueMainService.ts | 23 +-- .../electron-main/lifecycleMainService.ts | 9 +- .../electron-main/protocolMainService.ts | 7 +- .../electron-main/sharedProcess.ts | 11 +- 12 files changed, 205 insertions(+), 40 deletions(-) create mode 100644 src/vs/base/parts/ipc/electron-main/ipcMain.ts diff --git a/build/lib/layersChecker.js b/build/lib/layersChecker.js index 8caccef815e..c83d6fcf984 100644 --- a/build/lib/layersChecker.js +++ b/build/lib/layersChecker.js @@ -191,6 +191,9 @@ const RULES = [ 'Event', 'Request' ], + disallowedTypes: [ + 'ipcMain' // not allowed, use validatedIpcMain instead + ], disallowedDefinitions: [ 'lib.dom.d.ts' // no DOM ] diff --git a/build/lib/layersChecker.ts b/build/lib/layersChecker.ts index af9ccd0ae92..95810c3b5f4 100644 --- a/build/lib/layersChecker.ts +++ b/build/lib/layersChecker.ts @@ -69,7 +69,7 @@ const NATIVE_TYPES = [ 'ICommonNativeHostService' ]; -const RULES = [ +const RULES: IRule[] = [ // Tests: skip { @@ -210,6 +210,9 @@ const RULES = [ 'Event', 'Request' ], + disallowedTypes: [ + 'ipcMain' // not allowed, use validatedIpcMain instead + ], disallowedDefinitions: [ 'lib.dom.d.ts' // no DOM ] diff --git a/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts b/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts index fa64089c460..6b2eb1cb820 100644 --- a/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts +++ b/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts @@ -3,12 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { BrowserWindow, ipcMain, IpcMainEvent, Menu, MenuItem } from 'electron'; +import { BrowserWindow, IpcMainEvent, Menu, MenuItem } from 'electron'; +import { validatedIpcMain } from 'vs/base/parts/ipc/electron-main/ipcMain'; import { withNullAsUndefined } from 'vs/base/common/types'; import { CONTEXT_MENU_CHANNEL, CONTEXT_MENU_CLOSE_CHANNEL, IPopupOptions, ISerializableContextMenuItem } from 'vs/base/parts/contextmenu/common/contextmenu'; export function registerContextMenuListener(): void { - ipcMain.on(CONTEXT_MENU_CHANNEL, (event: IpcMainEvent, contextMenuId: number, items: ISerializableContextMenuItem[], onClickChannel: string, options?: IPopupOptions) => { + validatedIpcMain.on(CONTEXT_MENU_CHANNEL, (event: IpcMainEvent, contextMenuId: number, items: ISerializableContextMenuItem[], onClickChannel: string, options?: IPopupOptions) => { const menu = createMenu(event, onClickChannel, items); menu.popup({ diff --git a/src/vs/base/parts/ipc/electron-main/ipc.electron.ts b/src/vs/base/parts/ipc/electron-main/ipc.electron.ts index 431c44bf3d1..f0d395c4bfa 100644 --- a/src/vs/base/parts/ipc/electron-main/ipc.electron.ts +++ b/src/vs/base/parts/ipc/electron-main/ipc.electron.ts @@ -3,7 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ipcMain, WebContents } from 'electron'; +import { WebContents } from 'electron'; +import { validatedIpcMain } from 'vs/base/parts/ipc/electron-main/ipcMain'; import { VSBuffer } from 'vs/base/common/buffer'; import { Emitter, Event } from 'vs/base/common/event'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; @@ -16,7 +17,7 @@ interface IIPCEvent { } function createScopedOnMessageEvent(senderId: number, eventName: string): Event { - const onMessage = Event.fromNodeEventEmitter(ipcMain, eventName, (event, message) => ({ event, message })); + const onMessage = Event.fromNodeEventEmitter(validatedIpcMain, eventName, (event, message) => ({ event, message })); const onMessageFromSender = Event.filter(onMessage, ({ event }) => event.sender.id === senderId); return Event.map(onMessageFromSender, ({ message }) => message ? VSBuffer.wrap(message) : message); @@ -30,7 +31,7 @@ export class Server extends IPCServer { private static readonly Clients = new Map(); private static getOnDidClientConnect(): Event { - const onHello = Event.fromNodeEventEmitter(ipcMain, 'vscode:hello', ({ sender }) => sender); + const onHello = Event.fromNodeEventEmitter(validatedIpcMain, 'vscode:hello', ({ sender }) => sender); return Event.map(onHello, webContents => { const id = webContents.id; diff --git a/src/vs/base/parts/ipc/electron-main/ipc.mp.ts b/src/vs/base/parts/ipc/electron-main/ipc.mp.ts index 3384832669b..df6422ba227 100644 --- a/src/vs/base/parts/ipc/electron-main/ipc.mp.ts +++ b/src/vs/base/parts/ipc/electron-main/ipc.mp.ts @@ -3,7 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { BrowserWindow, ipcMain, IpcMainEvent, MessagePortMain } from 'electron'; +import { BrowserWindow, IpcMainEvent, MessagePortMain } from 'electron'; +import { validatedIpcMain } from 'vs/base/parts/ipc/electron-main/ipcMain'; import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; import { generateUuid } from 'vs/base/common/uuid'; @@ -50,7 +51,7 @@ export async function connect(window: BrowserWindow): Promise { // Wait until the window has returned the `MessagePort` // We need to filter by the `nonce` to ensure we listen // to the right response. - const onMessageChannelResult = Event.fromNodeEventEmitter<{ nonce: string; port: MessagePortMain }>(ipcMain, 'vscode:createMessageChannelResult', (e: IpcMainEvent, nonce: string) => ({ nonce, port: e.ports[0] })); + const onMessageChannelResult = Event.fromNodeEventEmitter<{ nonce: string; port: MessagePortMain }>(validatedIpcMain, 'vscode:createMessageChannelResult', (e: IpcMainEvent, nonce: string) => ({ nonce, port: e.ports[0] })); const { port } = await Event.toPromise(Event.once(Event.filter(onMessageChannelResult, e => e.nonce === nonce))); return port; diff --git a/src/vs/base/parts/ipc/electron-main/ipcMain.ts b/src/vs/base/parts/ipc/electron-main/ipcMain.ts new file mode 100644 index 00000000000..2d95a9f2f50 --- /dev/null +++ b/src/vs/base/parts/ipc/electron-main/ipcMain.ts @@ -0,0 +1,150 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ipcMain as unsafeIpcMain, IpcMainEvent, IpcMainInvokeEvent } from 'electron'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { Event } from 'vs/base/common/event'; + +type ipcMainListener = (event: IpcMainEvent, ...args: any[]) => void; + +class ValidatedIpcMain implements Event.NodeEventEmitter { + + // We need to keep a map of original listener to the wrapped variant in order + // to properly implement `removeListener`. We use a `WeakMap` because we do + // not want to prevent the `key` of the map to get garbage collected. + private readonly mapListenerToWrapper = new WeakMap(); + + /** + * Listens to `channel`, when a new message arrives `listener` would be called with + * `listener(event, args...)`. + */ + on(channel: string, listener: ipcMainListener): this { + + // Remember the wrapped listener so that later we can + // properly implement `removeListener`. + const wrappedListener = (event: IpcMainEvent, ...args: any[]) => { + if (this.validateEvent(channel, event)) { + listener(event, ...args); + } + }; + + this.mapListenerToWrapper.set(listener, wrappedListener); + + unsafeIpcMain.on(channel, wrappedListener); + + return this; + } + + /** + * Adds a one time `listener` function for the event. This `listener` is invoked + * only the next time a message is sent to `channel`, after which it is removed. + */ + once(channel: string, listener: ipcMainListener): this { + unsafeIpcMain.once(channel, (event: IpcMainEvent, ...args: any[]) => { + if (this.validateEvent(channel, event)) { + listener(event, ...args); + } + }); + + return this; + } + + /** + * Adds a handler for an `invoke`able IPC. This handler will be called whenever a + * renderer calls `ipcRenderer.invoke(channel, ...args)`. + * + * If `listener` returns a Promise, the eventual result of the promise will be + * returned as a reply to the remote caller. Otherwise, the return value of the + * listener will be used as the value of the reply. + * + * The `event` that is passed as the first argument to the handler is the same as + * that passed to a regular event listener. It includes information about which + * WebContents is the source of the invoke request. + * + * Errors thrown through `handle` in the main process are not transparent as they + * are serialized and only the `message` property from the original error is + * provided to the renderer process. Please refer to #24427 for details. + */ + handle(channel: string, listener: (event: IpcMainInvokeEvent, ...args: any[]) => Promise): this { + unsafeIpcMain.handle(channel, (event: IpcMainInvokeEvent, ...args: any[]) => { + if (this.validateEvent(channel, event)) { + return listener(event, ...args); + } + + return Promise.reject(`Invalid channel '${channel}' or sender for ipcMain.handle() usage.`); + }); + + return this; + } + + /** + * Removes any handler for `channel`, if present. + */ + removeHandler(channel: string): this { + unsafeIpcMain.removeHandler(channel); + + return this; + } + + /** + * Removes the specified `listener` from the listener array for the specified + * `channel`. + */ + removeListener(channel: string, listener: ipcMainListener): this { + const wrappedListener = this.mapListenerToWrapper.get(listener); + if (wrappedListener) { + unsafeIpcMain.removeListener(channel, wrappedListener); + this.mapListenerToWrapper.delete(listener); + } + + return this; + } + + private validateEvent(channel: string, event: IpcMainEvent | IpcMainInvokeEvent): boolean { + if (!channel || !channel.startsWith('vscode:')) { + onUnexpectedError(`Refused to handle ipcMain event for channel '${channel}' because the channel is unknown.`); + return false; // unexpected channel + } + + const sender = event.senderFrame; + if (!sender) { + return true; // happens when renderer uses `ipcRenderer.postMessage` (TODO@bpasero revisit with newer Electron version) + } + + const url = sender.url; + if (!url) { + return true; // seems to happen sometimes from the early `invoke` call from preload script + } + + let host = 'unknown'; + try { + host = new URL(url).host; + } catch (error) { + onUnexpectedError(`Refused to handle ipcMain event for channel '${channel}' because of a malformed URL '${url}'.`); + return false; // unexpected URL + } + + if (host !== 'vscode-app') { + onUnexpectedError(`Refused to handle ipcMain event for channel '${channel}' because of a bad origin of '${host}'.`); + return false; // unexpected sender + } + + if (sender.parent !== null) { + onUnexpectedError(`Refused to handle ipcMain event for channel '${channel}' because sender of origin '${host}' is not a main frame.`); + return false; // unexpected frame + } + + return true; + } +} + +/** + * A drop-in replacement of `ipcMain` that validates the sender of a message + * according to https://github.com/electron/electron/blob/main/docs/tutorial/security.md + * + * @deprecated direct use of Electron IPC is not encouraged. We have utilities in place + * to create services on top of IPC, see `ProxyChannel` for more information. + */ +export const validatedIpcMain = new ValidatedIpcMain(); diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index b424cecc213..e3f2f7a0d9f 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -3,7 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { app, BrowserWindow, contentTracing, dialog, ipcMain, protocol, session, Session, systemPreferences, WebFrameMain } from 'electron'; +import { app, BrowserWindow, contentTracing, dialog, protocol, session, Session, systemPreferences, WebFrameMain } from 'electron'; +import { validatedIpcMain } from 'vs/base/parts/ipc/electron-main/ipcMain'; import { statSync } from 'fs'; import { hostname, release } from 'os'; import { VSBuffer } from 'vs/base/common/buffer'; @@ -380,7 +381,7 @@ export class CodeApplication extends Disposable { //#region Bootstrap IPC Handlers - ipcMain.handle('vscode:fetchShellEnv', event => { + validatedIpcMain.handle('vscode:fetchShellEnv', event => { // Prefer to use the args and env from the target window // when resolving the shell env. It is possible that @@ -405,7 +406,7 @@ export class CodeApplication extends Disposable { return this.resolveShellEnvironment(args, env, false); }); - ipcMain.handle('vscode:writeNlsFile', (event, path: unknown, data: unknown) => { + validatedIpcMain.handle('vscode:writeNlsFile', (event, path: unknown, data: unknown) => { const uri = this.validateNlsPath([path]); if (!uri || typeof data !== 'string') { throw new Error('Invalid operation (vscode:writeNlsFile)'); @@ -414,7 +415,7 @@ export class CodeApplication extends Disposable { return this.fileService.writeFile(uri, VSBuffer.fromString(data)); }); - ipcMain.handle('vscode:readNlsFile', async (event, ...paths: unknown[]) => { + validatedIpcMain.handle('vscode:readNlsFile', async (event, ...paths: unknown[]) => { const uri = this.validateNlsPath(paths); if (!uri) { throw new Error('Invalid operation (vscode:readNlsFile)'); @@ -423,10 +424,10 @@ export class CodeApplication extends Disposable { return (await this.fileService.readFile(uri)).value.toString(); }); - ipcMain.on('vscode:toggleDevTools', event => event.sender.toggleDevTools()); - ipcMain.on('vscode:openDevTools', event => event.sender.openDevTools()); + validatedIpcMain.on('vscode:toggleDevTools', event => event.sender.toggleDevTools()); + validatedIpcMain.on('vscode:openDevTools', event => event.sender.openDevTools()); - ipcMain.on('vscode:reloadWindow', event => event.sender.reload()); + validatedIpcMain.on('vscode:reloadWindow', event => event.sender.reload()); //#endregion } diff --git a/src/vs/platform/diagnostics/electron-main/diagnosticsMainService.ts b/src/vs/platform/diagnostics/electron-main/diagnosticsMainService.ts index ca6c0f9cdd2..e8b236510d8 100644 --- a/src/vs/platform/diagnostics/electron-main/diagnosticsMainService.ts +++ b/src/vs/platform/diagnostics/electron-main/diagnosticsMainService.ts @@ -3,7 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event as IpcEvent, ipcMain } from 'electron'; +import { Event as IpcEvent } from 'electron'; +import { validatedIpcMain } from 'vs/base/parts/ipc/electron-main/ipcMain'; import { CancellationToken } from 'vs/base/common/cancellation'; import { URI } from 'vs/base/common/uri'; import { IDiagnosticInfo, IDiagnosticInfoOptions, IRemoteDiagnosticError, IRemoteDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnostics'; @@ -49,7 +50,7 @@ export class DiagnosticsMainService implements IDiagnosticsMainService { window.sendWhenReady('vscode:getDiagnosticInfo', CancellationToken.None, { replyChannel, args }); - ipcMain.once(replyChannel, (_: IpcEvent, data: IRemoteDiagnosticInfo) => { + validatedIpcMain.once(replyChannel, (_: IpcEvent, data: IRemoteDiagnosticInfo) => { // No data is returned if getting the connection fails. if (!data) { resolve({ hostName: remoteAuthority, errorMessage: `Unable to resolve connection to '${remoteAuthority}'.` }); diff --git a/src/vs/platform/issue/electron-main/issueMainService.ts b/src/vs/platform/issue/electron-main/issueMainService.ts index 828e7ae65b3..a61b652845a 100644 --- a/src/vs/platform/issue/electron-main/issueMainService.ts +++ b/src/vs/platform/issue/electron-main/issueMainService.ts @@ -3,7 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { BrowserWindow, BrowserWindowConstructorOptions, Display, ipcMain, IpcMainEvent, screen } from 'electron'; +import { BrowserWindow, BrowserWindowConstructorOptions, Display, IpcMainEvent, screen } from 'electron'; +import { validatedIpcMain } from 'vs/base/parts/ipc/electron-main/ipcMain'; import { arch, release, type } from 'os'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { DisposableStore } from 'vs/base/common/lifecycle'; @@ -65,14 +66,14 @@ export class IssueMainService implements ICommonIssueService { } private registerListeners(): void { - ipcMain.on('vscode:issueSystemInfoRequest', async event => { + validatedIpcMain.on('vscode:issueSystemInfoRequest', async event => { const [info, remoteData] = await Promise.all([this.launchMainService.getMainProcessInfo(), this.diagnosticsMainService.getRemoteDiagnostics({ includeProcesses: false, includeWorkspaceMetadata: false })]); const msg = await this.diagnosticsService.getSystemInfo(info, remoteData); this.safeSend(event, 'vscode:issueSystemInfoResponse', msg); }); - ipcMain.on('vscode:listProcesses', async event => { + validatedIpcMain.on('vscode:listProcesses', async event => { const processes = []; try { @@ -102,7 +103,7 @@ export class IssueMainService implements ICommonIssueService { this.safeSend(event, 'vscode:listProcessesResponse', processes); }); - ipcMain.on('vscode:issueReporterClipboard', async event => { + validatedIpcMain.on('vscode:issueReporterClipboard', async event => { const messageOptions = { title: this.productService.nameLong, message: localize('issueReporterWriteToClipboard', "There is too much data to send to GitHub directly. The data will be copied to the clipboard, please paste it into the GitHub issue page that is opened."), @@ -122,12 +123,12 @@ export class IssueMainService implements ICommonIssueService { } }); - ipcMain.on('vscode:issuePerformanceInfoRequest', async event => { + validatedIpcMain.on('vscode:issuePerformanceInfoRequest', async event => { const performanceInfo = await this.getPerformanceInfo(); this.safeSend(event, 'vscode:issuePerformanceInfoResponse', performanceInfo); }); - ipcMain.on('vscode:issueReporterConfirmClose', async () => { + validatedIpcMain.on('vscode:issueReporterConfirmClose', async () => { const messageOptions = { title: this.productService.nameLong, message: localize('confirmCloseIssueReporter', "Your input will not be saved. Are you sure you want to close this window?"), @@ -152,7 +153,7 @@ export class IssueMainService implements ICommonIssueService { } }); - ipcMain.on('vscode:workbenchCommand', (_: unknown, commandInfo: { id: any; from: any; args: any }) => { + validatedIpcMain.on('vscode:workbenchCommand', (_: unknown, commandInfo: { id: any; from: any; args: any }) => { const { id, from, args } = commandInfo; let parentWindow: BrowserWindow | null; @@ -172,23 +173,23 @@ export class IssueMainService implements ICommonIssueService { } }); - ipcMain.on('vscode:openExternal', (_: unknown, arg: string) => { + validatedIpcMain.on('vscode:openExternal', (_: unknown, arg: string) => { this.nativeHostMainService.openExternal(undefined, arg); }); - ipcMain.on('vscode:closeIssueReporter', event => { + validatedIpcMain.on('vscode:closeIssueReporter', event => { if (this.issueReporterWindow) { this.issueReporterWindow.close(); } }); - ipcMain.on('vscode:closeProcessExplorer', event => { + validatedIpcMain.on('vscode:closeProcessExplorer', event => { if (this.processExplorerWindow) { this.processExplorerWindow.close(); } }); - ipcMain.on('vscode:windowsInfoRequest', async event => { + validatedIpcMain.on('vscode:windowsInfoRequest', async event => { const mainProcessInfo = await this.launchMainService.getMainProcessInfo(); this.safeSend(event, 'vscode:windowsInfoResponse', mainProcessInfo.windows); }); diff --git a/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts b/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts index ac3b7bfb3b2..ae26de3e981 100644 --- a/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts +++ b/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts @@ -3,7 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { app, BrowserWindow, ipcMain } from 'electron'; +import { app, BrowserWindow } from 'electron'; +import { validatedIpcMain } from 'vs/base/parts/ipc/electron-main/ipcMain'; import { Barrier, Promises, timeout } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; @@ -511,11 +512,11 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe const okChannel = `vscode:ok${oneTimeEventToken}`; const cancelChannel = `vscode:cancel${oneTimeEventToken}`; - ipcMain.once(okChannel, () => { + validatedIpcMain.once(okChannel, () => { resolve(false); // no veto }); - ipcMain.once(cancelChannel, () => { + validatedIpcMain.once(cancelChannel, () => { resolve(true); // veto }); @@ -528,7 +529,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe const oneTimeEventToken = this.oneTimeListenerTokenGenerator++; const replyChannel = `vscode:reply${oneTimeEventToken}`; - ipcMain.once(replyChannel, () => resolve()); + validatedIpcMain.once(replyChannel, () => resolve()); window.send('vscode:onWillUnload', { replyChannel, reason }); }); diff --git a/src/vs/platform/protocol/electron-main/protocolMainService.ts b/src/vs/platform/protocol/electron-main/protocolMainService.ts index 324c8abbece..e414b377c6e 100644 --- a/src/vs/platform/protocol/electron-main/protocolMainService.ts +++ b/src/vs/platform/protocol/electron-main/protocolMainService.ts @@ -3,7 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ipcMain, session } from 'electron'; +import { session } from 'electron'; +import { validatedIpcMain } from 'vs/base/parts/ipc/electron-main/ipcMain'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { TernarySearchTree } from 'vs/base/common/map'; import { FileAccess, Schemas } from 'vs/base/common/network'; @@ -138,7 +139,7 @@ export class ProtocolMainService extends Disposable implements IProtocolMainServ // Install IPC handler const channel = resource.toString(); const handler = async (): Promise => obj; - ipcMain.handle(channel, handler); + validatedIpcMain.handle(channel, handler); this.logService.trace(`IPC Object URL: Registered new channel ${channel}.`); @@ -148,7 +149,7 @@ export class ProtocolMainService extends Disposable implements IProtocolMainServ dispose: () => { this.logService.trace(`IPC Object URL: Removed channel ${channel}.`); - ipcMain.removeHandler(channel); + validatedIpcMain.removeHandler(channel); } }; } diff --git a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts index 3d2122e121d..f7046a4d756 100644 --- a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts +++ b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts @@ -3,7 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { BrowserWindow, Event as ElectronEvent, ipcMain, IpcMainEvent, MessagePortMain } from 'electron'; +import { BrowserWindow, Event as ElectronEvent, IpcMainEvent, MessagePortMain } from 'electron'; +import { validatedIpcMain } from 'vs/base/parts/ipc/electron-main/ipcMain'; import { Barrier } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; @@ -49,10 +50,10 @@ export class SharedProcess extends Disposable implements ISharedProcess { private registerListeners(): void { // Shared process connections from workbench windows - ipcMain.on('vscode:createSharedProcessMessageChannel', (e, nonce: string) => this.onWindowConnection(e, nonce)); + validatedIpcMain.on('vscode:createSharedProcessMessageChannel', (e, nonce: string) => this.onWindowConnection(e, nonce)); // Shared process worker relay - ipcMain.on('vscode:relaySharedProcessWorkerMessageChannel', (e, configuration: ISharedProcessWorkerConfiguration) => this.onWorkerConnection(e, configuration)); + validatedIpcMain.on('vscode:relaySharedProcessWorkerMessageChannel', (e, configuration: ISharedProcessWorkerConfiguration) => this.onWorkerConnection(e, configuration)); // Lifecycle this._register(this.lifecycleMainService.onWillShutdown(() => this.onWillShutdown())); @@ -173,7 +174,7 @@ export class SharedProcess extends Disposable implements ISharedProcess { if (!this._whenReady) { // Overall signal that the shared process window was loaded and // all services within have been created. - this._whenReady = new Promise(resolve => ipcMain.once('vscode:shared-process->electron-main=init-done', () => { + this._whenReady = new Promise(resolve => validatedIpcMain.once('vscode:shared-process->electron-main=init-done', () => { this.logService.trace('SharedProcess: Overall ready'); resolve(); @@ -198,7 +199,7 @@ export class SharedProcess extends Disposable implements ISharedProcess { this.registerWindowListeners(); // Wait for window indicating that IPC connections are accepted - await new Promise(resolve => ipcMain.once('vscode:shared-process->electron-main=ipc-ready', () => { + await new Promise(resolve => validatedIpcMain.once('vscode:shared-process->electron-main=ipc-ready', () => { this.logService.trace('SharedProcess: IPC ready'); resolve();