[GH-ISSUE #8102] [Bug] analyze-javascript-application reports x.k = x.k || []; x.k.push() as an unresolved require #4674

Closed
opened 2026-10-31 00:52:18 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @kaoru0822-kitauji on GitHub (Oct 30, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/8102

Originally assigned to: @kaoru0822-kitauji on GitHub.

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

rea analyze-javascript-application reports an ordinary array push as an unresolved CommonJS require load when the receiver was given a self-default first (x.k = x.k || []). The application graph gets an imports edge to an unresolved-reference module node, and graph coverage drops from complete to partial.

From expressjs/express examples/mvc/index.js (lines 24-31):

app.response.message = function(msg){
  var sess = this.req.session;
  sess.messages = sess.messages || [];
  sess.messages.push(msg);
  return this;
};

the result includes:

{"relation": "imports", "properties": {"kind": "require", "operation": "load", "specifier": null, "expression": "sess.messages.push(msg)", "resolution_status": null}}

The same thing happens for a binding that defaults to itself, such as the classic analytics snippet var _gaq = _gaq || []; _gaq.push([...]). A real loader also loses its specifier: let r = require; r = r || require; r('x') gives specifier: null instead of x.

Reproduction

Reproduced on every run on main 317bf3fe2:

  1. mkdir app && printf 'var o = {};\no.list = o.list || [];\no.list.push(1);\n' > app/index.js && printf '{"name":"m","version":"1.0.0","main":"index.js"}\n' > app/package.json
  2. node scripts/rea.mjs analyze-javascript-application app --format json
  3. Observe the imports edge {"kind":"require","specifier":null,"expression":"o.list.push(1)"} and graph.coverage.status: "partial".

With o.list = []; instead of o.list = o.list || [];, the same file gives no imports edge and graph.coverage.status: "complete".

Real input: examples/mvc from https://github.com/expressjs/express (master tarball), analyzed as a directory.

Expected behavior

o.list.push(1), sess.messages.push(msg) and _gaq.push(...) are not module loads, so no imports edge should be produced. r('x') after r = r || require should keep specifier x, as it does without the self-reference. Real loader aliases that flow through a self-default, such as var o = { r: module.require }; o.r = o.r || fb; o.r('x'), should still be reported as possible loads.

Impact and scope

Any CommonJS source with a.b = a.b || default or var x = x || default followed by a call on that value gets a fabricated unresolved require relationship, and its graph coverage is marked partial. The pattern is common in hand-written Node and browser code. Observed in 1 of 25 express examples (mvc); other packages are untested.

REA version

source main 317bf3fe2 (6.8.0)

Runtime and platform

Node 26.5.0; macOS 26 arm64

Target and artifact context

Directory application, CommonJS JavaScript

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Command run: node scripts/rea.mjs analyze-javascript-application <dir> --format json on the 3-line file above and on express/examples/mvc. The edge and node are shown above; the target node has namespace: "unresolved-reference" and key: "require:o.list.push(1)".

Analysis or suspected cause

Source-derived, checked with a local trace: semanticModuleRequireCallAt (src/domain/javascript/javascriptSemanticRequireOrigin.ts) asks hasCommonJsModuleSource whether the callee can be a module.require delegate. That search returns true ("unknown") whenever a state repeats on its own ancestor chain while includeUnknown is set. A self-default is such a cycle. A member read inside the assigned value (o.list in o.list || []) is attributed to the outer call's read site, so it selects the same write again. A binding that reads itself in its own initializer selects that initializer again. The repeated state adds no new source, so treating it as an unknown loader produces the false require.

Additional context

Not related to webpack chunk push, which is handled separately (chunkRuntime).

Originally created by @kaoru0822-kitauji on GitHub (Oct 30, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/8102 Originally assigned to: @kaoru0822-kitauji on GitHub. ### 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 `rea analyze-javascript-application` reports an ordinary array `push` as an unresolved CommonJS `require` load when the receiver was given a self-default first (`x.k = x.k || []`). The application graph gets an `imports` edge to an `unresolved-reference` module node, and graph coverage drops from `complete` to `partial`. From `expressjs/express` `examples/mvc/index.js` (lines 24-31): ```js app.response.message = function(msg){ var sess = this.req.session; sess.messages = sess.messages || []; sess.messages.push(msg); return this; }; ``` the result includes: ```json {"relation": "imports", "properties": {"kind": "require", "operation": "load", "specifier": null, "expression": "sess.messages.push(msg)", "resolution_status": null}} ``` The same thing happens for a binding that defaults to itself, such as the classic analytics snippet `var _gaq = _gaq || []; _gaq.push([...])`. A real loader also loses its specifier: `let r = require; r = r || require; r('x')` gives `specifier: null` instead of `x`. ### Reproduction Reproduced on every run on main 317bf3fe2: 1. `mkdir app && printf 'var o = {};\no.list = o.list || [];\no.list.push(1);\n' > app/index.js && printf '{"name":"m","version":"1.0.0","main":"index.js"}\n' > app/package.json` 2. `node scripts/rea.mjs analyze-javascript-application app --format json` 3. Observe the `imports` edge `{"kind":"require","specifier":null,"expression":"o.list.push(1)"}` and `graph.coverage.status: "partial"`. With `o.list = [];` instead of `o.list = o.list || [];`, the same file gives no `imports` edge and `graph.coverage.status: "complete"`. Real input: `examples/mvc` from https://github.com/expressjs/express (master tarball), analyzed as a directory. ### Expected behavior `o.list.push(1)`, `sess.messages.push(msg)` and `_gaq.push(...)` are not module loads, so no `imports` edge should be produced. `r('x')` after `r = r || require` should keep specifier `x`, as it does without the self-reference. Real loader aliases that flow through a self-default, such as `var o = { r: module.require }; o.r = o.r || fb; o.r('x')`, should still be reported as possible loads. ### Impact and scope Any CommonJS source with `a.b = a.b || default` or `var x = x || default` followed by a call on that value gets a fabricated unresolved `require` relationship, and its graph coverage is marked partial. The pattern is common in hand-written Node and browser code. Observed in 1 of 25 express examples (`mvc`); other packages are untested. ### REA version source main 317bf3fe2 (6.8.0) ### Runtime and platform Node 26.5.0; macOS 26 arm64 ### Target and artifact context Directory application, CommonJS JavaScript ### Interface CLI ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence Command run: `node scripts/rea.mjs analyze-javascript-application <dir> --format json` on the 3-line file above and on `express/examples/mvc`. The edge and node are shown above; the target node has `namespace: "unresolved-reference"` and `key: "require:o.list.push(1)"`. ### Analysis or suspected cause Source-derived, checked with a local trace: `semanticModuleRequireCallAt` (`src/domain/javascript/javascriptSemanticRequireOrigin.ts`) asks `hasCommonJsModuleSource` whether the callee can be a `module.require` delegate. That search returns `true` ("unknown") whenever a state repeats on its own ancestor chain while `includeUnknown` is set. A self-default is such a cycle. A member read inside the assigned value (`o.list` in `o.list || []`) is attributed to the outer call's read site, so it selects the same write again. A binding that reads itself in its own initializer selects that initializer again. The repeated state adds no new source, so treating it as an unknown loader produces the false `require`. ### Additional context Not related to webpack chunk `push`, which is handled separately (`chunkRuntime`).
bsakaguchi 2026-10-31 00:52:18 +00:00
  • closed this issue
  • added the
    bug
    triage
    labels
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#4674
No description provided.