[GH-ISSUE #1909] [Bug] Nested Function.prototype.call chains bypass Electron intrinsic mutation checks #639

Closed
opened 2026-10-15 18:26:51 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @N0zoM1z0 on GitHub (Oct 11, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/1909

Before submitting

  • Searched open/closed issues and open PRs; #1842/#1857 fixed helper-name attribution, but this nested invocation reproduction remains on current main.
  • Removed credentials and private paths; the fixture is synthetic inert source.

Area

Evidence, artifacts, or reconstruction

Observed behavior

analyze-javascript-application / analyze_javascript_application attributes a constructor to Electron even though a visible nested native invocation overwrites Object.create before the Rolldown helper captures it. The new CLI and production stdio MCP regression fixture reports eight windows rather than the seven legitimate construction anchors.

Reproduction

  1. Build main e4ac7bbfc2f8c631ba1c60f507d85253e122d4c8 with npm ci and npm run build:cached.
  2. Create receiver-repro/package.json containing { "main": "main.cjs" } and save this source input as receiver-repro/main.cjs:
Function.prototype.call.call.call(Object.defineProperty, null, Object, "create", {value: customCreate});
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
    key = keys[i];
    if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
      get: ((k) => from[k]).bind(null, key),
      enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
    });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
  value: mod,
  enumerable: true
}) : target, mod));
let electron = require("electron");
electron = __toESM(electron, 1);
new electron.BrowserWindow({});
throw new Error("source input must remain inert");
  1. Analyze it with node scripts/rea.mjs analyze-javascript-application ./receiver-repro --json, or MCP analyze_javascript_application with input_path: "./receiver-repro". REA parses this input; the target is never executed.
  2. The constructor is inferred despite the overwritten helper dependency. A second source reproduction replaces the first statement with:
const change = Function.prototype.call.call.bind(Object.defineProperty, null, Object, "create");
change({value: customCreate});

Expected behavior

Retain the intrinsic write and leave the helper-derived constructor identity unresolved. Generic call/apply properties select their actual callable receiver; property-chain depth must not determine argument consumption. Fresh local targets must continue to preserve valid Electron findings.

Impact and scope

Static evidence attribution through nested generic calls and binding. Confirmed in source analysis and actual CLI/stdio MCP. This is a residual invocation edge case after #1857; its original helper-name reproduction is fixed. No GUI, Electron execution or deep provider is involved.

REA version

6.4.0 source main e4ac7bbfc2f8c631ba1c60f507d85253e122d4c8

Runtime and platform

Node.js v24.18.0; Linux x86_64, kernel 6.1.0-53-cloud-amd64

Target and artifact context

Synthetic CommonJS Electron source with the released Rolldown interop helper; no real runtime is required.

Interface

CLI and MCP (production stdio transport)

Earliest failure stage

Native analysis or reconstruction — JavaScript static lexical identity

Relevant output or evidence

Regression-first verification on the pinned main: four failing cases, 187 passing. Both source failures are the triple-call and bound nested-call forms above. Both public CLI/MCP tests report browser_windows: 8 where only seven legitimate anchors should be retained. An apply/apply control already remains excluded on main.

Typed outcome or recovery expected

Keep unproven constructor provenance unresolved without discarding the legitimate construction anchors or dynamic preload evidence.

Analysis or suspected cause

Source-derived: the invocation normalizer strips call/apply path segments and arguments before following the actual callable receiver. A deeper chain can consume an extra argument and classify the wrong target. A receiver-based normalization fixes the two reproduced source cases while retaining fresh-target controls.

Additional context

Follow-up to #1842 and #1857. Reduced controls live in src/domain/javascript/electronModuleInterop.test.ts; public coverage in tests/acceptance/applications/electronModuleInteropCli.test.ts. The existing wrapped IPC work in #1874 targets a different workflow and does not modify this invocation owner.

Originally created by @N0zoM1z0 on GitHub (Oct 11, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/1909 ### Before submitting - [x] Searched open/closed issues and open PRs; #1842/#1857 fixed helper-name attribution, but this nested invocation reproduction remains on current main. - [x] Removed credentials and private paths; the fixture is synthetic inert source. ### Area Evidence, artifacts, or reconstruction ### Observed behavior `analyze-javascript-application` / `analyze_javascript_application` attributes a constructor to Electron even though a visible nested native invocation overwrites `Object.create` before the Rolldown helper captures it. The new CLI and production stdio MCP regression fixture reports eight windows rather than the seven legitimate construction anchors. ### Reproduction 1. Build main `e4ac7bbfc2f8c631ba1c60f507d85253e122d4c8` with `npm ci` and `npm run build:cached`. 2. Create `receiver-repro/package.json` containing `{ "main": "main.cjs" }` and save this source input as `receiver-repro/main.cjs`: ```js Function.prototype.call.call.call(Object.defineProperty, null, Object, "create", {value: customCreate}); var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) { key = keys[i]; if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: ((k) => from[k]).bind(null, key), enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); let electron = require("electron"); electron = __toESM(electron, 1); new electron.BrowserWindow({}); throw new Error("source input must remain inert"); ``` 3. Analyze it with `node scripts/rea.mjs analyze-javascript-application ./receiver-repro --json`, or MCP `analyze_javascript_application` with `input_path: "./receiver-repro"`. REA parses this input; the target is never executed. 4. The constructor is inferred despite the overwritten helper dependency. A second source reproduction replaces the first statement with: ```js const change = Function.prototype.call.call.bind(Object.defineProperty, null, Object, "create"); change({value: customCreate}); ``` ### Expected behavior Retain the intrinsic write and leave the helper-derived constructor identity unresolved. Generic call/apply properties select their actual callable receiver; property-chain depth must not determine argument consumption. Fresh local targets must continue to preserve valid Electron findings. ### Impact and scope Static evidence attribution through nested generic calls and binding. Confirmed in source analysis and actual CLI/stdio MCP. This is a residual invocation edge case after #1857; its original helper-name reproduction is fixed. No GUI, Electron execution or deep provider is involved. ### REA version 6.4.0 source main `e4ac7bbfc2f8c631ba1c60f507d85253e122d4c8` ### Runtime and platform Node.js v24.18.0; Linux x86_64, kernel 6.1.0-53-cloud-amd64 ### Target and artifact context Synthetic CommonJS Electron source with the released Rolldown interop helper; no real runtime is required. ### Interface CLI and MCP (production stdio transport) ### Earliest failure stage Native analysis or reconstruction — JavaScript static lexical identity ### Relevant output or evidence Regression-first verification on the pinned main: four failing cases, 187 passing. Both source failures are the triple-call and bound nested-call forms above. Both public CLI/MCP tests report `browser_windows: 8` where only seven legitimate anchors should be retained. An apply/apply control already remains excluded on main. ### Typed outcome or recovery expected Keep unproven constructor provenance unresolved without discarding the legitimate construction anchors or dynamic preload evidence. ### Analysis or suspected cause Source-derived: the invocation normalizer strips call/apply path segments and arguments before following the actual callable receiver. A deeper chain can consume an extra argument and classify the wrong target. A receiver-based normalization fixes the two reproduced source cases while retaining fresh-target controls. ### Additional context Follow-up to #1842 and #1857. Reduced controls live in `src/domain/javascript/electronModuleInterop.test.ts`; public coverage in `tests/acceptance/applications/electronModuleInteropCli.test.ts`. The existing wrapped IPC work in #1874 targets a different workflow and does not modify this invocation owner.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#639
No description provided.