feat: add additional remote APIs filtering (#16293)

This commit is contained in:
Milan Burda 2019-01-08 23:27:56 +01:00 committed by Jeremy Apthorp
parent 6436a12d7f
commit 349a3c20ae
8 changed files with 323 additions and 69 deletions

View file

@ -435,6 +435,52 @@ Emitted when `remote.getGlobal()` is called in the renderer process of `webConte
Calling `event.preventDefault()` will prevent the global from being returned. Calling `event.preventDefault()` will prevent the global from being returned.
Custom value can be returned by setting `event.returnValue`. Custom value can be returned by setting `event.returnValue`.
### Event: 'remote-get-builtin'
Returns:
* `event` Event
* `webContents` [WebContents](web-contents.md)
* `moduleName` String
Emitted when `remote.getBuiltin()` is called in the renderer process of `webContents`.
Calling `event.preventDefault()` will prevent the module from being returned.
Custom value can be returned by setting `event.returnValue`.
### Event: 'remote-get-current-window'
Returns:
* `event` Event
* `webContents` [WebContents](web-contents.md)
Emitted when `remote.getCurrentWindow()` is called in the renderer process of `webContents`.
Calling `event.preventDefault()` will prevent the object from being returned.
Custom value can be returned by setting `event.returnValue`.
### Event: 'remote-get-current-web-contents'
Returns:
* `event` Event
* `webContents` [WebContents](web-contents.md)
Emitted when `remote.getCurrentWebContents()` is called in the renderer process of `webContents`.
Calling `event.preventDefault()` will prevent the object from being returned.
Custom value can be returned by setting `event.returnValue`.
### Event: 'remote-get-guest-web-contents'
Returns:
* `event` Event
* `webContents` [WebContents](web-contents.md)
* `guestWebContents` [WebContents](web-contents.md)
Emitted when `<webview>.getWebContents()` is called in the renderer process of `webContents`.
Calling `event.preventDefault()` will prevent the object from being returned.
Custom value can be returned by setting `event.returnValue`.
## Methods ## Methods
The `app` object has the following methods: The `app` object has the following methods:

View file

@ -694,6 +694,48 @@ Emitted when `remote.getGlobal()` is called in the renderer process.
Calling `event.preventDefault()` will prevent the global from being returned. Calling `event.preventDefault()` will prevent the global from being returned.
Custom value can be returned by setting `event.returnValue`. Custom value can be returned by setting `event.returnValue`.
#### Event: 'remote-get-builtin'
Returns:
* `event` Event
* `moduleName` String
Emitted when `remote.getBuiltin()` is called in the renderer process.
Calling `event.preventDefault()` will prevent the module from being returned.
Custom value can be returned by setting `event.returnValue`.
#### Event: 'remote-get-current-window'
Returns:
* `event` Event
Emitted when `remote.getCurrentWindow()` is called in the renderer process.
Calling `event.preventDefault()` will prevent the object from being returned.
Custom value can be returned by setting `event.returnValue`.
#### Event: 'remote-get-current-web-contents'
Returns:
* `event` Event
Emitted when `remote.getCurrentWebContents()` is called in the renderer process.
Calling `event.preventDefault()` will prevent the object from being returned.
Custom value can be returned by setting `event.returnValue`.
#### Event: 'remote-get-guest-web-contents'
Returns:
* `event` Event
* `guestWebContents` [WebContents](web-contents.md)
Emitted when `<webview>.getWebContents()` is called in the renderer process.
Calling `event.preventDefault()` will prevent the object from being returned.
Custom value can be returned by setting `event.returnValue`.
### Instance Methods ### Instance Methods
#### `contents.loadURL(url[, options])` #### `contents.loadURL(url[, options])`

View file

@ -377,17 +377,21 @@ WebContents.prototype._init = function () {
}) })
}) })
this.on('desktop-capturer-get-sources', (event, ...args) => { const forwardedEvents = [
app.emit('desktop-capturer-get-sources', event, this, ...args) 'desktop-capturer-get-sources',
}) 'remote-require',
'remote-get-global',
'remote-get-builtin',
'remote-get-current-window',
'remote-get-current-web-contents',
'remote-get-guest-web-contents'
]
this.on('remote-require', (event, ...args) => { for (const eventName of forwardedEvents) {
app.emit('remote-require', event, this, ...args) this.on(eventName, (event, ...args) => {
}) app.emit(eventName, event, this, ...args)
})
this.on('remote-get-global', (event, ...args) => { }
app.emit('remote-get-global', event, this, ...args)
})
deprecate.event(this, 'did-get-response-details', '-did-get-response-details') deprecate.event(this, 'did-get-response-details', '-did-get-response-details')
deprecate.event(this, 'did-get-redirect-request', '-did-get-redirect-request') deprecate.event(this, 'did-get-redirect-request', '-did-get-redirect-request')

View file

@ -298,42 +298,75 @@ handleRemoteCommand('ELECTRON_BROWSER_REQUIRE', function (event, contextId, modu
const customEvent = eventBinding.createWithSender(event.sender) const customEvent = eventBinding.createWithSender(event.sender)
event.sender.emit('remote-require', customEvent, moduleName) event.sender.emit('remote-require', customEvent, moduleName)
if (customEvent.defaultPrevented) { if (customEvent.returnValue === undefined) {
if (typeof customEvent.returnValue === 'undefined') { if (customEvent.defaultPrevented) {
throw new Error(`Invalid module: ${moduleName}`) throw new Error(`Blocked remote.require('${moduleName}')`)
} else {
customEvent.returnValue = process.mainModule.require(moduleName)
} }
} else {
customEvent.returnValue = process.mainModule.require(moduleName)
} }
return valueToMeta(event.sender, contextId, customEvent.returnValue) return valueToMeta(event.sender, contextId, customEvent.returnValue)
}) })
handleRemoteCommand('ELECTRON_BROWSER_GET_BUILTIN', function (event, contextId, module) { handleRemoteCommand('ELECTRON_BROWSER_GET_BUILTIN', function (event, contextId, moduleName) {
return valueToMeta(event.sender, contextId, electron[module]) const customEvent = eventBinding.createWithSender(event.sender)
event.sender.emit('remote-get-builtin', customEvent, moduleName)
if (customEvent.returnValue === undefined) {
if (customEvent.defaultPrevented) {
throw new Error(`Blocked remote.getBuiltin('${moduleName}')`)
} else {
customEvent.returnValue = electron[moduleName]
}
}
return valueToMeta(event.sender, contextId, customEvent.returnValue)
}) })
handleRemoteCommand('ELECTRON_BROWSER_GLOBAL', function (event, contextId, globalName) { handleRemoteCommand('ELECTRON_BROWSER_GLOBAL', function (event, contextId, globalName) {
const customEvent = eventBinding.createWithSender(event.sender) const customEvent = eventBinding.createWithSender(event.sender)
event.sender.emit('remote-get-global', customEvent, globalName) event.sender.emit('remote-get-global', customEvent, globalName)
if (customEvent.defaultPrevented) { if (customEvent.returnValue === undefined) {
if (typeof customEvent.returnValue === 'undefined') { if (customEvent.defaultPrevented) {
throw new Error(`Invalid global: ${globalName}`) throw new Error(`Blocked remote.getGlobal('${globalName}')`)
} else {
customEvent.returnValue = global[globalName]
} }
} else {
customEvent.returnValue = global[globalName]
} }
return valueToMeta(event.sender, contextId, customEvent.returnValue) return valueToMeta(event.sender, contextId, customEvent.returnValue)
}) })
handleRemoteCommand('ELECTRON_BROWSER_CURRENT_WINDOW', function (event, contextId) { handleRemoteCommand('ELECTRON_BROWSER_CURRENT_WINDOW', function (event, contextId) {
return valueToMeta(event.sender, contextId, event.sender.getOwnerBrowserWindow()) const customEvent = eventBinding.createWithSender(event.sender)
event.sender.emit('remote-get-current-window', customEvent)
if (customEvent.returnValue === undefined) {
if (customEvent.defaultPrevented) {
throw new Error('Blocked remote.getCurrentWindow()')
} else {
customEvent.returnValue = event.sender.getOwnerBrowserWindow()
}
}
return valueToMeta(event.sender, contextId, customEvent.returnValue)
}) })
handleRemoteCommand('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', function (event, contextId) { handleRemoteCommand('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', function (event, contextId) {
return valueToMeta(event.sender, contextId, event.sender) const customEvent = eventBinding.createWithSender(event.sender)
event.sender.emit('remote-get-current-web-contents', customEvent)
if (customEvent.returnValue === undefined) {
if (customEvent.defaultPrevented) {
throw new Error('Blocked remote.getCurrentWebContents()')
} else {
customEvent.returnValue = event.sender
}
}
return valueToMeta(event.sender, contextId, customEvent.returnValue)
}) })
handleRemoteCommand('ELECTRON_BROWSER_CONSTRUCTOR', function (event, contextId, id, args) { handleRemoteCommand('ELECTRON_BROWSER_CONSTRUCTOR', function (event, contextId, id, args) {
@ -413,7 +446,19 @@ 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 guest = guestViewManager.getGuestForWebContents(guestInstanceId, event.sender) const guest = guestViewManager.getGuestForWebContents(guestInstanceId, event.sender)
return valueToMeta(event.sender, contextId, guest)
const customEvent = eventBinding.createWithSender(event.sender)
event.sender.emit('remote-get-guest-web-contents', customEvent, guest)
if (customEvent.returnValue === undefined) {
if (customEvent.defaultPrevented) {
throw new Error(`Blocked remote.getGuestForWebContents()`)
} else {
customEvent.returnValue = guest
}
}
return valueToMeta(event.sender, contextId, customEvent.returnValue)
}) })
// Implements window.close() // Implements window.close()

View file

@ -379,51 +379,103 @@ describe('app module', () => {
w = new BrowserWindow({ show: false }) w = new BrowserWindow({ show: false })
}) })
it('should emit desktop-capturer-get-sources event when desktopCapturer.getSources() is invoked', (done) => { it('should emit desktop-capturer-get-sources event when desktopCapturer.getSources() is invoked', async () => {
app.once('desktop-capturer-get-sources', (event, webContents) => {
expect(webContents).to.equal(w.webContents)
done()
})
w = new BrowserWindow({ w = new BrowserWindow({
show: false, show: false,
webPreferences: { webPreferences: {
nodeIntegration: true nodeIntegration: true
} }
}) })
w.loadURL('about:blank') await w.loadURL('about:blank')
const promise = emittedOnce(app, 'desktop-capturer-get-sources')
w.webContents.executeJavaScript(`require('electron').desktopCapturer.getSources({ types: ['screen'] }, () => {})`) w.webContents.executeJavaScript(`require('electron').desktopCapturer.getSources({ types: ['screen'] }, () => {})`)
const [, webContents] = await promise
expect(webContents).to.equal(w.webContents)
}) })
it('should emit remote-require event when remote.require() is invoked', (done) => { it('should emit remote-require event when remote.require() is invoked', async () => {
app.once('remote-require', (event, webContents, moduleName) => {
expect(webContents).to.equal(w.webContents)
expect(moduleName).to.equal('test')
done()
})
w = new BrowserWindow({ w = new BrowserWindow({
show: false, show: false,
webPreferences: { webPreferences: {
nodeIntegration: true nodeIntegration: true
} }
}) })
w.loadURL('about:blank') await w.loadURL('about:blank')
const promise = emittedOnce(app, 'remote-require')
w.webContents.executeJavaScript(`require('electron').remote.require('test')`) w.webContents.executeJavaScript(`require('electron').remote.require('test')`)
const [, webContents, moduleName] = await promise
expect(webContents).to.equal(w.webContents)
expect(moduleName).to.equal('test')
}) })
it('should emit remote-get-global event when remote.getGlobal() is invoked', (done) => { it('should emit remote-get-global event when remote.getGlobal() is invoked', async () => {
app.once('remote-get-global', (event, webContents, globalName) => {
expect(webContents).to.equal(w.webContents)
expect(globalName).to.equal('test')
done()
})
w = new BrowserWindow({ w = new BrowserWindow({
show: false, show: false,
webPreferences: { webPreferences: {
nodeIntegration: true nodeIntegration: true
} }
}) })
w.loadURL('about:blank') await w.loadURL('about:blank')
const promise = emittedOnce(app, 'remote-get-global')
w.webContents.executeJavaScript(`require('electron').remote.getGlobal('test')`) w.webContents.executeJavaScript(`require('electron').remote.getGlobal('test')`)
const [, webContents, globalName] = await promise
expect(webContents).to.equal(w.webContents)
expect(globalName).to.equal('test')
})
it('should emit remote-get-builtin event when remote.getBuiltin() is invoked', async () => {
w = new BrowserWindow({
show: false,
webPreferences: {
nodeIntegration: true
}
})
await w.loadURL('about:blank')
const promise = emittedOnce(app, 'remote-get-builtin')
w.webContents.executeJavaScript(`require('electron').remote.app`)
const [, webContents, moduleName] = await promise
expect(webContents).to.equal(w.webContents)
expect(moduleName).to.equal('app')
})
it('should emit remote-get-current-window event when remote.getCurrentWindow() is invoked', async () => {
w = new BrowserWindow({
show: false,
webPreferences: {
nodeIntegration: true
}
})
await w.loadURL('about:blank')
const promise = emittedOnce(app, 'remote-get-current-window')
w.webContents.executeJavaScript(`require('electron').remote.getCurrentWindow()`)
const [, webContents] = await promise
expect(webContents).to.equal(w.webContents)
})
it('should emit remote-get-current-web-contents event when remote.getCurrentWebContents() is invoked', async () => {
w = new BrowserWindow({
show: false,
webPreferences: {
nodeIntegration: true
}
})
await w.loadURL('about:blank')
const promise = emittedOnce(app, 'remote-get-current-web-contents')
w.webContents.executeJavaScript(`require('electron').remote.getCurrentWebContents()`)
const [, webContents] = await promise
expect(webContents).to.equal(w.webContents)
}) })
}) })

View file

@ -28,7 +28,7 @@ describe('remote module', () => {
afterEach(() => closeWindow(w).then(() => { w = null })) afterEach(() => closeWindow(w).then(() => { w = null }))
describe('remote.getGlobal', () => { describe('remote.getGlobal filtering', () => {
it('can return custom values', () => { it('can return custom values', () => {
ipcRenderer.send('handle-next-remote-get-global', { test: 'Hello World!' }) ipcRenderer.send('handle-next-remote-get-global', { test: 'Hello World!' })
expect(remote.getGlobal('test')).to.be.equal('Hello World!') expect(remote.getGlobal('test')).to.be.equal('Hello World!')
@ -36,11 +36,23 @@ describe('remote module', () => {
it('throws when no returnValue set', () => { it('throws when no returnValue set', () => {
ipcRenderer.send('handle-next-remote-get-global') ipcRenderer.send('handle-next-remote-get-global')
expect(() => remote.getGlobal('process')).to.throw('Invalid global: process') expect(() => remote.getGlobal('test')).to.throw(`Blocked remote.getGlobal('test')`)
}) })
}) })
describe('remote.require', () => { describe('remote.getBuiltin filtering', () => {
it('can return custom values', () => {
ipcRenderer.send('handle-next-remote-get-builtin', { test: 'Hello World!' })
expect(remote.getBuiltin('test')).to.be.equal('Hello World!')
})
it('throws when no returnValue set', () => {
ipcRenderer.send('handle-next-remote-get-builtin')
expect(() => remote.getBuiltin('test')).to.throw(`Blocked remote.getBuiltin('test')`)
})
})
describe('remote.require filtering', () => {
it('can return custom values', () => { it('can return custom values', () => {
ipcRenderer.send('handle-next-remote-require', { test: 'Hello World!' }) ipcRenderer.send('handle-next-remote-require', { test: 'Hello World!' })
expect(remote.require('test')).to.be.equal('Hello World!') expect(remote.require('test')).to.be.equal('Hello World!')
@ -48,9 +60,11 @@ describe('remote module', () => {
it('throws when no returnValue set', () => { it('throws when no returnValue set', () => {
ipcRenderer.send('handle-next-remote-require') ipcRenderer.send('handle-next-remote-require')
expect(() => remote.require('electron')).to.throw('Invalid module: electron') expect(() => remote.require('test')).to.throw(`Blocked remote.require('test')`)
}) })
})
describe('remote.require', () => {
it('should returns same object for the same module', () => { it('should returns same object for the same module', () => {
const dialog1 = remote.require('electron') const dialog1 = remote.require('electron')
const dialog2 = remote.require('electron') const dialog2 = remote.require('electron')
@ -451,6 +465,30 @@ describe('remote module', () => {
}) })
}) })
describe('remote.getCurrentWindow filtering', () => {
it('can return custom value', () => {
ipcRenderer.send('handle-next-remote-get-current-window', 'Hello World!')
expect(remote.getCurrentWindow()).to.be.equal('Hello World!')
})
it('throws when no returnValue set', () => {
ipcRenderer.send('handle-next-remote-get-current-window')
expect(() => remote.getCurrentWindow()).to.throw('Blocked remote.getCurrentWindow()')
})
})
describe('remote.getCurrentWebContents filtering', () => {
it('can return custom value', () => {
ipcRenderer.send('handle-next-remote-get-current-web-contents', 'Hello World!')
expect(remote.getCurrentWebContents()).to.be.equal('Hello World!')
})
it('throws when no returnValue set', () => {
ipcRenderer.send('handle-next-remote-get-current-web-contents')
expect(() => remote.getCurrentWebContents()).to.throw('Blocked remote.getCurrentWebContents()')
})
})
describe('remote class', () => { describe('remote class', () => {
const cl = remote.require(path.join(fixtures, 'module', 'class.js')) const cl = remote.require(path.join(fixtures, 'module', 'class.js'))
const base = cl.base const base = cl.base

View file

@ -235,29 +235,38 @@ app.on('ready', function () {
}) })
}) })
ipcMain.on('handle-next-desktop-capturer-get-sources', function (event) { for (const eventName of [
event.sender.once('desktop-capturer-get-sources', (event) => { 'remote-require',
event.preventDefault() 'remote-get-global',
'remote-get-builtin'
]) {
ipcMain.on(`handle-next-${eventName}`, function (event, valuesMap = {}) {
event.sender.once(eventName, (event, name) => {
if (valuesMap.hasOwnProperty(name)) {
event.returnValue = valuesMap[name]
} else {
event.preventDefault()
}
})
}) })
}) }
ipcMain.on('handle-next-remote-require', function (event, modulesMap = {}) { for (const eventName of [
event.sender.once('remote-require', (event, moduleName) => { 'desktop-capturer-get-sources',
event.preventDefault() 'remote-get-current-window',
if (modulesMap.hasOwnProperty(moduleName)) { 'remote-get-current-web-contents',
event.returnValue = modulesMap[moduleName] 'remote-get-guest-web-contents'
} ]) {
ipcMain.on(`handle-next-${eventName}`, function (event, returnValue) {
event.sender.once(eventName, (event) => {
if (returnValue) {
event.returnValue = returnValue
} else {
event.preventDefault()
}
})
}) })
}) }
ipcMain.on('handle-next-remote-get-global', function (event, globalsMap = {}) {
event.sender.once('remote-get-global', (event, globalName) => {
event.preventDefault()
if (globalsMap.hasOwnProperty(globalName)) {
event.returnValue = globalsMap[globalName]
}
})
})
ipcMain.on('set-client-certificate-option', function (event, skip) { ipcMain.on('set-client-certificate-option', function (event, skip) {
app.once('select-client-certificate', function (event, webContents, url, list, callback) { app.once('select-client-certificate', function (event, webContents, url, list, callback) {

View file

@ -1188,6 +1188,24 @@ describe('<webview> tag', function () {
}) })
}) })
describe('<webview>.getWebContents filtering', () => {
it('can return custom value', async () => {
const src = 'about:blank'
await loadWebView(webview, { src })
ipcRenderer.send('handle-next-remote-get-guest-web-contents', 'Hello World!')
expect(webview.getWebContents()).to.be.equal('Hello World!')
})
it('throws when no returnValue set', async () => {
const src = 'about:blank'
await loadWebView(webview, { src })
ipcRenderer.send('handle-next-remote-get-guest-web-contents')
expect(() => webview.getWebContents()).to.throw('Blocked remote.getGuestForWebContents()')
})
})
// FIXME(deepak1556): Ch69 follow up. // FIXME(deepak1556): Ch69 follow up.
xdescribe('document.visibilityState/hidden', () => { xdescribe('document.visibilityState/hidden', () => {
afterEach(() => { afterEach(() => {