Browse Source

Merge pull request #16252 from electron/miniak/guest-view-manager

fix: security: improve IPC validation in guest-view-manager
Jeremy Apthorp 6 years ago
parent
commit
959c7a76e9
2 changed files with 39 additions and 18 deletions
  1. 36 16
      lib/browser/guest-view-manager.js
  2. 3 2
      lib/browser/rpc-server.js

+ 36 - 16
lib/browser/guest-view-manager.js

@@ -184,9 +184,12 @@ const attachGuest = function (event, embedderFrameId, elementInstanceId, guestIn
   const guestInstance = guestInstances[guestInstanceId]
   // If this isn't a valid guest instance then do nothing.
   if (!guestInstance) {
-    return
+    throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`)
   }
   const { guest } = guestInstance
+  if (guest.hostWebContents !== event.sender) {
+    throw new Error(`Access denied to guestInstanceId: ${guestInstanceId}`)
+  }
 
   // If this guest is already attached to an element then remove it
   if (guestInstance.elementInstanceId) {
@@ -351,26 +354,35 @@ handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST_SYNC', function (event,
 })
 
 handleMessage('ELECTRON_GUEST_VIEW_MANAGER_DESTROY_GUEST', function (event, guestInstanceId) {
-  const guest = getGuest(guestInstanceId)
-  if (guest) {
+  try {
+    const guest = getGuestForWebContents(guestInstanceId, event.sender)
     guest.destroy()
+  } catch (error) {
+    console.error(`Guest destroy failed: ${error}`)
   }
 })
 
 handleMessage('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', function (event, embedderFrameId, elementInstanceId, guestInstanceId, params) {
-  attachGuest(event, embedderFrameId, elementInstanceId, guestInstanceId, params)
+  try {
+    attachGuest(event, embedderFrameId, elementInstanceId, guestInstanceId, params)
+  } catch (error) {
+    console.error(`Guest attach failed: ${error}`)
+  }
 })
 
-handleMessage('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', function (event, focus, guestInstanceId) {
-  event.sender.emit('focus-change', {}, focus, guestInstanceId)
+// this message is sent by the actual <webview>
+ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', function (event, focus, guestInstanceId) {
+  const guest = getGuest(guestInstanceId)
+  if (guest === event.sender) {
+    event.sender.emit('focus-change', {}, focus, guestInstanceId)
+  } else {
+    console.error(`focus-change for guestInstanceId: ${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(`Invalid guestInstanceId: ${guestInstanceId}`)
-    }
+    const guest = getGuestForWebContents(guestInstanceId, event.sender)
     if (!asyncMethods.has(method)) {
       throw new Error(`Invalid method: ${method}`)
     }
@@ -390,10 +402,7 @@ handleMessage('ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL', function (event, request
 
 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(`Invalid guestInstanceId: ${guestInstanceId}`)
-    }
+    const guest = getGuestForWebContents(guestInstanceId, event.sender)
     if (!syncMethods.has(method)) {
       throw new Error(`Invalid method: ${method}`)
     }
@@ -403,6 +412,18 @@ handleMessage('ELECTRON_GUEST_VIEW_MANAGER_SYNC_CALL', function (event, guestIns
   }
 })
 
+// Returns WebContents from its guest id hosted in given webContents.
+const getGuestForWebContents = function (guestInstanceId, contents) {
+  const guest = getGuest(guestInstanceId)
+  if (!guest) {
+    throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`)
+  }
+  if (guest.hostWebContents !== contents) {
+    throw new Error(`Access denied to guestInstanceId: ${guestInstanceId}`)
+  }
+  return guest
+}
+
 // Returns WebContents from its guest id.
 const getGuest = function (guestInstanceId) {
   const guestInstance = guestInstances[guestInstanceId]
@@ -415,5 +436,4 @@ const getEmbedder = function (guestInstanceId) {
   if (guestInstance != null) return guestInstance.embedder
 }
 
-exports.getGuest = getGuest
-exports.getEmbedder = getEmbedder
+exports.getGuestForWebContents = getGuestForWebContents

+ 3 - 2
lib/browser/rpc-server.js

@@ -13,6 +13,7 @@ const { isPromise } = electron
 
 const ipcMain = require('@electron/internal/browser/ipc-main-internal')
 const objectsRegistry = require('@electron/internal/browser/objects-registry')
+const guestViewManager = require('@electron/internal/browser/guest-view-manager')
 const bufferUtils = require('@electron/internal/common/buffer-utils')
 const errorUtils = require('@electron/internal/common/error-utils')
 
@@ -411,8 +412,8 @@ handleRemoteCommand('ELECTRON_BROWSER_CONTEXT_RELEASE', (event, contextId) => {
 })
 
 handleRemoteCommand('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', function (event, contextId, guestInstanceId) {
-  const guestViewManager = require('@electron/internal/browser/guest-view-manager')
-  return valueToMeta(event.sender, contextId, guestViewManager.getGuest(guestInstanceId))
+  const guest = guestViewManager.getGuestForWebContents(guestInstanceId, event.sender)
+  return valueToMeta(event.sender, contextId, guest)
 })
 
 // Implements window.close()