Browse Source

fix: execute session preload scripts in sandboxed renderers (#17875)

Milan Burda 6 years ago
parent
commit
b3ed83055c

+ 12 - 5
lib/browser/rpc-server.js

@@ -490,20 +490,27 @@ ipcMain.on('ELECTRON_BROWSER_CLIPBOARD_WRITE_FIND_TEXT', function (event, text)
   setReturnValue(event, () => electron.clipboard.writeFindText(text))
 })
 
-ipcMain.on('ELECTRON_BROWSER_SANDBOX_LOAD', function (event) {
-  const preloadPath = event.sender._getPreloadPath()
+const getPreloadScript = function (preloadPath) {
   let preloadSrc = null
   let preloadError = null
   if (preloadPath) {
     try {
       preloadSrc = fs.readFileSync(preloadPath).toString()
     } catch (err) {
-      preloadError = { stack: err ? err.stack : (new Error(`Failed to load "${preloadPath}"`)).stack }
+      preloadError = errorUtils.serialize(err)
     }
   }
+  return { preloadPath, preloadSrc, preloadError }
+}
+
+ipcMain.on('ELECTRON_BROWSER_SANDBOX_LOAD', function (event) {
+  const preloadPaths = [
+    ...(event.sender.session ? event.sender.session.getPreloads() : []),
+    event.sender._getPreloadPath()
+  ]
+
   event.returnValue = {
-    preloadSrc,
-    preloadError,
+    preloadScripts: preloadPaths.map(path => getPreloadScript(path)),
     isRemoteModuleEnabled: event.sender._isRemoteModuleEnabled(),
     process: {
       arch: process.arch,

+ 18 - 4
lib/sandboxed_renderer/init.js

@@ -67,7 +67,7 @@ ipcNative.onExit = function () {
 }
 
 const {
-  preloadSrc, preloadError, isRemoteModuleEnabled, process: processProps
+  preloadScripts, isRemoteModuleEnabled, process: processProps
 } = ipcRenderer.sendSync('ELECTRON_BROWSER_SANDBOX_LOAD')
 
 const makePropertyNonConfigurable = function (object, name) {
@@ -131,6 +131,8 @@ if (!process.guestInstanceId && preloadProcess.argv.includes('--webview-tag=true
   require('@electron/internal/renderer/web-view/web-view').setupWebView(window)
 }
 
+const errorUtils = require('@electron/internal/common/error-utils')
+
 // Wrap the script into a function executed in global scope. It won't have
 // access to the current scope, so we'll expose a few objects as arguments:
 //
@@ -150,7 +152,7 @@ if (!process.guestInstanceId && preloadProcess.argv.includes('--webview-tag=true
 // and any `require('electron')` calls in `preload.js` will work as expected
 // since browserify won't try to include `electron` in the bundle, falling back
 // to the `preloadRequire` function above.
-if (preloadSrc) {
+function runPreloadScript (preloadSrc) {
   const preloadWrapperSrc = `(function(require, process, Buffer, global, setImmediate, clearImmediate) {
   ${preloadSrc}
   })`
@@ -158,9 +160,21 @@ if (preloadSrc) {
   // eval in window scope
   const preloadFn = binding.createPreloadScript(preloadWrapperSrc)
   const { setImmediate, clearImmediate } = require('timers')
+
   preloadFn(preloadRequire, preloadProcess, Buffer, global, setImmediate, clearImmediate)
-} else if (preloadError) {
-  console.error(preloadError.stack)
+}
+
+for (const { preloadPath, preloadSrc, preloadError } of preloadScripts) {
+  try {
+    if (preloadSrc) {
+      runPreloadScript(preloadSrc)
+    } else if (preloadError) {
+      throw errorUtils.deserialize(preloadError)
+    }
+  } catch (error) {
+    console.error(`Unable to load preload script: ${preloadPath}`)
+    console.error(`${error}`)
+  }
 }
 
 // Warn about security issues

+ 22 - 15
spec/api-browser-window-spec.js

@@ -1343,22 +1343,29 @@ describe('BrowserWindow module', () => {
         assert.deepStrictEqual(defaultSession.getPreloads(), preloads)
       })
 
-      it('loads the script before other scripts in window including normal preloads', function (done) {
-        ipcMain.once('vars', function (event, preload1, preload2, preload3) {
-          assert.strictEqual(preload1, 'preload-1')
-          assert.strictEqual(preload2, 'preload-1-2')
-          assert.strictEqual(preload3, 'preload-1-2-3')
-          done()
-        })
-        w.destroy()
-        w = new BrowserWindow({
-          show: false,
-          webPreferences: {
-            preload: path.join(fixtures, 'module', 'set-global-preload-3.js')
-          }
+      const generateSpecs = (description, sandbox) => {
+        describe(description, () => {
+          it('loads the script before other scripts in window including normal preloads', function (done) {
+            ipcMain.once('vars', function (event, preload1, preload2) {
+              assert.strictEqual(preload1, 'preload-1')
+              assert.strictEqual(preload2, 'preload-1-2')
+              done()
+            })
+            w.destroy()
+            w = new BrowserWindow({
+              show: false,
+              webPreferences: {
+                sandbox,
+                preload: path.join(fixtures, 'module', 'get-global-preload.js')
+              }
+            })
+            w.loadURL('about:blank')
+          })
         })
-        w.loadFile(path.join(fixtures, 'api', 'preloads.html'))
-      })
+      }
+
+      generateSpecs('without sandbox', false)
+      generateSpecs('with sandbox', true)
     })
 
     describe('"additionalArguments" option', () => {

+ 1 - 0
spec/fixtures/module/get-global-preload.js

@@ -0,0 +1 @@
+require('electron').ipcRenderer.send('vars', window.preload1, window.preload2)

+ 0 - 1
spec/fixtures/module/set-global-preload-3.js

@@ -1 +0,0 @@
-window.preload3 = window.preload2 + '-3'