[GH-ISSUE #3355] [Bug] Child-process stdio ignores option overwrites and claims resolved unknown options #1222

Closed
opened 2026-10-16 21:02:59 +00:00 by bsakaguchi · 1 comment
Owner

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

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

Static application analysis through analyze-javascript-application reports obsolete or unavailable child-process stdio options as resolved facts. A later duplicate property does not replace the first value, and an unknown spread or options expression does not keep stdio unresolved. This can misdescribe a recovered child process's input/output connections.

All four child-process nodes are retained, and every connects-stdio relation has resolution: "resolved". Their stdio modes, ordered by source line, are pipe, inherit, pipe, and default. The child-process family has no unknown entries for the unavailable stdio values. The single-property control reports the expected inherit mode.

Reproduction

  1. Start from a clean checkout at 8dde6370d0809ad04cda3342d4e6f290f48aa063. From the repository root, install locked dependencies and build the runtime:
npm ci
npm run build:cached

The observed run used installed dependencies and a built runtime.

  1. Run the following from the repository root. It creates a disposable input and isolated configuration home, invokes the public CLI, and prints 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'
import { spawn } from 'node:child_process';
function run(options) {
  spawn('program', [], { stdio: 'pipe', stdio: 'inherit' });
  spawn('program', [], { stdio: 'inherit' });
  spawn('program', [], { stdio: 'pipe', ...options });
  spawn('program', [], options);
}
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 ['child-process']:
        print('node', node['kind'], node['identity']['source_range'], node['properties'])
for relation in graph['relations']:
    if relation['relation'] in ['connects-stdio']:
        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. Inspect normalized_result.semantic_graph.nodes for the child-process stdio values and normalized_result.semantic_graph.relations for connects-stdio. Compare the duplicate-property call with the single-property control and check the unknown spread and unknown options-expression calls.

Expected behavior

The duplicate-property call has stdio mode inherit, just like the single-property control. The calls with an unknown options spread or options expression must keep stdio uncertainty rather than claim pipe or a proven default. Literal object options follow JavaScript property replacement order; an unavailable final option cannot establish an exact connection mode.

docs/javascript-application-workflows.md:68-85 and docs/javascript-application-workflows.md:96-106 require object writes/spreads, unsupported shapes, and unresolved facts to retain uncertainty. The child-process extractor's advertised literal argv/env/stdio scope is retained in the semantic graph's limitations.

Impact and scope

The supported impact level is P2. The static graph gives incorrect stdio facts for bounded, supported source patterns, affecting child-process analysis without changing or executing the target.

Workaround status is unverified. The single explicit final property control was verified, but rewriting unknown options into literals changes the analyzed artifact and does not recover the original Evidence. No public option that preserves the supplied artifact and avoids these claims was verified.

REA version

6.4.0 (main at 8dde6370d0)

Runtime and platform

Node.js v26.10.0; macOS 27.0.1. Architecture was not recorded in the supplied reproduction evidence.

Target and artifact context

A local directory containing the inline JavaScript fixture main.mjs, analyzed as text. No native engine, browser, or external service is needed. The complete Evidence is saved as evidence.json under the disposable work directory.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

The observed fields, summarized in source-line order, were:

exit: 0
child-process stdio modes: pipe, inherit, pipe, default
all connects-stdio relations: resolution="resolved"
child-process unknown entries for unavailable stdio values: none
single-property control: inherit

The public CLI reproduction is complete and locally reproduced; these observations come from the executed Evidence.

Typed outcome or recovery expected

Rerun the exact fixture. The duplicate-property call must report inherit and retain a resolved stdio relation. The unknown spread and unknown options calls must not report a resolved fixed stdio mode; retain their uncertainty explicitly. Preserve all four child-process nodes and the single-property control's resolved inherit mode.

Analysis or suspected cause

Source-derived reasoning: src/domain/javascript/javascriptSemanticChildProcesses.ts:304-348 reads only object-expression options and selects the first matching property. The object reader does not account for a later duplicate property replacing an earlier stdio value or for an unknown spread replacing that value. An unavailable options expression becomes no literal options object, which the stdio reader treats as a default rather than preserving uncertainty. This source behavior is consistent with the observed node properties and resolved relations.

Additional context

  • Related completed issue #2571 concerns options disappearing when argv is omitted; this reproduction supplies explicit empty argv and concerns property replacement and unknown option values.
  • Related completed issue #2572 concerns unknown kill signals becoming SIGTERM; this report concerns stdio modes and connects-stdio relations.
  • Related completed issue #2746 concerns false exact counts from spread argv arrays; this report concerns effective stdio properties in options objects or expressions.
  • Related completed issue #1237 concerns stale Electron BrowserWindow preload associations after option overrides; this report concerns the separate child-process stdio extractor and graph relations.
  • Related completed issue #3212 concerns omitted nested ContextBridge members after a leading spread; this report concerns false resolved stdio values after overrides or unavailable options.
Originally created by @coygeek on GitHub (Oct 14, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3355 ### 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 Static application analysis through `analyze-javascript-application` reports obsolete or unavailable child-process stdio options as resolved facts. A later duplicate property does not replace the first value, and an unknown spread or options expression does not keep stdio unresolved. This can misdescribe a recovered child process's input/output connections. All four child-process nodes are retained, and every `connects-stdio` relation has `resolution: "resolved"`. Their stdio modes, ordered by source line, are `pipe`, `inherit`, `pipe`, and `default`. The child-process family has no unknown entries for the unavailable stdio values. The single-property control reports the expected `inherit` mode. ### Reproduction 1. Start from a clean checkout at `8dde6370d0809ad04cda3342d4e6f290f48aa063`. From the repository root, install locked dependencies and build the runtime: ```sh npm ci npm run build:cached ``` The observed run used installed dependencies and a built runtime. 2. Run the following from the repository root. It creates a disposable input and isolated configuration home, invokes the public CLI, and prints 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' import { spawn } from 'node:child_process'; function run(options) { spawn('program', [], { stdio: 'pipe', stdio: 'inherit' }); spawn('program', [], { stdio: 'inherit' }); spawn('program', [], { stdio: 'pipe', ...options }); spawn('program', [], options); } 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 ['child-process']: print('node', node['kind'], node['identity']['source_range'], node['properties']) for relation in graph['relations']: if relation['relation'] in ['connects-stdio']: 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. Inspect `normalized_result.semantic_graph.nodes` for the child-process stdio values and `normalized_result.semantic_graph.relations` for `connects-stdio`. Compare the duplicate-property call with the single-property control and check the unknown spread and unknown options-expression calls. ### Expected behavior The duplicate-property call has stdio mode `inherit`, just like the single-property control. The calls with an unknown options spread or options expression must keep stdio uncertainty rather than claim `pipe` or a proven default. Literal object options follow JavaScript property replacement order; an unavailable final option cannot establish an exact connection mode. `docs/javascript-application-workflows.md:68-85` and `docs/javascript-application-workflows.md:96-106` require object writes/spreads, unsupported shapes, and unresolved facts to retain uncertainty. The child-process extractor's advertised literal argv/env/stdio scope is retained in the semantic graph's `limitations`. ### Impact and scope The supported impact level is P2. The static graph gives incorrect stdio facts for bounded, supported source patterns, affecting child-process analysis without changing or executing the target. Workaround status is unverified. The single explicit final property control was verified, but rewriting unknown options into literals changes the analyzed artifact and does not recover the original Evidence. No public option that preserves the supplied artifact and avoids these claims was verified. ### REA version 6.4.0 (main at 8dde6370d0809ad04cda3342d4e6f290f48aa063) ### Runtime and platform Node.js v26.10.0; macOS 27.0.1. Architecture was not recorded in the supplied reproduction evidence. ### Target and artifact context A local directory containing the inline JavaScript fixture `main.mjs`, analyzed as text. No native engine, browser, or external service is needed. The complete Evidence is saved as `evidence.json` under the disposable work directory. ### Interface CLI ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence The observed fields, summarized in source-line order, were: ```text exit: 0 child-process stdio modes: pipe, inherit, pipe, default all connects-stdio relations: resolution="resolved" child-process unknown entries for unavailable stdio values: none single-property control: inherit ``` The public CLI reproduction is complete and locally reproduced; these observations come from the executed Evidence. ### Typed outcome or recovery expected Rerun the exact fixture. The duplicate-property call must report `inherit` and retain a resolved stdio relation. The unknown spread and unknown options calls must not report a resolved fixed stdio mode; retain their uncertainty explicitly. Preserve all four child-process nodes and the single-property control's resolved `inherit` mode. ### Analysis or suspected cause Source-derived reasoning: `src/domain/javascript/javascriptSemanticChildProcesses.ts:304-348` reads only object-expression options and selects the first matching property. The object reader does not account for a later duplicate property replacing an earlier stdio value or for an unknown spread replacing that value. An unavailable options expression becomes no literal options object, which the stdio reader treats as a default rather than preserving uncertainty. This source behavior is consistent with the observed node properties and resolved relations. ### Additional context - Related completed issue [#2571](https://github.com/morluto/rea/issues/2571) concerns options disappearing when argv is omitted; this reproduction supplies explicit empty argv and concerns property replacement and unknown option values. - Related completed issue [#2572](https://github.com/morluto/rea/issues/2572) concerns unknown kill signals becoming SIGTERM; this report concerns stdio modes and `connects-stdio` relations. - Related completed issue [#2746](https://github.com/morluto/rea/issues/2746) concerns false exact counts from spread argv arrays; this report concerns effective stdio properties in options objects or expressions. - Related completed issue [#1237](https://github.com/morluto/rea/issues/1237) concerns stale Electron BrowserWindow preload associations after option overrides; this report concerns the separate child-process stdio extractor and graph relations. - Related completed issue [#3212](https://github.com/morluto/rea/issues/3212) concerns omitted nested ContextBridge members after a leading spread; this report concerns false resolved stdio values after overrides or unavailable options.
Author
Owner

@Xinglan233 commented on GitHub (Oct 16, 2026):

I've opened #4336 to fix this, with regression tests for option overrides and unknown stdio values.

<!-- gh-comment-id:6206761437 --> @Xinglan233 commented on GitHub (Oct 16, 2026): I've opened #4336 to fix this, with regression tests for option overrides and unknown stdio values.
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#1222
No description provided.