security: improve IPC validation in guest-view-manager

This commit is contained in:
Milan Burda 2019-01-03 19:31:10 +01:00
parent 876064036d
commit a0cdcc5f8d
2 changed files with 39 additions and 18 deletions

View file

@ -184,9 +184,12 @@ const attachGuest = function (event, embedderFrameId, elementInstanceId, guestIn
const guestInstance = guestInstances[guestInstanceId] const guestInstance = guestInstances[guestInstanceId]
// If this isn't a valid guest instance then do nothing. // If this isn't a valid guest instance then do nothing.
if (!guestInstance) { if (!guestInstance) {
return throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`)
} }
const { guest } = guestInstance 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 this guest is already attached to an element then remove it
if (guestInstance.elementInstanceId) { 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) { handleMessage('ELECTRON_GUEST_VIEW_MANAGER_DESTROY_GUEST', function (event, guestInstanceId) {
const guest = getGuest(guestInstanceId) try {
if (guest) { const guest = getGuestForWebContents(guestInstanceId, event.sender)
guest.destroy() guest.destroy()
} catch (error) {
console.error(`Guest destroy failed: ${error}`)
} }
}) })
handleMessage('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) 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) { // this message is sent by the actual <webview>
event.sender.emit('focus-change', {}, focus, guestInstanceId) 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) { handleMessage('ELECTRON_GUEST_VIEW_MANAGER_ASYNC_CALL', function (event, requestId, guestInstanceId, method, args, hasCallback) {
new Promise(resolve => { new Promise(resolve => {
const guest = getGuest(guestInstanceId) const guest = getGuestForWebContents(guestInstanceId, event.sender)
if (guest.hostWebContents !== event.sender) {
throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`)
}
if (!asyncMethods.has(method)) { if (!asyncMethods.has(method)) {
throw new Error(`Invalid method: ${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) { handleMessage('ELECTRON_GUEST_VIEW_MANAGER_SYNC_CALL', function (event, guestInstanceId, method, args) {
try { try {
const guest = getGuest(guestInstanceId) const guest = getGuestForWebContents(guestInstanceId, event.sender)
if (guest.hostWebContents !== event.sender) {
throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`)
}
if (!syncMethods.has(method)) { if (!syncMethods.has(method)) {
throw new Error(`Invalid method: ${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. // Returns WebContents from its guest id.
const getGuest = function (guestInstanceId) { const getGuest = function (guestInstanceId) {
const guestInstance = guestInstances[guestInstanceId] const guestInstance = guestInstances[guestInstanceId]
@ -415,5 +436,4 @@ const getEmbedder = function (guestInstanceId) {
if (guestInstance != null) return guestInstance.embedder if (guestInstance != null) return guestInstance.embedder
} }
exports.getGuest = getGuest exports.getGuestForWebContents = getGuestForWebContents
exports.getEmbedder = getEmbedder

View file

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