fix(javascript): retain stored receiver capture authority

This commit is contained in:
morluto committed 2026-10-24 08:22:14 +08:00
1 parent d3a33fcfca
commit eb721ebbf1
3 files changed
+284 -23

No files matched your search

@@ -1,3 +1,4 @@
import { runInNewContext } from "node:vm";
import { describe, expect, it } from "vitest";
import { analyzeJavaScriptStaticSource } from "./javascriptStaticAnalysis.js";
@@ -69,6 +70,8 @@ describe("Electron constant option evidence", () => {
"const { webPreferences: alias } = options; mutate(alias);",
"Object.assign(options, unknown);",
"const box = {}; box.options = options; box.options.webPreferences.sandbox = false;",
"const box = {}; box.options = options; const saved = box.options; box.options = {}; saved.webPreferences.sandbox = false;",
"const values = [options]; values[Symbol.iterator] = input; const box = {}; [box.options] = values; box.options.webPreferences.sandbox = false;",
"options = replacement;",
])("keeps changed or escaped constant options unknown: %s", (mutation) => {
const analysis = analyzeJavaScriptStaticSource(`
@@ -87,6 +90,77 @@ describe("Electron constant option evidence", () => {
]);
});
it.each([
"const box = {}; box.options = options; box.options = { webPreferences: { sandbox: false } }; box.options.webPreferences.sandbox = false;",
"let box = { options: { webPreferences: { sandbox: false } } }; const saved = box; box = {}; box.options = options; saved.options.webPreferences.sandbox = false;",
"const values = [{ webPreferences: { sandbox: false } }]; values[Symbol.iterator] = input; const box = {}; [box.options] = values; box.options.webPreferences.sandbox = false;",
])(
"keeps independent options intact after stored receiver replacement: %s",
(mutation) => {
const analysis = analyzeJavaScriptStaticSource(`
const { BrowserWindow } = require("electron");
const options = { webPreferences: { sandbox: true } };
${mutation}
new BrowserWindow(options);
`);
expect(analysis.electron.browser_windows).toMatchObject([
{
options_status: "object-literal",
web_preferences_status: "object-literal",
web_preferences: [
{ name: "sandbox", value: { status: "literal", value: true } },
],
},
]);
},
);
it.each([
["inner.current = second;", [true, false], ["object-literal", "dynamic"]],
[
"source.inner = { current: second };",
[false, true],
["dynamic", "object-literal"],
],
] as const)(
"preserves shallow-copy capture depth across later writes: %s",
(replacement, runtime, statuses) => {
const body = `
const first = { webPreferences: { sandbox: true } };
const second = { webPreferences: { sandbox: true } };
const inner = { current: first };
const source = { inner };
const box = {};
({...box.options} = source);
${replacement}
box.options.inner.current.webPreferences.sandbox = false;
`;
expect(
Array.from(
runInNewContext(
`${body};[first.webPreferences.sandbox, second.webPreferences.sandbox]`,
),
),
).toEqual(runtime);
const analysis = analyzeJavaScriptStaticSource(`
const { BrowserWindow } = require("electron");
${body}
new BrowserWindow(first);
new BrowserWindow(second);
`);
expect(
analysis.electron.browser_windows.map(
({ options_status }) => options_status,
),
).toEqual(statuses);
const intact =
analysis.electron.browser_windows[statuses.indexOf("object-literal")];
expect(intact?.web_preferences).toMatchObject([
{ name: "sandbox", value: { status: "literal", value: true } },
]);
},
);
it.each([
"new BrowserWindow(options); const options = { webPreferences: { sandbox: true } };",
"const options = alias; const alias = { webPreferences: { sandbox: true } }; new BrowserWindow(options);",