From 40e007d6a4de9b4c0be49eb5f2d4f1706eaf801d Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Sun, 19 Sep 2021 00:38:19 +0200 Subject: security: do not expose electron API to services (#1964) * security: do not expose electron API to services Service code running the the main world should not have access to any electron API. This still allows recipes from webview.js accessing these APIs through the @electron/remote module and/or the Ferdi object, but webview-unsafe.js and the untrusted code coming from the service will not have any access. Currently, no recipe accesses these APIs in its webview-unsafe.js, so the change should not break any recipes. * Expose electron API through the Ferdi object Instead of the unsafe window.ferdi in the main world, we should expose functionality to recipes through the RecipeWebview class. * Update CHANGELOG.md --- src/webview/lib/RecipeWebview.js | 32 +++++++++++++++++++++++++++++++- src/webview/recipe.js | 16 ++++++---------- 2 files changed, 37 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/webview/lib/RecipeWebview.js b/src/webview/lib/RecipeWebview.js index 4085b925b..a4951ed69 100644 --- a/src/webview/lib/RecipeWebview.js +++ b/src/webview/lib/RecipeWebview.js @@ -1,12 +1,14 @@ import { ipcRenderer } from 'electron'; +import { BrowserWindow, desktopCapturer, getCurrentWebContents } from '@electron/remote'; import { pathExistsSync, readFileSync, existsSync } from 'fs-extra'; const debug = require('debug')('Ferdi:Plugin:RecipeWebview'); class RecipeWebview { - constructor(badgeHandler, notificationsHandler) { + constructor(badgeHandler, notificationsHandler, sessionHandler) { this.badgeHandler = badgeHandler; this.notificationsHandler = notificationsHandler; + this.sessionHandler = sessionHandler; ipcRenderer.on('poll', () => { this.loopFunc(); @@ -23,6 +25,26 @@ class RecipeWebview { darkModeHandler = false; + // TODO Remove this once we implement a proper wrapper. + get ipcRenderer() { + return ipcRenderer; + } + + // TODO Remove this once we implement a proper wrapper. + get desktopCapturer() { + return desktopCapturer; + } + + // TODO Remove this once we implement a proper wrapper. + get BrowserWindow() { + return BrowserWindow; + } + + // TODO Remove this once we implement a proper wrapper. + get getCurrentWebContents() { + return getCurrentWebContents; + } + /** * Initialize the loop * @@ -113,6 +135,14 @@ class RecipeWebview { fn(); } } + + clearStorageData(storageLocations) { + this.sessionHandler.clearStorageData(storageLocations); + } + + releaseServiceWorkers() { + this.sessionHandler.releaseServiceWorkers(); + } } export default RecipeWebview; diff --git a/src/webview/recipe.js b/src/webview/recipe.js index d7032da3f..32df0f756 100644 --- a/src/webview/recipe.js +++ b/src/webview/recipe.js @@ -1,7 +1,6 @@ /* eslint-disable global-require */ /* eslint-disable import/first */ -import { contextBridge, desktopCapturer, ipcRenderer } from 'electron'; -import { BrowserWindow, getCurrentWebContents } from '@electron/remote'; +import { contextBridge, ipcRenderer } from 'electron'; import { join } from 'path'; import { autorun, computed, observable } from 'mobx'; import { pathExistsSync, readFileSync } from 'fs-extra'; @@ -109,15 +108,8 @@ contextBridge.exposeInMainWorld('ferdi', { safeParseInt: text => badgeHandler.safeParseInt(text), displayNotification: (title, options) => notificationsHandler.displayNotification(title, options), - clearStorageData: storageLocations => - sessionHandler.clearStorageData(storageLocations), releaseServiceWorkers: () => sessionHandler.releaseServiceWorkers(), getDisplayMediaSelector, - getCurrentWebContents, - BrowserWindow, - ipcRenderer, - // TODO: When the discord recipe is changed to use the screenshare.js, this can be removed - desktopCapturer, }); ipcRenderer.sendToHost( @@ -207,7 +199,11 @@ class RecipeController { // Delete module from cache delete require.cache[require.resolve(modulePath)]; try { - this.recipe = new RecipeWebview(badgeHandler, notificationsHandler); + this.recipe = new RecipeWebview( + badgeHandler, + notificationsHandler, + sessionHandler, + ); // eslint-disable-next-line import/no-dynamic-require require(modulePath)(this.recipe, { ...config, recipe }); debug('Initialize Recipe', config, recipe); -- cgit v1.2.3-54-g00ecf