Browse Source

security: only handle related IPCs when <webview> tag is enabled (backport: 4-0-x) (#15931)

* refactor: move guest-view-manager related IPC handling out of rpc-server

* feat: only handle related IPCs when <webview> tag is enabled
trop[bot] 6 years ago
parent
commit
4ad3a39f1d

+ 59 - 5
lib/browser/guest-view-manager.js

@@ -3,6 +3,7 @@
 const { webContents } = require('electron')
 const ipcMain = require('@electron/internal/browser/ipc-main-internal')
 const parseFeaturesString = require('@electron/internal/common/parse-features-string')
+const errorUtils = require('@electron/internal/common/error-utils')
 
 // Doesn't exist in early initialization.
 let webViewManager = null
@@ -327,29 +328,82 @@ const watchEmbedder = function (embedder) {
   })
 }
 
-ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', function (event, params, requestId) {
+const isWebViewTagEnabledCache = new WeakMap()
+
+const isWebViewTagEnabled = function (contents) {
+  if (!isWebViewTagEnabledCache.has(contents)) {
+    const value = contents.getLastWebPreferences().webviewTag
+    isWebViewTagEnabledCache.set(contents, value)
+  }
+
+  return isWebViewTagEnabledCache.get(contents)
+}
+
+const handleMessage = function (channel, handler) {
+  ipcMain.on(channel, (event, ...args) => {
+    if (isWebViewTagEnabled(event.sender)) {
+      handler(event, ...args)
+    } else {
+      event.returnValue = null
+    }
+  })
+}
+
+handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', function (event, params, requestId) {
   event.sender._sendInternal(`ELECTRON_RESPONSE_${requestId}`, createGuest(event.sender, params))
 })
 
-ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST_SYNC', function (event, params) {
+handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST_SYNC', function (event, params) {
   event.returnValue = createGuest(event.sender, params)
 })
 
-ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_DESTROY_GUEST', function (event, guestInstanceId) {
+handleMessage('ELECTRON_GUEST_VIEW_MANAGER_DESTROY_GUEST', function (event, guestInstanceId) {
   const guest = getGuest(guestInstanceId)
   if (guest) {
     guest.destroy()
   }
 })
 
-ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', function (event, embedderFrameId, elementInstanceId, guestInstanceId, params) {
+handleMessage('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', function (event, embedderFrameId, elementInstanceId, guestInstanceId, params) {
   attachGuest(event, embedderFrameId, elementInstanceId, guestInstanceId, params)
 })
 
-ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', function (event, focus, guestInstanceId) {
+handleMessage('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', function (event, focus, guestInstanceId) {
   event.sender.emit('focus-change', {}, focus, guestInstanceId)
 })
 
+handleMessage('ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL', function (event, requestId, guestInstanceId, method, args, hasCallback) {
+  new Promise(resolve => {
+    const guest = getGuest(guestInstanceId)
+    if (guest.hostWebContents !== event.sender) {
+      throw new Error('Access denied')
+    }
+    if (hasCallback) {
+      guest[method](...args, resolve)
+    } else {
+      resolve(guest[method](...args))
+    }
+  }).then(result => {
+    return [null, result]
+  }, error => {
+    return [errorUtils.serialize(error)]
+  }).then(responseArgs => {
+    event.sender._sendInternal(`ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL_RESPONSE_${requestId}`, ...responseArgs)
+  })
+})
+
+handleMessage('ELECTRON_GUEST_VIEW_MANAGER_SYNC_CALL', function (event, guestInstanceId, method, args) {
+  try {
+    const guest = getGuest(guestInstanceId)
+    if (guest.hostWebContents !== event.sender) {
+      throw new Error('Access denied')
+    }
+    event.returnValue = [null, guest[method].apply(guest, args)]
+  } catch (error) {
+    event.returnValue = [errorUtils.serialize(error)]
+  }
+})
+
 // Returns WebContents from its guest id.
 const getGuest = function (guestInstanceId) {
   const guestInstance = guestInstances[guestInstanceId]

+ 0 - 34
lib/browser/rpc-server.js

@@ -413,40 +413,6 @@ handleRemoteCommand('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', function (event, cont
   return valueToMeta(event.sender, contextId, guestViewManager.getGuest(guestInstanceId))
 })
 
-ipcMain.on('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', function (event, requestId, guestInstanceId, method, args, hasCallback) {
-  new Promise(resolve => {
-    const guestViewManager = require('@electron/internal/browser/guest-view-manager')
-    const guest = guestViewManager.getGuest(guestInstanceId)
-    if (guest.hostWebContents !== event.sender) {
-      throw new Error('Access denied')
-    }
-    if (hasCallback) {
-      guest[method](...args, resolve)
-    } else {
-      resolve(guest[method](...args))
-    }
-  }).then(result => {
-    return [null, result]
-  }, error => {
-    return [errorUtils.serialize(error)]
-  }).then(responseArgs => {
-    event.sender._sendInternal(`ELECTRON_RENDERER_ASYNC_CALL_TO_GUEST_VIEW_RESPONSE_${requestId}`, ...responseArgs)
-  })
-})
-
-ipcMain.on('ELECTRON_BROWSER_SYNC_CALL_TO_GUEST_VIEW', function (event, guestInstanceId, method, args) {
-  try {
-    const guestViewManager = require('@electron/internal/browser/guest-view-manager')
-    const guest = guestViewManager.getGuest(guestInstanceId)
-    if (guest.hostWebContents !== event.sender) {
-      throw new Error('Access denied')
-    }
-    event.returnValue = [null, guest[method].apply(guest, args)]
-  } catch (error) {
-    event.returnValue = [errorUtils.serialize(error)]
-  }
-})
-
 // Implements window.close()
 ipcMain.on('ELECTRON_BROWSER_WINDOW_CLOSE', function (event) {
   const window = event.sender.getOwnerBrowserWindow()

+ 1 - 1
lib/renderer/web-view/web-view-attributes.js

@@ -186,7 +186,7 @@ class SrcAttribute extends WebViewAttribute {
     const method = 'loadURL'
     const args = [this.getValue(), opts]
 
-    const [error] = ipcRenderer.sendSync('ELECTRON_BROWSER_SYNC_CALL_TO_GUEST_VIEW', guestInstanceId, method, args)
+    const [error] = ipcRenderer.sendSync('ELECTRON_GUEST_VIEW_MANAGER_SYNC_CALL', guestInstanceId, method, args)
     if (error) {
       throw errorUtils.deserialize(error)
     }

+ 3 - 3
lib/renderer/web-view/web-view.js

@@ -306,7 +306,7 @@ const registerWebViewElement = function () {
   // Forward proto.foo* method calls to WebViewImpl.foo*.
   const createBlockHandler = function (method) {
     return function (...args) {
-      const [error, result] = ipcRenderer.sendSync('ELECTRON_BROWSER_SYNC_CALL_TO_GUEST_VIEW', getGuestInstanceId(this), method, args)
+      const [error, result] = ipcRenderer.sendSync('ELECTRON_GUEST_VIEW_MANAGER_SYNC_CALL', getGuestInstanceId(this), method, args)
       if (error) {
         throw errorUtils.deserialize(error)
       } else {
@@ -322,14 +322,14 @@ const registerWebViewElement = function () {
     return function (...args) {
       const callback = (typeof args[args.length - 1] === 'function') ? args.pop() : null
       const requestId = getNextId()
-      ipcRenderer.once(`ELECTRON_RENDERER_ASYNC_CALL_TO_GUEST_VIEW_RESPONSE_${requestId}`, function (event, error, result) {
+      ipcRenderer.once(`ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL_RESPONSE_${requestId}`, function (event, error, result) {
         if (error == null) {
           if (callback) callback(result)
         } else {
           throw errorUtils.deserialize(error)
         }
       })
-      ipcRenderer.send('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', requestId, getGuestInstanceId(this), method, args, callback != null)
+      ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL', requestId, getGuestInstanceId(this), method, args, callback != null)
     }
   }
   for (const method of nonblockMethods) {