From 94d054cf11c1f6bf22f6e13222b17aa070c90a89 Mon Sep 17 00:00:00 2001 From: Hari Krishna Reddy Juturu Date: Sat, 6 May 2017 22:10:42 -0700 Subject: [PATCH] Add option to override webview security --- atom/browser/web_contents_preferences.cc | 7 +++++ atom/common/options_switches.cc | 4 +++ atom/common/options_switches.h | 2 ++ docs/api/browser-window.md | 5 +++ docs/api/webview-tag.md | 4 +++ lib/renderer/init.js | 5 ++- spec/webview-spec.js | 39 +++++++++++++++++++++++- 7 files changed, 64 insertions(+), 2 deletions(-) diff --git a/atom/browser/web_contents_preferences.cc b/atom/browser/web_contents_preferences.cc index 7bb8605039a1..642e25598543 100644 --- a/atom/browser/web_contents_preferences.cc +++ b/atom/browser/web_contents_preferences.cc @@ -101,6 +101,13 @@ void WebContentsPreferences::AppendExtraCommandLineSwitches( if (web_preferences.GetBoolean(options::kNodeIntegrationInWorker, &b) && b) command_line->AppendSwitch(switches::kNodeIntegrationInWorker); + // Check if webview tag creation is overriden. + bool enable_webview_override = false; + web_preferences.GetBoolean(options::kEnableWebViewOverride, + &enable_webview_override); + command_line->AppendSwitchASCII(switches::kEnableWebViewOverride, + enable_webview_override ? "true" : "false"); + // If the `sandbox` option was passed to the BrowserWindow's webPreferences, // pass `--enable-sandbox` to the renderer so it won't have any node.js // integration. diff --git a/atom/common/options_switches.cc b/atom/common/options_switches.cc index 2f1c0368f35e..71aed95337fc 100644 --- a/atom/common/options_switches.cc +++ b/atom/common/options_switches.cc @@ -128,6 +128,9 @@ const char kDisableBlinkFeatures[] = "disableBlinkFeatures"; // Enable the node integration in WebWorker. const char kNodeIntegrationInWorker[] = "nodeIntegrationInWorker"; +// Enable the web view tag irrespective of node-integration setting. +const char kEnableWebViewOverride[] = "enableWebViewOverride"; + } // namespace options namespace switches { @@ -172,6 +175,7 @@ const char kGuestInstanceID[] = "guest-instance-id"; const char kOpenerID[] = "opener-id"; const char kScrollBounce[] = "scroll-bounce"; const char kHiddenPage[] = "hidden-page"; +const char kEnableWebViewOverride[] = "enable-webview-override"; // Command switch passed to renderer process to control nodeIntegration. const char kNodeIntegrationInWorker[] = "node-integration-in-worker"; diff --git a/atom/common/options_switches.h b/atom/common/options_switches.h index 69e7af029e15..4ad0984b02d0 100644 --- a/atom/common/options_switches.h +++ b/atom/common/options_switches.h @@ -64,6 +64,7 @@ extern const char kScrollBounce[]; extern const char kBlinkFeatures[]; extern const char kDisableBlinkFeatures[]; extern const char kNodeIntegrationInWorker[]; +extern const char kEnableWebViewOverride[]; } // namespace options @@ -93,6 +94,7 @@ extern const char kOpenerID[]; extern const char kScrollBounce[]; extern const char kHiddenPage[]; extern const char kNodeIntegrationInWorker[]; +extern const char kEnableWebViewOverride[]; extern const char kWidevineCdmPath[]; extern const char kWidevineCdmVersion[]; diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 8fcc3f8213d0..3d079669e41c 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -307,6 +307,11 @@ It creates a new `BrowserWindow` with native properties as set by the `options`. 'Electron Isolated Context' entry in the combo box at the top of the Console tab. **Note:** This option is currently experimental and may change or be removed in future Electron releases. + * `enableWebViewOverride` Boolean (optional) - Whether to enable [webview-tag](webview-tag.md) + ignoring the security restriction based on `nodeIntegration`. Enabling this option will + have security implication on creating `webview` with `nodeIntegration` disabled. To avoid the + security risk, listen to `will-create-webview` event on [web-contents](web-contents.md) and + stop creating `webview` or removing preload scripts. When setting minimum or maximum window size with `minWidth`/`maxWidth`/ `minHeight`/`maxHeight`, it only constrains the users. It won't prevent you from diff --git a/docs/api/webview-tag.md b/docs/api/webview-tag.md index db02ae2ede4d..6dc8d7de58cf 100644 --- a/docs/api/webview-tag.md +++ b/docs/api/webview-tag.md @@ -15,6 +15,10 @@ between your app and embedded content will be asynchronous. This keeps your app safe from the embedded content. **Note:** Most methods called on the webview from the host page require a syncronous call to the main process. +For security purposes, `webview` can only be used in `BrowserWindow`s that have +`nodeIntegration` enabled. You can override this security restiction using +`enableWebViewOverride` option on [browser-window](browser-window.md). + ## Example To embed a web page in your app, add the `webview` tag to your app's embedder diff --git a/lib/renderer/init.js b/lib/renderer/init.js index cbcd43a3e63e..65b580fa3fd5 100644 --- a/lib/renderer/init.js +++ b/lib/renderer/init.js @@ -57,6 +57,7 @@ let nodeIntegration = 'false' let preloadScript = null let isBackgroundPage = false let appPath = null +let enableWebView = false for (let arg of process.argv) { if (arg.indexOf('--guest-instance-id=') === 0) { // This is a guest web view. @@ -72,6 +73,8 @@ for (let arg of process.argv) { isBackgroundPage = true } else if (arg.indexOf('--app-path=') === 0) { appPath = arg.substr(arg.indexOf('=') + 1) + } else if (arg.indexOf('--enable-webview-override=') === 0) { + enableWebView = arg.substr(arg.indexOf('=') + 1) } } @@ -94,7 +97,7 @@ if (window.location.protocol === 'chrome-devtools:') { require('./content-scripts-injector') // Load webview tag implementation. - if (process.guestInstanceId == null) { + if ((nodeIntegration === 'true' || enableWebView === 'true') && process.guestInstanceId == null) { require('./web-view/web-view') require('./web-view/web-view-attributes') } diff --git a/spec/webview-spec.js b/spec/webview-spec.js index 744d63eff7ee..10d2998c69e0 100644 --- a/spec/webview-spec.js +++ b/spec/webview-spec.js @@ -8,7 +8,7 @@ const {closeWindow} = require('./window-helpers') const isCI = remote.getGlobal('isCi') -describe(' tag', function () { +describe.only(' tag', function () { this.timeout(3 * 60 * 1000) var fixtures = path.join(__dirname, 'fixtures') @@ -36,6 +36,43 @@ describe(' tag', function () { w.loadURL('file://' + fixtures + '/pages/webview-no-script.html') }) + it('is disabled when nodeIntegration is disabled', function (done) { + w = new BrowserWindow({ + show: false, + webPreferences: { + nodeIntegration: false, + preload: path.join(fixtures, 'module', 'preload-webview.js') + } + }) + ipcMain.once('webview', function (event, type) { + if (type === 'undefined') { + done() + } else { + done('WebView still exists') + } + }) + w.loadURL('file://' + fixtures + '/pages/webview-no-script.html') + }) + + it('is enabled when override is set', function (done) { + w = new BrowserWindow({ + show: false, + webPreferences: { + nodeIntegration: false, + preload: path.join(fixtures, 'module', 'preload-webview.js'), + enableWebViewOverride: true + } + }) + ipcMain.once('webview', function (event, type) { + if (type !== 'undefined') { + done() + } else { + done('WebView is not created') + } + }) + w.loadURL('file://' + fixtures + '/pages/webview-no-script.html') + }) + describe('src attribute', function () { it('specifies the page to load', function (done) { webview.addEventListener('console-message', function (e) {