security: only handle related IPCs when <webview> tag is enabled (#15859)
* refactor: move guest-view-manager related IPC handling out of rpc-server * feat: only handle related IPCs when <webview> tag is enabled
This commit is contained in:
parent
8cca1c987b
commit
8483cb4aa7
4 changed files with 63 additions and 43 deletions
|
@ -3,6 +3,7 @@
|
||||||
const { webContents } = require('electron')
|
const { webContents } = require('electron')
|
||||||
const ipcMain = require('@electron/internal/browser/ipc-main-internal')
|
const ipcMain = require('@electron/internal/browser/ipc-main-internal')
|
||||||
const parseFeaturesString = require('@electron/internal/common/parse-features-string')
|
const parseFeaturesString = require('@electron/internal/common/parse-features-string')
|
||||||
|
const errorUtils = require('@electron/internal/common/error-utils')
|
||||||
|
|
||||||
// Doesn't exist in early initialization.
|
// Doesn't exist in early initialization.
|
||||||
let webViewManager = null
|
let webViewManager = null
|
||||||
|
@ -319,29 +320,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))
|
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)
|
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)
|
const guest = getGuest(guestInstanceId)
|
||||||
if (guest) {
|
if (guest) {
|
||||||
guest.destroy()
|
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)
|
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)
|
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.
|
// Returns WebContents from its guest id.
|
||||||
const getGuest = function (guestInstanceId) {
|
const getGuest = function (guestInstanceId) {
|
||||||
const guestInstance = guestInstances[guestInstanceId]
|
const guestInstance = guestInstances[guestInstanceId]
|
||||||
|
|
|
@ -413,40 +413,6 @@ handleRemoteCommand('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', function (event, cont
|
||||||
return valueToMeta(event.sender, contextId, guestViewManager.getGuest(guestInstanceId))
|
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()
|
// Implements window.close()
|
||||||
ipcMain.on('ELECTRON_BROWSER_WINDOW_CLOSE', function (event) {
|
ipcMain.on('ELECTRON_BROWSER_WINDOW_CLOSE', function (event) {
|
||||||
const window = event.sender.getOwnerBrowserWindow()
|
const window = event.sender.getOwnerBrowserWindow()
|
||||||
|
|
|
@ -186,7 +186,7 @@ class SrcAttribute extends WebViewAttribute {
|
||||||
const method = 'loadURL'
|
const method = 'loadURL'
|
||||||
const args = [this.getValue(), opts]
|
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) {
|
if (error) {
|
||||||
throw errorUtils.deserialize(error)
|
throw errorUtils.deserialize(error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -306,7 +306,7 @@ const registerWebViewElement = function () {
|
||||||
// Forward proto.foo* method calls to WebViewImpl.foo*.
|
// Forward proto.foo* method calls to WebViewImpl.foo*.
|
||||||
const createBlockHandler = function (method) {
|
const createBlockHandler = function (method) {
|
||||||
return function (...args) {
|
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) {
|
if (error) {
|
||||||
throw errorUtils.deserialize(error)
|
throw errorUtils.deserialize(error)
|
||||||
} else {
|
} else {
|
||||||
|
@ -322,14 +322,14 @@ const registerWebViewElement = function () {
|
||||||
return function (...args) {
|
return function (...args) {
|
||||||
const callback = (typeof args[args.length - 1] === 'function') ? args.pop() : null
|
const callback = (typeof args[args.length - 1] === 'function') ? args.pop() : null
|
||||||
const requestId = getNextId()
|
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 (error == null) {
|
||||||
if (callback) callback(result)
|
if (callback) callback(result)
|
||||||
} else {
|
} else {
|
||||||
throw errorUtils.deserialize(error)
|
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) {
|
for (const method of nonblockMethods) {
|
||||||
|
|
Loading…
Reference in a new issue