Skip to content

[Bug] Application graph invents imports for lexically shadowed require calls #2890

Closed as not planned

Description

@JFSAS

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.

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.

Activity

  1. hussnainahmedd commented on Oct 13, 2026

    @hussnainahmedd
    Contributor

    I'd like to work on this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions