[GH-ISSUE #3884] [Bug] Local require method creates a resolved native-addon binding #1504

Closed
opened 2026-10-16 21:06:17 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @coygeek on GitHub (Oct 15, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/3884

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Evidence, artifacts, or reconstruction

Observed behavior

analyze-javascript-application reports a resolved native-addon binding for helper.require('./fake.node') even though helper.require is an inline local arrow function that returns a plain object and never requests a native addon.

Analysis succeeds and reports one resolved native-addon binding for helper.require('./fake.node'), creating a native-export node with resolved_path: "fake.node", binding_kind: "require", state: "observed", and confidence: "exact", plus an imports edge with operation map-native-addon-binding.

An independent Node execution of the same source prints local and never loads the placeholder addon.

A separate CLI probe also reproduced a false binding for a function parameter named require.

Reproduction

Run from a built source checkout (the probe invokes the CLI through scripts/rea.mjs). Create a disposable directory containing this exact main.cjs:

const helper = { require: name => ({ source: 'local', name }) };
const addon = helper.require('./fake.node');
console.log(addon.source);

Place a sibling fake.node holding the bytes fixture bytes, never executed with no trailing newline, then run the Node control and the CLI:

fixture="$(mktemp -d)"
cat > "$fixture/main.cjs" <<'JS'
const helper = { require: name => ({ source: 'local', name }) };
const addon = helper.require('./fake.node');
console.log(addon.source);
JS
printf 'fixture bytes, never executed' > "$fixture/fake.node"
nice -n 10 node "$fixture/main.cjs"
nice -n 10 node scripts/rea.mjs analyze-javascript-application "$fixture" \
  --source-path main.cjs --format json > "$fixture/out.json"
jq '{native_addon_bindings:.normalized_result.summary.native_addon_bindings,
     resolved_native_addon_bindings:.normalized_result.summary.resolved_native_addon_bindings,
     bindings:[.normalized_result.graph.nodes[] | select(.kind=="native-export") |
       .observations[] | {properties,state:.evidence.state,confidence:.evidence.confidence}]}' "$fixture/out.json"

Expected behavior

docs/javascript-artifact-reconstruction.md:241 promises native .node binding requests, and src/domain/javascript/electronStaticAnalysisTypes.ts:176 defines these as bindings or re-exports requested from a native addon.

A call to a known local object method that returns a plain object does not request a native addon and must not create a native binding or an inferred native import.

Ordinary dependency collection already checks loader provenance at src/domain/javascript/javascriptStaticAnalysisCalls.ts:234, including exclusion of locally bound require calls at line 246.

Impact and scope

local functions or methods named require produce false native-addon records, inflate binding counts, and attach unrelated inventoried .node files to the reconstructed graph; no execution, mutation, or native-export verification is claimed.

Workaround: available. the retained source location lets an analyst disregard bindings whose callee is visibly local, as the fixture confirms; no caller option that corrects the returned graph was verified.

Reporter's impact estimate: P2 (supported).

REA version

6.5.0, built from source at main commit 034342ba28 (latest npm release is 6.5.0)

Runtime and platform

Node.js v26.10.0; macOS 27.0.1 arm64

Target and artifact context

CommonJS Electron main source with a placeholder .node file

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

  • Expected source: docs/javascript-artifact-reconstruction.md:241; native binding type at src/domain/javascript/electronStaticAnalysisTypes.ts:176; provenance check at src/domain/javascript/javascriptStaticAnalysisCalls.ts:234 and line 246
  • Failure source: observed one resolved native-addon binding (output below); static cause at src/domain/javascript/electronStaticAnalysisNative.ts:220-229, where nativeRequire accepts any callee name ending in .require, any bare require, or a name containing __webpack_require__, without consulting lexical binding or receiver identity
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete
local
native_addon_bindings: 1
resolved_native_addon_bindings: 1
binding properties:
  {"specifier":"./fake.node","binding_kind":"require","requested_members":[],
   "namespace_access":true,"dynamic_member_access":false,
   "resolved_path":"fake.node","native_export_verification":"not-performed"}
binding evidence: {"state":"observed","confidence":"exact"}

Typed outcome or recovery expected

A call to a visibly local method named require that returns a plain object should produce no native-addon binding and no inferred native import; native_addon_bindings should be 0.

Restoration check:

Failing: helper.require('./fake.node') yields native_addon_bindings: 1, resolved_native_addon_bindings: 1, and a native-export node attributed to the local method.

Passing: the same local-method source yields native_addon_bindings: 0 and no native-export node, while an unshadowed top-level require('./fake.node') control still yields one resolved native binding.

Analysis or suspected cause

electronStaticAnalysisNative.ts:220-229 accepts any callee ending in .require, bare require, or containing webpack_require without checking lexical binding or receiver identity, so the local helper.require is treated as a native addon request and emitted at ElectronBoundaryGraphNative.ts:56/98 and counted at ElectronBoundaryAnalysis.ts:171. The ordinary dependency collector already excludes such local calls (javascriptStaticAnalysisCalls.ts:246), so this is a consumer regression after closed #2890.

Additional context

The native collector is reached from the public CLI through src/cli/electronCommands.ts:230, src/domain/javascript/javascriptStaticAnalysis.ts:235, and src/domain/javascript/electronStaticAnalysis.ts:15; the graph consumer resolves the string and emits the binding at src/application/javascript/ElectronBoundaryGraphNative.ts:56 and line 98, counted at src/application/javascript/ElectronBoundaryAnalysis.ts:171.

Related issues: closed #2890 fixed the same provenance requirement in the ordinary dependency collector; this is a remaining consumer regression because the Electron native binding collector still invents an import. Closed #822 concerns computed member keys, not loader identity. No exact open duplicate found.

This report came from AI-assisted source review with local reproduction where stated above; the evidence labels distinguish observed runs from source-only reasoning.

Originally created by @coygeek on GitHub (Oct 15, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3884 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Evidence, artifacts, or reconstruction ### Observed behavior `analyze-javascript-application` reports a resolved native-addon binding for `helper.require('./fake.node')` even though `helper.require` is an inline local arrow function that returns a plain object and never requests a native addon. Analysis succeeds and reports one resolved native-addon binding for `helper.require('./fake.node')`, creating a `native-export` node with `resolved_path: "fake.node"`, `binding_kind: "require"`, `state: "observed"`, and `confidence: "exact"`, plus an `imports` edge with operation `map-native-addon-binding`. An independent Node execution of the same source prints `local` and never loads the placeholder addon. A separate CLI probe also reproduced a false binding for a function parameter named `require`. ### Reproduction Run from a built source checkout (the probe invokes the CLI through `scripts/rea.mjs`). Create a disposable directory containing this exact `main.cjs`: ```js const helper = { require: name => ({ source: 'local', name }) }; const addon = helper.require('./fake.node'); console.log(addon.source); ``` Place a sibling `fake.node` holding the bytes `fixture bytes, never executed` with no trailing newline, then run the Node control and the CLI: ```sh fixture="$(mktemp -d)" cat > "$fixture/main.cjs" <<'JS' const helper = { require: name => ({ source: 'local', name }) }; const addon = helper.require('./fake.node'); console.log(addon.source); JS printf 'fixture bytes, never executed' > "$fixture/fake.node" nice -n 10 node "$fixture/main.cjs" nice -n 10 node scripts/rea.mjs analyze-javascript-application "$fixture" \ --source-path main.cjs --format json > "$fixture/out.json" jq '{native_addon_bindings:.normalized_result.summary.native_addon_bindings, resolved_native_addon_bindings:.normalized_result.summary.resolved_native_addon_bindings, bindings:[.normalized_result.graph.nodes[] | select(.kind=="native-export") | .observations[] | {properties,state:.evidence.state,confidence:.evidence.confidence}]}' "$fixture/out.json" ``` ### Expected behavior docs/javascript-artifact-reconstruction.md:241 promises native `.node` binding requests, and src/domain/javascript/electronStaticAnalysisTypes.ts:176 defines these as bindings or re-exports requested from a native addon. A call to a known local object method that returns a plain object does not request a native addon and must not create a native binding or an inferred native import. Ordinary dependency collection already checks loader provenance at src/domain/javascript/javascriptStaticAnalysisCalls.ts:234, including exclusion of locally bound `require` calls at line 246. ### Impact and scope local functions or methods named `require` produce false native-addon records, inflate binding counts, and attach unrelated inventoried `.node` files to the reconstructed graph; no execution, mutation, or native-export verification is claimed. Workaround: available. the retained source location lets an analyst disregard bindings whose callee is visibly local, as the fixture confirms; no caller option that corrects the returned graph was verified. Reporter's impact estimate: P2 (supported). ### REA version 6.5.0, built from source at main commit 034342ba28c8e56c3746be92b78aa7299afa02bd (latest npm release is 6.5.0) ### Runtime and platform Node.js v26.10.0; macOS 27.0.1 arm64 ### Target and artifact context CommonJS Electron main source with a placeholder .node file ### Interface CLI ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence - Expected source: docs/javascript-artifact-reconstruction.md:241; native binding type at src/domain/javascript/electronStaticAnalysisTypes.ts:176; provenance check at src/domain/javascript/javascriptStaticAnalysisCalls.ts:234 and line 246 - Failure source: observed one resolved native-addon binding (output below); static cause at src/domain/javascript/electronStaticAnalysisNative.ts:220-229, where `nativeRequire` accepts any callee name ending in `.require`, any bare `require`, or a name containing `__webpack_require__`, without consulting lexical binding or receiver identity - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete ```text local native_addon_bindings: 1 resolved_native_addon_bindings: 1 binding properties: {"specifier":"./fake.node","binding_kind":"require","requested_members":[], "namespace_access":true,"dynamic_member_access":false, "resolved_path":"fake.node","native_export_verification":"not-performed"} binding evidence: {"state":"observed","confidence":"exact"} ``` ### Typed outcome or recovery expected A call to a visibly local method named require that returns a plain object should produce no native-addon binding and no inferred native import; native_addon_bindings should be 0. Restoration check: Failing: `helper.require('./fake.node')` yields `native_addon_bindings: 1`, `resolved_native_addon_bindings: 1`, and a `native-export` node attributed to the local method. Passing: the same local-method source yields `native_addon_bindings: 0` and no `native-export` node, while an unshadowed top-level `require('./fake.node')` control still yields one resolved native binding. ### Analysis or suspected cause electronStaticAnalysisNative.ts:220-229 accepts any callee ending in .require, bare require, or containing __webpack_require__ without checking lexical binding or receiver identity, so the local helper.require is treated as a native addon request and emitted at ElectronBoundaryGraphNative.ts:56/98 and counted at ElectronBoundaryAnalysis.ts:171. The ordinary dependency collector already excludes such local calls (javascriptStaticAnalysisCalls.ts:246), so this is a consumer regression after closed #2890. ### Additional context The native collector is reached from the public CLI through src/cli/electronCommands.ts:230, src/domain/javascript/javascriptStaticAnalysis.ts:235, and src/domain/javascript/electronStaticAnalysis.ts:15; the graph consumer resolves the string and emits the binding at src/application/javascript/ElectronBoundaryGraphNative.ts:56 and line 98, counted at src/application/javascript/ElectronBoundaryAnalysis.ts:171. Related issues: closed #2890 fixed the same provenance requirement in the ordinary dependency collector; this is a remaining consumer regression because the Electron native binding collector still invents an import. Closed #822 concerns computed member keys, not loader identity. No exact open duplicate found. This report came from AI-assisted source review with local reproduction where stated above; the evidence labels distinguish observed runs from source-only reasoning.
Author
Owner

@coygeek commented on GitHub (Oct 24, 2026):

Checked the reported fix on main at d87e73f2db.

Native-addon collection now uses semanticRequireCallAt with shared lexical/loader ownership rather than callee spelling. It rejects local, unknown, non-load and non-source-relative loader identities. The public CLI/stdio-MCP acceptance fixture includes helper.require and a shadowed require parameter alongside real require controls, and expects only the five legitimate native bindings; it also excludes local.cjs from static dependency sources.

Implementation: electronStaticAnalysisNative.ts, electronStaticAnalysisNative.ts.

The relevant regression checks in javascriptNativeBindingOwnership.test.ts passed in the focused test run on this revision. This closure is based on the current source and those checks; provider fixtures do not establish a real-engine run. Closing as completed on main.

<!-- gh-comment-id:6380761263 --> @coygeek commented on GitHub (Oct 24, 2026): Checked the reported fix on `main` at d87e73f2db50be72d33a4b3c7199af35518b441c. Native-addon collection now uses semanticRequireCallAt with shared lexical/loader ownership rather than callee spelling. It rejects local, unknown, non-load and non-source-relative loader identities. The public CLI/stdio-MCP acceptance fixture includes helper.require and a shadowed require parameter alongside real require controls, and expects only the five legitimate native bindings; it also excludes local.cjs from static dependency sources. Implementation: [electronStaticAnalysisNative.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/domain/javascript/electronStaticAnalysisNative.ts#L299), [electronStaticAnalysisNative.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/domain/javascript/electronStaticAnalysisNative.ts#L313). The relevant regression checks in [javascriptNativeBindingOwnership.test.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/tests/acceptance/applications/javascriptNativeBindingOwnership.test.ts) passed in the focused test run on this revision. This closure is based on the current source and those checks; provider fixtures do not establish a real-engine run. Closing as completed on main.
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#1504
No description provided.