fix(javascript): follow actual receivers in nested intrinsic calls (#1912)

Consume arguments per generic function invocation rather than property-chain
depth. Preserve the callable receiver through call/apply, Reflect.apply and
bound invocation. Cover nested mutations and fresh targets through source
analysis and inert production CLI/MCP fixtures.

Fixes #1909.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2026-10-12 01:22:26 +08:00
1 parent d28b41ea4e
commit 9d8e42bdd6
4 files changed
+99 -45

No files matched your search

@@ -534,6 +534,50 @@ describe("Electron module interop capture and scope", () => {
});
});
describe("Electron module interop generic invocations", () => {
it.each([
'const change = Function.prototype.call.bind(Object.defineProperty); change(null,Object,"create",{value:customCreate});',
'const call = Function.prototype.call; const change = call.bind(Object.defineProperty); change(null,Object,"create",{value:customCreate});',
'const change = Function.prototype.call.bind(Object.defineProperty,null,Object,"create"); change({value:customCreate});',
'Function.prototype.call.apply(Object.defineProperty,[null,Object,"create",{value:customCreate}]);',
'Reflect.apply(Function.prototype.call,Object.defineProperty,[null,Object,"create",{value:customCreate}]);',
'const change = Function.prototype.apply.bind(Object.defineProperty); change(null,[Object,"create",{value:customCreate}]);',
'Reflect.apply(Function.prototype.apply,Object.defineProperty,[null,[Object,"create",{value:customCreate}]]);',
'const first = Function.prototype.call.bind(Object.defineProperty,null,Object,"create"); const change = first.bind(fake); change({value:customCreate});',
"const change = Function.prototype.call.bind(Object.defineProperty); change(null,...args);",
"const change = Function.prototype.apply.bind(Object.defineProperty); change(null,args);",
'const change = Function.prototype.call.bind(Object.defineProperty,null,...args); change(metadata,"x",{value:1});',
'Function.prototype.call.call.call(Object.defineProperty,null,Object,"create",{value:customCreate});',
'Object.defineProperty.call.call(Object.defineProperty,null,Object,"create",{value:customCreate});',
'Function.prototype.apply.apply(Object.defineProperty,[null,[Object,"create",{value:customCreate}]]);',
'const change = Object.defineProperty.call.bind(Object.defineProperty); change(null,Object,"create",{value:customCreate});',
'const change = Function.prototype.call.call.bind(Object.defineProperty,null,Object,"create"); change({value:customCreate});',
])("follows the callable receiver of a generic invocation: %s", (write) => {
expect(
analyzeJavaScriptStaticSource(
`${write}\n${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP} ${WINDOW}`,
).electron.browser_windows,
).toEqual([]);
});
it.each([
'const change = Function.prototype.call.bind(Object.defineProperty); change(null,metadata,"x",{value:1});',
'const change = Function.prototype.call.bind(Object.defineProperty,null,metadata,"x"); change({value:1});',
'Function.prototype.call.call(Object.defineProperty,null,metadata,"x",{value:1});',
'const change = Function.prototype.apply.bind(Object.defineProperty); change(null,[metadata,"x",{value:1}]);',
'Reflect.apply(Function.prototype.apply,Object.defineProperty,[null,[metadata,"x",{value:1}]]);',
'Function.prototype.call.call.call(Object.defineProperty,null,metadata,"x",{value:1});',
'Function.prototype.apply.apply(Object.defineProperty,[null,[metadata,"x",{value:1}]]);',
'const change = Object.defineProperty.call.bind(Object.defineProperty); change(null,metadata,"x",{value:1});',
])("preserves fresh targets through a generic invocation: %s", (write) => {
expect(
analyzeJavaScriptStaticSource(
`const metadata = {}; ${write}\n${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP} ${WINDOW}`,
).electron.browser_windows,
).toHaveLength(1);
});
});
describe("Electron module interop bound mutators", () => {
it.each([
'const change = Object.defineProperty.bind(null); change(Object,"create",{value:customCreate});',
@@ -560,6 +604,9 @@ describe("Electron module interop bound mutators", () => {
'Reflect.apply = Object.defineProperty; Reflect.apply(Object,"create",{value:customCreate});',
'const apply = Reflect.apply; Reflect.apply = Object.defineProperty; Reflect.apply(Object,"create",{value:customCreate});',
'const change = Reflect.set.bind(null,Object,"create"); change(customCreate);',
'const change = Object.defineProperty.call; change.call(Object.defineProperty,null,Object,"create",{value:customCreate});',
'const {apply:change} = Object.defineProperty; change.call(Object.defineProperty,null,[Object,"create",{value:customCreate}]);',
'const change = Reflect.apply.call; change.call(Reflect.apply,null,Object.defineProperty,null,[Object,"create",{value:customCreate}]);',
'Reflect.set({},"create",customCreate,Object);',
'const metadata = {}; Reflect.set(metadata,"create",customCreate,Object);',
'const change = Reflect.set.bind(null,{},"create",customCreate); change(Object);',
@@ -568,9 +615,6 @@ describe("Electron module interop bound mutators", () => {
'Reflect.set({},"create",...valuesAndReceivers);',
"Reflect.set({},...keysValuesAndReceivers);",
'Reflect.set({},"create",customCreate,receiver);',
'const change = Object.defineProperty.call; change(null,Object,"create",{value:customCreate});',
'const {apply:change} = Object.defineProperty; change(null,[Object,"create",{value:customCreate}]);',
'const change = Reflect.apply.call; change(null,Object.defineProperty,null,[Object,"create",{value:customCreate}]);',
])("rejects intrinsic writes through a bound mutator: %s", (write) => {
expect(
analyzeJavaScriptStaticSource(
@@ -593,8 +637,8 @@ describe("Electron module interop bound mutators", () => {
'const change = Object.defineProperty.bind(null,Object,"create");',
'Object.defineProperty.call(null,metadata,"x",{value:1});',
'Object.defineProperty.apply(null,[metadata,"x",{value:1}]);',
'const change = Object.defineProperty.call; change(null,metadata,"x",{value:1});',
'const {apply:change} = Object.defineProperty; change(null,[metadata,"x",{value:1}]);',
'const change = Object.defineProperty.call; change.call(Object.defineProperty,null,metadata,"x",{value:1});',
'const {apply:change} = Object.defineProperty; change.call(Object.defineProperty,null,[metadata,"x",{value:1}]);',
'Reflect.set(metadata,"x",1,{});',
'const receiver = {}; Reflect.set({},"x",1,receiver);',
])("keeps a fresh bound target or unused binding separate: %s", (write) => {