[GH-ISSUE #2890] [Bug] Application graph invents imports for lexically shadowed require calls #1018

Closed
opened 2026-10-15 18:30:24 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @JFSAS on GitHub (Oct 13, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/2890

Observed behavior

analyze-javascript-application creates a resolved, high-confidence imports relationship for a call to a local function parameter named require. The same false edge is returned by stdio MCP analyze_javascript_application.

This is an incorrect static inference, not a claim that REA executed the file. The graph labels the source coverage complete and supplies only its generic runtime-execution caveat, even though lexical binding already establishes that this call is not the CommonJS loader.

Minimal reproduction

On baseline 3e7c6302233ecbcb9b92e43ee5a60337738961bd, place two files in an otherwise empty directory.

main.cjs:

function run(require) {
  return require("./ghost.cjs");
}
console.log(run(() => "local"));

ghost.cjs:

throw new Error("A local function must not load this module");

Run:

node /absolute/path/to/fixture/main.cjs
rea analyze-javascript-application /absolute/path/to/fixture --json

The real Node control prints local and exits successfully. REA nevertheless returns a graph edge with:

{
  "relation": "imports",
  "properties": {
    "kind": "require",
    "specifier": "./ghost.cjs",
    "resolution_status": "resolved",
    "resolved_path": "ghost.cjs"
  }
}

Its evidence has state: "inferred", confidence: "high", coverage.status: "complete", and points to the local call in main.cjs.

Expected behavior

Do not emit a CommonJS import edge for a call whose require resolves to a local parameter, hoisted function, catch binding or block binding. Apply the same decision to optional calls.

Preserve true ordinary and optional CommonJS calls, an uninitialized top-level var require that retains Node's wrapper loader, a direct createRequire(import.meta.url) binding, and the separately recognized bundle-factory loaders.

inspectCall already receives the lexical binding index, but requireCallSpecifier in javascriptStaticAnalysisCalls.ts recognizes the bare name without consulting it. JavaScriptArtifactGraphFindings then resolves that syntactic candidate to an inventoried file and emits the incorrect graph relationship.

The existing reference-source importer has a loader-binding classifier that handles these cases. Reusing that owner can avoid creating another incompatible scope heuristic.

I searched current and closed issues/PRs for shadowed require and application graph imports. #509 addressed semantic require origins; #1657 addressed reference-source import relationships; #1803 retained genuine unbound semantic dependencies. The application graph's separate static-findings path remains affected on the current base, so this is a consumer gap after those fixes.

Executed verification and scope

  • macOS arm64, Node 24.18.0, npm 11.16.0.
  • A public CLI/stdio MCP regression fails on the base through both adapters, with five false dependencies across parameter, optional parameter, hoisted function, catch and block cases.
  • The regression executes each self-authored fixture with real Node as an independent binding control. After the fix, both REA adapters exclude the false edges and preserve the four real-loader controls.
  • Related static-analysis, bundle, semantic-link and reference-source tests were run; a repair PR follows with exact validation results.
  • REA itself continues to parse target code as data. The Node executions above belong only to the regression's inert, self-authored controls. No native analysis provider was involved; Windows/Linux were not run.

Prepared with AI assistance; the reproductions and before/after checks above were executed locally.

Originally created by @JFSAS on GitHub (Oct 13, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/2890 ### Observed behavior `analyze-javascript-application` creates a resolved, high-confidence `imports` relationship for a call to a **local function parameter** named `require`. The same false edge is returned by stdio MCP `analyze_javascript_application`. This is an incorrect static inference, not a claim that REA executed the file. The graph labels the source coverage complete and supplies only its generic runtime-execution caveat, even though lexical binding already establishes that this call is not the CommonJS loader. ### Minimal reproduction On baseline `3e7c6302233ecbcb9b92e43ee5a60337738961bd`, place two files in an otherwise empty directory. `main.cjs`: ```js function run(require) { return require("./ghost.cjs"); } console.log(run(() => "local")); ``` `ghost.cjs`: ```js throw new Error("A local function must not load this module"); ``` Run: ```sh node /absolute/path/to/fixture/main.cjs rea analyze-javascript-application /absolute/path/to/fixture --json ``` The real Node control prints `local` and exits successfully. REA nevertheless returns a graph edge with: ```json { "relation": "imports", "properties": { "kind": "require", "specifier": "./ghost.cjs", "resolution_status": "resolved", "resolved_path": "ghost.cjs" } } ``` Its evidence has `state: "inferred"`, `confidence: "high"`, `coverage.status: "complete"`, and points to the local call in `main.cjs`. ### Expected behavior Do not emit a CommonJS import edge for a call whose `require` resolves to a local parameter, hoisted function, catch binding or block binding. Apply the same decision to optional calls. Preserve true ordinary and optional CommonJS calls, an uninitialized top-level `var require` that retains Node's wrapper loader, a direct `createRequire(import.meta.url)` binding, and the separately recognized bundle-factory loaders. ### Cause and related work `inspectCall` already receives the lexical binding index, but `requireCallSpecifier` in `javascriptStaticAnalysisCalls.ts` recognizes the bare name without consulting it. `JavaScriptArtifactGraphFindings` then resolves that syntactic candidate to an inventoried file and emits the incorrect graph relationship. The existing reference-source importer has a loader-binding classifier that handles these cases. Reusing that owner can avoid creating another incompatible scope heuristic. I searched current and closed issues/PRs for shadowed require and application graph imports. #509 addressed semantic require origins; #1657 addressed reference-source import relationships; #1803 retained genuine unbound semantic dependencies. The application graph's separate static-findings path remains affected on the current base, so this is a consumer gap after those fixes. ### Executed verification and scope - macOS arm64, Node **24.18.0**, npm **11.16.0**. - A public CLI/stdio MCP regression fails on the base through both adapters, with five false dependencies across parameter, optional parameter, hoisted function, catch and block cases. - The regression executes each self-authored fixture with real Node as an independent binding control. After the fix, both REA adapters exclude the false edges and preserve the four real-loader controls. - Related static-analysis, bundle, semantic-link and reference-source tests were run; a repair PR follows with exact validation results. - REA itself continues to parse target code as data. The Node executions above belong only to the regression's inert, self-authored controls. No native analysis provider was involved; Windows/Linux were not run. Prepared with AI assistance; the reproductions and before/after checks above were executed locally.
bsakaguchi 2026-10-15 18:30:24 +00:00
  • closed this issue
  • added the
    bug
    label
Author
Owner

@hussnainahmedd commented on GitHub (Oct 13, 2026):

I'd like to work on this.

<!-- gh-comment-id:6152575073 --> @hussnainahmedd commented on GitHub (Oct 13, 2026): I'd like to work on this.
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#1018
No description provided.