[GH-ISSUE #3393] [Bug] Overwritten require.resolve produces a false reference-source dependency #1258

Closed
opened 2026-10-16 21:03:29 +00:00 by bsakaguchi · 0 comments
Owner

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

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

import-reference-source treats a call to an overwritten require.resolve property as Node's module resolver. A CommonJS file replaces that property with a function that ignores its argument and returns custom, but the imported graph still reports a parsed internal requires relationship from main.cjs to the literal argument dep.cjs. The graph does not identify uncertainty about the replaced resolver.

Running the input under Node exits with status 0 and prints custom. The import also exits with status 0 and reports no parse failures. Removing the assignment and importing the original-resolver control produces the same relationship. The public CLI failure and control were reproduced locally with complete inputs.

This is a static attribution failure, not a claim that REA should execute source code or prove runtime loading.

Reproduction

  1. Create a clean checkout, select the reproduced baseline, install its locked dependencies, and build the runtime.
git clone https://github.com/morluto/rea.git rea-resolver-repro
cd rea-resolver-repro
git checkout 8dde6370d0809ad04cda3342d4e6f290f48aa063
npm ci
npm run build:cached

The recorded reproduction used a built source checkout. The preparation commands supply those prerequisites and do not claim that dependency installation or building was independently repeated during reproduction.

  1. From the repository root, create this complete disposable source tree, isolate configuration reads, execute the Node control, and import the source.
case_dir="$(mktemp -d)"
mkdir -p "$case_dir/source" "$case_dir/home"
cat > "$case_dir/source/main.cjs" <<'JS'
require.resolve = () => "custom";
console.log(require.resolve("./dep.cjs"));
JS
printf 'module.exports = 7;\n' > "$case_dir/source/dep.cjs"
env HOME="$case_dir/home" node "$case_dir/source/main.cjs"
env HOME="$case_dir/home" node scripts/rea.mjs import-reference-source "$case_dir/source" --format json > "$case_dir/result.json"
printf 'exit=%s\n' "$?"
cat "$case_dir/result.json"
  1. For the discriminating control, remove the property assignment and repeat the import, then remove the disposable files.
printf 'console.log(require.resolve("./dep.cjs"));\n' > "$case_dir/source/main.cjs"
env HOME="$case_dir/home" node scripts/rea.mjs import-reference-source "$case_dir/source" --format json > "$case_dir/control.json"
printf 'exit=%s\n' "$?"
cat "$case_dir/control.json"
rm -rf "$case_dir"

Expected behavior

Preserve both file entries, but do not claim a parsed Node dependency from the overwritten resolver call. The replacement function ignores its argument and returns custom; its literal argument does not establish module-resolution intent. If static analysis cannot establish the member's current identity, preserve that uncertainty. The control's original Node resolver must continue to produce the dependency relationship.

tests/boundary/filesystem/referenceSourceShadowedRequire.test.ts:104-120 retains only calls attributed to the Node loader and excludes local replacements. tests/boundary/filesystem/referenceSourceComputedRequire.test.ts:16-25 supports literal require.resolve calls. AGENTS.md:21 requires unresolved identity to remain unknown, and docs/tool-design.md:57-59 requires preserving inferred relationships and partial facts.

Impact and scope

Impact level: P2, supported. Historical-source dependency analysis receives a false internal edge for a valid CommonJS property replacement. The retained files remain intact, but callers can mistake an arbitrary string argument for a module relationship.

Workaround status: available. The caller can inspect the original source and reject this edge manually. The two-line source and Node control directly establish the replacement's identity; this preserves the original imported file hashes while avoiding reliance on the false edge.

REA version

6.4.0 (main at 8dde6370d0)

Runtime and platform

Node.js v26.10.0, macOS 27.0.1, arm64. REA CLI ran from the built source checkout. No external analysis engine was used.

Target and artifact context

A local CommonJS source directory containing main.cjs and dep.cjs, with the complete contents supplied above. The affected artifact is the imported historical-source graph's relationships array. The isolated configuration home is separate from the source directory.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Node prints custom and exits with status 0. The import exits with status 0 and reports no parse failures. Its relationships array contains the following observed relationship; the original-resolver control produces the same relationship.

[
  {
    "from_path": "main.cjs",
    "to": "dep.cjs",
    "kind": "requires",
    "resolution": "internal",
    "parse_state": "parsed"
  }
]

Typed outcome or recovery expected

The overwritten-resolver input must retain both hashes without a definite dependency claim from its replacement call. The original-resolver control must still report the parsed internal relationship. Existing lexical-shadowing behavior must continue to exclude calls to local replacement functions. Restoration must not require target execution or blanket removal of literal require.resolve support.

Analysis or suspected cause

Source-derived reasoning: src/domain/referenceSourceRequireBindings.ts:45-77 checks writes to the require binding but still admits this member call, and src/domain/referenceSourceImportParsing.ts:243-256 projects it as a parsed requires relationship. This explains the suspected gap between loader-binding admission and resolver-property identity. The executed CLI import supplies the failure evidence.

Additional context

Related closed #2890, marked not planned, concerns lexically shadowed bare require calls in application analysis; this behavior replaces a property on the genuine loader in reference-source import.

Related open #3341 concerns a valid wrapper redeclaration losing its dependency; this behavior falsely retains a dependency after replacing the resolver.

Related open #3367 concerns replaced timer, fetch, and JSON.parse built-ins retaining application semantic effects; this behavior concerns the reference-source loader classifier.

Related open #3315 concerns replaced or deleted Webpack getters retaining Electron attribution; this behavior concerns direct assignment to Node's resolver property.

Related completed #2022 concerns unsupported extensionless CommonJS lookup candidates; this behavior supplies an explicit relative filename to a function that no longer resolves modules.

Related open #3307 concerns incorrect ESM/HTML URL path interpretation; this behavior concerns resolver identity before any path lookup.

Related completed #2883 concerns omitted optional source-map require calls; this behavior falsely includes an ordinary reference-source member call.

Related completed #3223 concerns missing semantic exports and require origins after wrapper redeclarations; this behavior replaces the resolver property without redeclaring the loader.

Related completed #1793 concerns missing semantic edges for valid bare require calls with unused results; this behavior emits a false reference-source edge for a replacement function.

Related completed #2057 concerns applying file-URL decoding to CommonJS strings; this behavior uses an ordinary relative argument whose replacement function ignores it.

Originally created by @coygeek on GitHub (Oct 14, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3393 ### 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 `import-reference-source` treats a call to an overwritten `require.resolve` property as Node's module resolver. A CommonJS file replaces that property with a function that ignores its argument and returns `custom`, but the imported graph still reports a parsed internal `requires` relationship from `main.cjs` to the literal argument `dep.cjs`. The graph does not identify uncertainty about the replaced resolver. Running the input under Node exits with status 0 and prints `custom`. The import also exits with status 0 and reports no parse failures. Removing the assignment and importing the original-resolver control produces the same relationship. The public CLI failure and control were reproduced locally with complete inputs. This is a static attribution failure, not a claim that REA should execute source code or prove runtime loading. ### Reproduction 1. Create a clean checkout, select the reproduced baseline, install its locked dependencies, and build the runtime. ```sh git clone https://github.com/morluto/rea.git rea-resolver-repro cd rea-resolver-repro git checkout 8dde6370d0809ad04cda3342d4e6f290f48aa063 npm ci npm run build:cached ``` The recorded reproduction used a built source checkout. The preparation commands supply those prerequisites and do not claim that dependency installation or building was independently repeated during reproduction. 2. From the repository root, create this complete disposable source tree, isolate configuration reads, execute the Node control, and import the source. ```sh case_dir="$(mktemp -d)" mkdir -p "$case_dir/source" "$case_dir/home" cat > "$case_dir/source/main.cjs" <<'JS' require.resolve = () => "custom"; console.log(require.resolve("./dep.cjs")); JS printf 'module.exports = 7;\n' > "$case_dir/source/dep.cjs" env HOME="$case_dir/home" node "$case_dir/source/main.cjs" env HOME="$case_dir/home" node scripts/rea.mjs import-reference-source "$case_dir/source" --format json > "$case_dir/result.json" printf 'exit=%s\n' "$?" cat "$case_dir/result.json" ``` 3. For the discriminating control, remove the property assignment and repeat the import, then remove the disposable files. ```sh printf 'console.log(require.resolve("./dep.cjs"));\n' > "$case_dir/source/main.cjs" env HOME="$case_dir/home" node scripts/rea.mjs import-reference-source "$case_dir/source" --format json > "$case_dir/control.json" printf 'exit=%s\n' "$?" cat "$case_dir/control.json" rm -rf "$case_dir" ``` ### Expected behavior Preserve both file entries, but do not claim a parsed Node dependency from the overwritten resolver call. The replacement function ignores its argument and returns `custom`; its literal argument does not establish module-resolution intent. If static analysis cannot establish the member's current identity, preserve that uncertainty. The control's original Node resolver must continue to produce the dependency relationship. `tests/boundary/filesystem/referenceSourceShadowedRequire.test.ts:104-120` retains only calls attributed to the Node loader and excludes local replacements. `tests/boundary/filesystem/referenceSourceComputedRequire.test.ts:16-25` supports literal `require.resolve` calls. `AGENTS.md:21` requires unresolved identity to remain unknown, and `docs/tool-design.md:57-59` requires preserving inferred relationships and partial facts. ### Impact and scope Impact level: P2, supported. Historical-source dependency analysis receives a false internal edge for a valid CommonJS property replacement. The retained files remain intact, but callers can mistake an arbitrary string argument for a module relationship. Workaround status: available. The caller can inspect the original source and reject this edge manually. The two-line source and Node control directly establish the replacement's identity; this preserves the original imported file hashes while avoiding reliance on the false edge. ### REA version 6.4.0 (main at 8dde6370d0809ad04cda3342d4e6f290f48aa063) ### Runtime and platform Node.js v26.10.0, macOS 27.0.1, arm64. REA CLI ran from the built source checkout. No external analysis engine was used. ### Target and artifact context A local CommonJS source directory containing `main.cjs` and `dep.cjs`, with the complete contents supplied above. The affected artifact is the imported historical-source graph's `relationships` array. The isolated configuration home is separate from the source directory. ### Interface CLI ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence Node prints `custom` and exits with status 0. The import exits with status 0 and reports no parse failures. Its `relationships` array contains the following observed relationship; the original-resolver control produces the same relationship. ```text [ { "from_path": "main.cjs", "to": "dep.cjs", "kind": "requires", "resolution": "internal", "parse_state": "parsed" } ] ``` ### Typed outcome or recovery expected The overwritten-resolver input must retain both hashes without a definite dependency claim from its replacement call. The original-resolver control must still report the parsed internal relationship. Existing lexical-shadowing behavior must continue to exclude calls to local replacement functions. Restoration must not require target execution or blanket removal of literal `require.resolve` support. ### Analysis or suspected cause Source-derived reasoning: `src/domain/referenceSourceRequireBindings.ts:45-77` checks writes to the `require` binding but still admits this member call, and `src/domain/referenceSourceImportParsing.ts:243-256` projects it as a parsed `requires` relationship. This explains the suspected gap between loader-binding admission and resolver-property identity. The executed CLI import supplies the failure evidence. ### Additional context Related closed [#2890](https://github.com/morluto/rea/issues/2890), marked not planned, concerns lexically shadowed bare `require` calls in application analysis; this behavior replaces a property on the genuine loader in reference-source import. Related open [#3341](https://github.com/morluto/rea/issues/3341) concerns a valid wrapper redeclaration losing its dependency; this behavior falsely retains a dependency after replacing the resolver. Related open [#3367](https://github.com/morluto/rea/issues/3367) concerns replaced timer, fetch, and JSON.parse built-ins retaining application semantic effects; this behavior concerns the reference-source loader classifier. Related open [#3315](https://github.com/morluto/rea/issues/3315) concerns replaced or deleted Webpack getters retaining Electron attribution; this behavior concerns direct assignment to Node's resolver property. Related completed [#2022](https://github.com/morluto/rea/issues/2022) concerns unsupported extensionless CommonJS lookup candidates; this behavior supplies an explicit relative filename to a function that no longer resolves modules. Related open [#3307](https://github.com/morluto/rea/issues/3307) concerns incorrect ESM/HTML URL path interpretation; this behavior concerns resolver identity before any path lookup. Related completed [#2883](https://github.com/morluto/rea/issues/2883) concerns omitted optional source-map require calls; this behavior falsely includes an ordinary reference-source member call. Related completed [#3223](https://github.com/morluto/rea/issues/3223) concerns missing semantic exports and require origins after wrapper redeclarations; this behavior replaces the resolver property without redeclaring the loader. Related completed [#1793](https://github.com/morluto/rea/issues/1793) concerns missing semantic edges for valid bare require calls with unused results; this behavior emits a false reference-source edge for a replacement function. Related completed [#2057](https://github.com/morluto/rea/issues/2057) concerns applying file-URL decoding to CommonJS strings; this behavior uses an ordinary relative argument whose replacement function ignores it.
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#1258
No description provided.