fix: ensure the sandboxed preloads globals do not leak (#17712)
This commit is contained in:
parent
b3d8db6996
commit
be6fb7cb12
6 changed files with 157 additions and 6 deletions
61
BUILD.gn
61
BUILD.gn
|
@ -10,6 +10,7 @@ import("//tools/grit/repack.gni")
|
||||||
import("//tools/v8_context_snapshot/v8_context_snapshot.gni")
|
import("//tools/v8_context_snapshot/v8_context_snapshot.gni")
|
||||||
import("//v8/snapshot_toolchain.gni")
|
import("//v8/snapshot_toolchain.gni")
|
||||||
import("build/asar.gni")
|
import("build/asar.gni")
|
||||||
|
import("build/js_wrap.gni")
|
||||||
import("build/npm.gni")
|
import("build/npm.gni")
|
||||||
import("build/tsc.gni")
|
import("build/tsc.gni")
|
||||||
import("buildflags/buildflags.gni")
|
import("buildflags/buildflags.gni")
|
||||||
|
@ -70,7 +71,7 @@ npm_action("build_electron_definitions") {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
npm_action("atom_browserify_sandbox") {
|
npm_action("atom_browserify_sandbox_unwrapped") {
|
||||||
script = "browserify"
|
script = "browserify"
|
||||||
deps = [
|
deps = [
|
||||||
":build_electron_definitions",
|
":build_electron_definitions",
|
||||||
|
@ -79,7 +80,7 @@ npm_action("atom_browserify_sandbox") {
|
||||||
inputs = auto_filenames.sandbox_browserify_deps
|
inputs = auto_filenames.sandbox_browserify_deps
|
||||||
|
|
||||||
outputs = [
|
outputs = [
|
||||||
"$target_gen_dir/js2c/preload_bundle.js",
|
"$target_gen_dir/js2c/preload_bundle_unwrapped.js",
|
||||||
]
|
]
|
||||||
|
|
||||||
args = [
|
args = [
|
||||||
|
@ -94,12 +95,14 @@ npm_action("atom_browserify_sandbox") {
|
||||||
"-p",
|
"-p",
|
||||||
"tsconfig.electron.json",
|
"tsconfig.electron.json",
|
||||||
"]",
|
"]",
|
||||||
|
"--standalone",
|
||||||
|
"sandboxed_preload",
|
||||||
"-o",
|
"-o",
|
||||||
rebase_path(outputs[0]),
|
rebase_path(outputs[0]),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
npm_action("atom_browserify_isolated") {
|
npm_action("atom_browserify_isolated_unwrapped") {
|
||||||
script = "browserify"
|
script = "browserify"
|
||||||
deps = [
|
deps = [
|
||||||
":build_electron_definitions",
|
":build_electron_definitions",
|
||||||
|
@ -108,7 +111,7 @@ npm_action("atom_browserify_isolated") {
|
||||||
inputs = auto_filenames.isolated_browserify_deps
|
inputs = auto_filenames.isolated_browserify_deps
|
||||||
|
|
||||||
outputs = [
|
outputs = [
|
||||||
"$target_gen_dir/js2c/isolated_bundle.js",
|
"$target_gen_dir/js2c/isolated_bundle_unwrapped.js",
|
||||||
]
|
]
|
||||||
|
|
||||||
args = [
|
args = [
|
||||||
|
@ -121,12 +124,14 @@ npm_action("atom_browserify_isolated") {
|
||||||
"-p",
|
"-p",
|
||||||
"tsconfig.electron.json",
|
"tsconfig.electron.json",
|
||||||
"]",
|
"]",
|
||||||
|
"--standalone",
|
||||||
|
"isolated_preload",
|
||||||
"-o",
|
"-o",
|
||||||
rebase_path(outputs[0]),
|
rebase_path(outputs[0]),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
npm_action("atom_browserify_content_script") {
|
npm_action("atom_browserify_content_script_unwrapped") {
|
||||||
script = "browserify"
|
script = "browserify"
|
||||||
deps = [
|
deps = [
|
||||||
":build_electron_definitions",
|
":build_electron_definitions",
|
||||||
|
@ -135,7 +140,7 @@ npm_action("atom_browserify_content_script") {
|
||||||
inputs = auto_filenames.context_script_browserify_deps
|
inputs = auto_filenames.context_script_browserify_deps
|
||||||
|
|
||||||
outputs = [
|
outputs = [
|
||||||
"$target_gen_dir/js2c/content_script_bundle.js",
|
"$target_gen_dir/js2c/content_script_bundle_unwrapped.js",
|
||||||
]
|
]
|
||||||
|
|
||||||
args = [
|
args = [
|
||||||
|
@ -148,11 +153,55 @@ npm_action("atom_browserify_content_script") {
|
||||||
"-p",
|
"-p",
|
||||||
"tsconfig.electron.json",
|
"tsconfig.electron.json",
|
||||||
"]",
|
"]",
|
||||||
|
"--standalone",
|
||||||
|
"content_script_preload",
|
||||||
"-o",
|
"-o",
|
||||||
rebase_path(outputs[0]),
|
rebase_path(outputs[0]),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
js_wrap("atom_browserify_content_script") {
|
||||||
|
deps = [
|
||||||
|
":atom_browserify_content_script_unwrapped",
|
||||||
|
]
|
||||||
|
|
||||||
|
inputs = [
|
||||||
|
"$target_gen_dir/js2c/content_script_bundle_unwrapped.js",
|
||||||
|
]
|
||||||
|
|
||||||
|
outputs = [
|
||||||
|
"$target_gen_dir/js2c/content_script_bundle.js",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
js_wrap("atom_browserify_isolated") {
|
||||||
|
deps = [
|
||||||
|
":atom_browserify_isolated_unwrapped",
|
||||||
|
]
|
||||||
|
|
||||||
|
inputs = [
|
||||||
|
"$target_gen_dir/js2c/isolated_bundle_unwrapped.js",
|
||||||
|
]
|
||||||
|
|
||||||
|
outputs = [
|
||||||
|
"$target_gen_dir/js2c/isolated_bundle.js",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
js_wrap("atom_browserify_sandbox") {
|
||||||
|
deps = [
|
||||||
|
":atom_browserify_sandbox_unwrapped",
|
||||||
|
]
|
||||||
|
|
||||||
|
inputs = [
|
||||||
|
"$target_gen_dir/js2c/preload_bundle_unwrapped.js",
|
||||||
|
]
|
||||||
|
|
||||||
|
outputs = [
|
||||||
|
"$target_gen_dir/js2c/preload_bundle.js",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
copy("atom_js2c_copy") {
|
copy("atom_js2c_copy") {
|
||||||
sources = [
|
sources = [
|
||||||
"lib/common/asar.js",
|
"lib/common/asar.js",
|
||||||
|
|
19
build/js_wrap.gni
Normal file
19
build/js_wrap.gni
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
template("js_wrap") {
|
||||||
|
assert(defined(invoker.inputs), "Need input JS script")
|
||||||
|
assert(defined(invoker.outputs), "Need output JS script")
|
||||||
|
|
||||||
|
action(target_name) {
|
||||||
|
forward_variables_from(invoker,
|
||||||
|
[
|
||||||
|
"deps",
|
||||||
|
"public_deps",
|
||||||
|
"sources",
|
||||||
|
"inputs",
|
||||||
|
"outputs",
|
||||||
|
])
|
||||||
|
|
||||||
|
script = "//electron/build/js_wrap.py"
|
||||||
|
args = [ "--in" ] + rebase_path(invoker.inputs) + [ "--out" ] +
|
||||||
|
rebase_path(invoker.outputs)
|
||||||
|
}
|
||||||
|
}
|
19
build/js_wrap.py
Normal file
19
build/js_wrap.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import sys
|
||||||
|
|
||||||
|
in_start = sys.argv.index("--in") + 1
|
||||||
|
out_start = sys.argv.index("--out") + 1
|
||||||
|
|
||||||
|
in_bundles = sys.argv[in_start:out_start - 1]
|
||||||
|
out_bundles = sys.argv[out_start:]
|
||||||
|
|
||||||
|
if len(in_bundles) is not len(out_bundles):
|
||||||
|
print("--out and --in must provide the same number of arguments")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
for i in range(len(in_bundles)):
|
||||||
|
in_bundle = in_bundles[i]
|
||||||
|
out_path = out_bundles[i]
|
||||||
|
with open(in_bundle, 'r') as f:
|
||||||
|
lines = ["(function(){var exports={},module={exports};"] + f.readlines() + ["})();"]
|
||||||
|
with open(out_path, 'w') as out_f:
|
||||||
|
out_f.writelines(lines)
|
|
@ -1357,6 +1357,48 @@ describe('BrowserWindow module', () => {
|
||||||
afterEach(() => { ipcMain.removeAllListeners('answer') })
|
afterEach(() => { ipcMain.removeAllListeners('answer') })
|
||||||
|
|
||||||
describe('"preload" option', () => {
|
describe('"preload" option', () => {
|
||||||
|
const doesNotLeakSpec = (name, webPrefs) => {
|
||||||
|
it(name, async function () {
|
||||||
|
w.destroy()
|
||||||
|
w = new BrowserWindow({
|
||||||
|
webPreferences: {
|
||||||
|
...webPrefs,
|
||||||
|
preload: path.resolve(fixtures, 'module', 'empty.js')
|
||||||
|
},
|
||||||
|
show: false
|
||||||
|
})
|
||||||
|
const leakResult = emittedOnce(ipcMain, 'leak-result')
|
||||||
|
w.loadFile(path.join(fixtures, 'api', 'no-leak.html'))
|
||||||
|
const [, result] = await leakResult
|
||||||
|
console.log(result)
|
||||||
|
expect(result).to.have.property('require', 'undefined')
|
||||||
|
expect(result).to.have.property('exports', 'undefined')
|
||||||
|
expect(result).to.have.property('windowExports', 'undefined')
|
||||||
|
expect(result).to.have.property('windowPreload', 'undefined')
|
||||||
|
expect(result).to.have.property('windowRequire', 'undefined')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
doesNotLeakSpec('does not leak require', {
|
||||||
|
nodeIntegration: false,
|
||||||
|
sandbox: false,
|
||||||
|
contextIsolation: false
|
||||||
|
})
|
||||||
|
doesNotLeakSpec('does not leak require when sandbox is enabled', {
|
||||||
|
nodeIntegration: false,
|
||||||
|
sandbox: true,
|
||||||
|
contextIsolation: false
|
||||||
|
})
|
||||||
|
doesNotLeakSpec('does not leak require when context isolation is enabled', {
|
||||||
|
nodeIntegration: false,
|
||||||
|
sandbox: false,
|
||||||
|
contextIsolation: true
|
||||||
|
})
|
||||||
|
doesNotLeakSpec('does not leak require when context isolation and sandbox are enabled', {
|
||||||
|
nodeIntegration: false,
|
||||||
|
sandbox: true,
|
||||||
|
contextIsolation: true
|
||||||
|
})
|
||||||
|
|
||||||
it('loads the script before other scripts in window', async () => {
|
it('loads the script before other scripts in window', async () => {
|
||||||
const preload = path.join(fixtures, 'module', 'set-global.js')
|
const preload = path.join(fixtures, 'module', 'set-global.js')
|
||||||
w.destroy()
|
w.destroy()
|
||||||
|
|
17
spec/fixtures/api/no-leak.html
vendored
Normal file
17
spec/fixtures/api/no-leak.html
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Document</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
window.postMessage({
|
||||||
|
require: typeof require,
|
||||||
|
exports: typeof exports,
|
||||||
|
windowRequire: typeof window.require,
|
||||||
|
windowExports: typeof window.exports,
|
||||||
|
windowPreload: typeof window.sandboxed_preload,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
5
spec/fixtures/module/empty.js
vendored
Normal file
5
spec/fixtures/module/empty.js
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
const { ipcRenderer } = require('electron')
|
||||||
|
|
||||||
|
window.addEventListener('message', (event) => {
|
||||||
|
ipcRenderer.send('leak-result', event.data)
|
||||||
|
})
|
Loading…
Add table
Reference in a new issue