[GH-ISSUE #3367] [Bug] Replaced globals retain resolved built-in timer, fetch, and JSON.parse effects #1236

Closed
opened 2026-10-16 21:03:08 +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/3367

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 continues to classify calls as Node timer scheduling, fetch request construction, and built-in JSON parsing after the corresponding globals or properties are explicitly replaced with local no-op functions. The resulting resolved effects misdescribe the application's timer, network, and parsing behavior.

The CLI exits with code 0. It retains a timer node and a resolved schedules-timer relation on line 3, a fetch request node and a resolved constructs-request relation on line 5, and a JSON.parse boundary with a resolved parses relation on line 7. None of these effects is downgraded for the preceding explicit replacement. A separate control with the three replacement assignments removed retains the same three ordinary built-in effects with resolved relationships. The public CLI reproduction was observed and locally reproduced, with a complete reproduction.

Reproduction

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

The recorded reproduction used an installed dependency tree and built runtime. These preparation commands supply those prerequisites; they are not a claim that installation and building were independently repeated during reproduction.

  1. From the repository root, create a disposable input and isolated configuration home, invoke the public CLI, and print the affected facts from its complete Evidence. The analyzer reads the target as text and does not execute its calls.
work=$(mktemp -d)
mkdir "$work/app" "$work/home"
cat > "$work/app/main.mjs" <<'JS'
function work() {}
setTimeout = () => null;
setTimeout(work, 100);
fetch = () => null;
fetch('https://example.invalid');
JSON.parse = () => null;
JSON.parse('null');
JS
python3 - "$work" <<'PY'
import json, os, pathlib, subprocess, sys
work = pathlib.Path(sys.argv[1])
child_environment = dict(os.environ, HOME=str(work / 'home'))
result = subprocess.run(
    ['node', 'scripts/rea.mjs', 'analyze-javascript-application',
     str(work / 'app'), '--format', 'json'],
    env=child_environment, capture_output=True, text=True, check=True)
evidence = json.loads(result.stdout)
(work / 'evidence.json').write_text(result.stdout)
graph = evidence['normalized_result']['semantic_graph']
print('exit:', result.returncode)
nodes = {node['node_id']: node for node in graph['nodes']}
for node in sorted(graph['nodes'], key=lambda n: ((n['identity']['source_range'] or {}).get('start', {}).get('line', 0), n['node_id'])):
    if node['kind'] in ['timer', 'request', 'boundary']:
        print('node', node['kind'], node['identity']['source_range'], node['properties'])
for relation in graph['relations']:
    if relation['relation'] in ['schedules-timer', 'constructs-request', 'parses']:
        source = nodes[relation['source_node_id']]
        target = nodes[relation['target_node_id']]
        print('relation', relation['relation'], relation['resolution'], source['kind'], source['node_id'], source['identity']['source_range'], '=>', target['kind'], target['node_id'], target['identity']['source_range'])
print('effect_unknowns', [(u['family'], u['detail']) for u in graph['unknowns'] if u['family'] in ['timer', 'child-process', 'event', 'request', 'boundary']])
PY
  1. For the discriminating control, remove the three replacement assignments into a separate application directory, analyze it, and print the same effect families' relations.
mkdir "$work/control"
python3 - "$work" <<'PYCONTROL'
import pathlib, sys
work = pathlib.Path(sys.argv[1])
replacements = {
    'setTimeout = () => null;',
    'fetch = () => null;',
    'JSON.parse = () => null;',
}
lines = (work / 'app/main.mjs').read_text().splitlines()
(work / 'control/main.mjs').write_text(
    '\n'.join(line for line in lines if line not in replacements) + '\n')
PYCONTROL
env HOME="$work/home" node scripts/rea.mjs analyze-javascript-application "$work/control" --format json > "$work/control-evidence.json"
python3 - "$work/control-evidence.json" <<'PYCONTROL'
import json, pathlib, sys
with pathlib.Path(sys.argv[1]).open() as source:
    graph = json.load(source)['normalized_result']['semantic_graph']
for relation in graph['relations']:
    if relation['relation'] in ['schedules-timer', 'constructs-request', 'parses']:
        print(relation['relation'], relation['resolution'])
PYCONTROL

The separate source without these assignments was also analyzed and retained the ordinary built-in effects with resolved relationships.

Expected behavior

Calls after these visible replacements must not receive resolved built-in timer, fetch, or JSON.parse effects solely from the identifier or property spelling. Retain the local callable identity or explicit built-in identity uncertainty. Ordinary unmodified built-ins remain eligible for their documented static effects.

docs/javascript-application-workflows.md:68 through line 85 require conservative mutation handling, and docs/javascript-application-workflows.md:96 through line 106 require explicit unknowns for unresolved identities and define resolved relationships as uniquely identified admitted static targets. The graph's limitations advertise Node timer, fetch, and unshadowed JSON/global boundary extraction, rather than arbitrary local functions with matching names.

Impact and scope

Impact level: P2, supported. Explicit local replacements produce false timer, network, and parsing effects in static analysis. The reproduction runs only the analyzer and performs no target network request.

Workaround status: unverified. No public option preserving the source Evidence and filtering only effects invalidated by visible global replacements was verified. Ignoring all built-in effect families would discard valid results as well.

REA version

6.4.0 (main at 8dde6370d0)

Runtime and platform

Node.js v26.10.0; macOS 27.0.1. Architecture was not recorded for this reproduction. Dependencies were installed and the runtime was built.

Target and artifact context

A local directory containing the inline JavaScript main.mjs fixture. The retained application Evidence contains the affected timer/request/boundary nodes and their effect relations in normalized_result.semantic_graph. No native engine, browser, or external service is needed.

Interface

CLI

Earliest failure stage

Transport or result projection

Relevant output or evidence

The following is a compact transcription of the observed CLI results, rather than complete raw output.

CLI exit code: 0
main.mjs line 3: timer node; schedules-timer relation resolved
main.mjs line 5: fetch request node; constructs-request relation resolved
main.mjs line 7: JSON.parse boundary; parses relation resolved
The preceding explicit replacements do not downgrade these built-in effects.
Control without the three replacements: the same three built-in effects remain resolved.

Typed outcome or recovery expected

Rerun the fixture. No post-replacement call may retain a resolved built-in scheduling, request-construction, or JSON.parse effect. If a speculative classification remains, make its identity uncertainty explicit. Verify the corresponding calls in the separate source without the three replacement assignments still recover ordinary built-in effects.

Analysis or suspected cause

Source-derived reasoning: src/domain/javascript/javascriptSemanticAsyncEffects.ts:293 through line 305 and src/domain/javascript/javascriptSemanticDataEffects.ts:349 through line 374 admit globals through lexical and dynamic-scope checks despite the explicit ambient writes. The former admits timer names without a lexical binding; the latter admits JSON parsing and fetch identity using the corresponding unbound-name checks. These locators explain the suspected identity-admission gap; the executed public CLI Evidence supplies the failure evidence.

Additional context

Related #3315 concerns reflective replacement or deletion of recovered Webpack getters leaving Electron attribution; this report concerns direct ambient assignments leaving timer, fetch, and JSON built-in effects.

Related #3313 concerns stale release ownership after replacement of an acquired stream handle; this report concerns the identity of a built-in callable before effect creation.

Related #3328 concerns loss of endpoint metadata on a legitimate fetch response consumer; this report falsely admits a replaced fetch function as a request constructor.

Related completed #2890 concerns lexically shadowed require calls inventing application imports; this report concerns visible ambient writes without lexical bindings and different semantic extractors.

Originally created by @coygeek on GitHub (Oct 14, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3367 ### 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` continues to classify calls as Node timer scheduling, fetch request construction, and built-in JSON parsing after the corresponding globals or properties are explicitly replaced with local no-op functions. The resulting resolved effects misdescribe the application's timer, network, and parsing behavior. The CLI exits with code 0. It retains a timer node and a resolved `schedules-timer` relation on line 3, a fetch request node and a resolved `constructs-request` relation on line 5, and a `JSON.parse` boundary with a resolved `parses` relation on line 7. None of these effects is downgraded for the preceding explicit replacement. A separate control with the three replacement assignments removed retains the same three ordinary built-in effects with resolved relationships. The public CLI reproduction was observed and locally reproduced, with a complete reproduction. ### Reproduction 1. Clone a clean checkout, select the reproduced baseline, install the locked dependencies, and build the runtime. ```sh git clone https://github.com/morluto/rea.git rea-globals-repro cd rea-globals-repro git checkout 8dde6370d0809ad04cda3342d4e6f290f48aa063 npm ci npm run build:cached ``` The recorded reproduction used an installed dependency tree and built runtime. These preparation commands supply those prerequisites; they are not a claim that installation and building were independently repeated during reproduction. 2. From the repository root, create a disposable input and isolated configuration home, invoke the public CLI, and print the affected facts from its complete Evidence. The analyzer reads the target as text and does not execute its calls. ```bash work=$(mktemp -d) mkdir "$work/app" "$work/home" cat > "$work/app/main.mjs" <<'JS' function work() {} setTimeout = () => null; setTimeout(work, 100); fetch = () => null; fetch('https://example.invalid'); JSON.parse = () => null; JSON.parse('null'); JS python3 - "$work" <<'PY' import json, os, pathlib, subprocess, sys work = pathlib.Path(sys.argv[1]) child_environment = dict(os.environ, HOME=str(work / 'home')) result = subprocess.run( ['node', 'scripts/rea.mjs', 'analyze-javascript-application', str(work / 'app'), '--format', 'json'], env=child_environment, capture_output=True, text=True, check=True) evidence = json.loads(result.stdout) (work / 'evidence.json').write_text(result.stdout) graph = evidence['normalized_result']['semantic_graph'] print('exit:', result.returncode) nodes = {node['node_id']: node for node in graph['nodes']} for node in sorted(graph['nodes'], key=lambda n: ((n['identity']['source_range'] or {}).get('start', {}).get('line', 0), n['node_id'])): if node['kind'] in ['timer', 'request', 'boundary']: print('node', node['kind'], node['identity']['source_range'], node['properties']) for relation in graph['relations']: if relation['relation'] in ['schedules-timer', 'constructs-request', 'parses']: source = nodes[relation['source_node_id']] target = nodes[relation['target_node_id']] print('relation', relation['relation'], relation['resolution'], source['kind'], source['node_id'], source['identity']['source_range'], '=>', target['kind'], target['node_id'], target['identity']['source_range']) print('effect_unknowns', [(u['family'], u['detail']) for u in graph['unknowns'] if u['family'] in ['timer', 'child-process', 'event', 'request', 'boundary']]) PY ``` 3. For the discriminating control, remove the three replacement assignments into a separate application directory, analyze it, and print the same effect families' relations. ```bash mkdir "$work/control" python3 - "$work" <<'PYCONTROL' import pathlib, sys work = pathlib.Path(sys.argv[1]) replacements = { 'setTimeout = () => null;', 'fetch = () => null;', 'JSON.parse = () => null;', } lines = (work / 'app/main.mjs').read_text().splitlines() (work / 'control/main.mjs').write_text( '\n'.join(line for line in lines if line not in replacements) + '\n') PYCONTROL env HOME="$work/home" node scripts/rea.mjs analyze-javascript-application "$work/control" --format json > "$work/control-evidence.json" python3 - "$work/control-evidence.json" <<'PYCONTROL' import json, pathlib, sys with pathlib.Path(sys.argv[1]).open() as source: graph = json.load(source)['normalized_result']['semantic_graph'] for relation in graph['relations']: if relation['relation'] in ['schedules-timer', 'constructs-request', 'parses']: print(relation['relation'], relation['resolution']) PYCONTROL ``` The separate source without these assignments was also analyzed and retained the ordinary built-in effects with resolved relationships. ### Expected behavior Calls after these visible replacements must not receive resolved built-in timer, fetch, or `JSON.parse` effects solely from the identifier or property spelling. Retain the local callable identity or explicit built-in identity uncertainty. Ordinary unmodified built-ins remain eligible for their documented static effects. `docs/javascript-application-workflows.md:68` through line 85 require conservative mutation handling, and `docs/javascript-application-workflows.md:96` through line 106 require explicit unknowns for unresolved identities and define resolved relationships as uniquely identified admitted static targets. The graph's `limitations` advertise Node timer, fetch, and unshadowed JSON/global boundary extraction, rather than arbitrary local functions with matching names. ### Impact and scope Impact level: P2, supported. Explicit local replacements produce false timer, network, and parsing effects in static analysis. The reproduction runs only the analyzer and performs no target network request. Workaround status: unverified. No public option preserving the source Evidence and filtering only effects invalidated by visible global replacements was verified. Ignoring all built-in effect families would discard valid results as well. ### REA version 6.4.0 (main at 8dde6370d0809ad04cda3342d4e6f290f48aa063) ### Runtime and platform Node.js v26.10.0; macOS 27.0.1. Architecture was not recorded for this reproduction. Dependencies were installed and the runtime was built. ### Target and artifact context A local directory containing the inline JavaScript `main.mjs` fixture. The retained application Evidence contains the affected timer/request/boundary nodes and their effect relations in `normalized_result.semantic_graph`. No native engine, browser, or external service is needed. ### Interface CLI ### Earliest failure stage Transport or result projection ### Relevant output or evidence The following is a compact transcription of the observed CLI results, rather than complete raw output. ```text CLI exit code: 0 main.mjs line 3: timer node; schedules-timer relation resolved main.mjs line 5: fetch request node; constructs-request relation resolved main.mjs line 7: JSON.parse boundary; parses relation resolved The preceding explicit replacements do not downgrade these built-in effects. Control without the three replacements: the same three built-in effects remain resolved. ``` ### Typed outcome or recovery expected Rerun the fixture. No post-replacement call may retain a resolved built-in scheduling, request-construction, or `JSON.parse` effect. If a speculative classification remains, make its identity uncertainty explicit. Verify the corresponding calls in the separate source without the three replacement assignments still recover ordinary built-in effects. ### Analysis or suspected cause Source-derived reasoning: `src/domain/javascript/javascriptSemanticAsyncEffects.ts:293` through line 305 and `src/domain/javascript/javascriptSemanticDataEffects.ts:349` through line 374 admit globals through lexical and dynamic-scope checks despite the explicit ambient writes. The former admits timer names without a lexical binding; the latter admits JSON parsing and fetch identity using the corresponding unbound-name checks. These locators explain the suspected identity-admission gap; the executed public CLI Evidence supplies the failure evidence. ### Additional context Related [#3315](https://github.com/morluto/rea/issues/3315) concerns reflective replacement or deletion of recovered Webpack getters leaving Electron attribution; this report concerns direct ambient assignments leaving timer, fetch, and JSON built-in effects. Related [#3313](https://github.com/morluto/rea/issues/3313) concerns stale release ownership after replacement of an acquired stream handle; this report concerns the identity of a built-in callable before effect creation. Related [#3328](https://github.com/morluto/rea/issues/3328) concerns loss of endpoint metadata on a legitimate fetch response consumer; this report falsely admits a replaced fetch function as a request constructor. Related completed [#2890](https://github.com/morluto/rea/issues/2890) concerns lexically shadowed require calls inventing application imports; this report concerns visible ambient writes without lexical bindings and different semantic extractors.
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#1236
No description provided.