[GH-ISSUE #7392] [Bug] Web module tracing exits successfully after refusing a source digest mismatch #4397

Closed
opened 2026-10-31 00:49:29 +00:00 by bsakaguchi · 0 comments
Owner

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

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

CLI

Observed behavior

node scripts/rea.mjs trace-web-module-imports <manifest> 0 --json refuses a synthetic exported script with a mismatched SHA-256, emits artifact_integrity_mismatch with analysis: "not-performed", and exits 0. Adding --full-output also exits 0 and places the error under data with ok: true. The invalid-index control exits 1. This was observed on the current-main build below; no target code was executed.

Reproduction

Use Node 24.22.1 and checkout commit 86d4a21215cb0fbafaf555b5c7d9fcda3f255d82. For a fresh checkout, prepare the built runtime with npm ci && npm run build:cached; the verification checkout already had locked dependencies installed and its runtime was rebuilt. Then run this isolated synthetic fixture and the public CLI commands from the repository root:

WORK=$(mktemp -d)
export HOME="$WORK/home" XDG_CONFIG_HOME="$WORK/config" XDG_DATA_HOME="$WORK/data"
export XDG_CACHE_HOME="$WORK/cache" XDG_STATE_HOME="$WORK/state" TMPDIR="$WORK/temp"
mkdir -p "$HOME" "$XDG_CONFIG_HOME" "$XDG_DATA_HOME" "$XDG_CACHE_HOME" "$XDG_STATE_HOME" "$TMPDIR"
node <<'JS'
const fs = require('node:fs');
const path = require('node:path');
const {createHash} = require('node:crypto');
const work = process.env.TMPDIR;
const directory = path.join(work, 'export');
const relative = 'origins/' + 'a'.repeat(64) + '/app/main.js';
const source = Buffer.from('export const a = 1;\n');
fs.mkdirSync(path.dirname(path.join(directory, 'files', relative)), {recursive:true});
fs.writeFileSync(path.join(directory, 'files', relative), source);
const manifest = {
  capture_path:path.join(work,'capture.json'),
  capture_sha256:createHash('sha256').update(source).digest('hex'),
  capture_kind:'browser-scenario', source_evidence_id:null,
  capture_completeness:{status:'complete',equality_eligible:true,missing_sections:[],truncated_sections:[]},
  output_directory:directory, analysis_input:{input_path:path.join(directory,'files'),format:'directory'},
  scripts:[{source:{kind:'scenario-response',transaction_id:'request-1',request_sequence:1,response_sequence:2,status:200},
    url:'https://example.test/app/main.js',content:{state:'exported',relative_path:relative,layout:'url-path',layout_reason:null,sha256:'0'.repeat(64),bytes:source.length,media_type:'text/javascript',redacted:false,representation:'browser-decoded-response-bytes'}}],
  limitations:[]
};
const manifestPath = path.join(directory,'manifest.json');
fs.writeFileSync(manifestPath,JSON.stringify(manifest));
JS
node scripts/rea.mjs trace-web-module-imports "$TMPDIR/export/manifest.json" 0 --json > "$WORK/plain.json"
printf 'plain exit=%s\n' "$?"
node scripts/rea.mjs trace-web-module-imports "$TMPDIR/export/manifest.json" 0 --json --full-output > "$WORK/full.json"
printf 'full exit=%s\n' "$?"
node scripts/rea.mjs trace-web-module-imports "$TMPDIR/export/manifest.json" 5 --json > "$WORK/control.json"
printf 'control exit=%s\n' "$?"
node - "$WORK" <<'JS'
const fs = require('node:fs');
const path = require('node:path');
for (const name of ['plain', 'full', 'control']) {
  const value = JSON.parse(fs.readFileSync(path.join(process.argv[2], name + '.json'), 'utf8'));
  console.log(name, JSON.stringify({ok:value.ok, code:value.code, data_code:value.data?.code}));
}
JS

The plain, full-output, and invalid-index commands each reproduced the stated result twice, including one execution of the standalone shell block above.

Expected behavior

docs/cli.md:940-1001 says exit 1 means the operation could not complete, exit 0 means it completed, and output controls do not change operation status. Both invocations should exit 1 and preserve the typed integrity refusal; full output should use a failure envelope. The same nonzero integrity-error expectation is encoded in tests/process-global/cliLogging.test.ts:188-218 for a projection without the acquisition receipt.

Impact and scope

Callers relying on process status or ok can continue after a trace produced no result. This is a bounded P2 failure-reporting defect: byte verification still refuses the mismatched source, and callers can detect the failure through plain code or full-output data.code. A separate plain-JSON trace-web-source-location invocation against this fixture also emitted the integrity refusal and exited 0. MCP and matching-digest success were not tested.

REA version

Exact node scripts/rea.mjs --version output: 6.8.0. Built source commit: 86d4a21215cb0fbafaf555b5c7d9fcda3f255d82.

Runtime and platform

Node.js v24.22.1; macOS 27.0.1; uname -sm: Darwin arm64.

Target and artifact context

A local synthetic export_web_scripts manifest and a 20-byte JavaScript source. The manifest declares 64 zeroes as the source SHA-256; the actual source SHA-256 is 037ecd1db38c230c248787e60fd7bfc0cb0101b187b59535b6e7483be762d350. No capture, browser, engine, network request, or device is needed.

Interface

CLI

Earliest failure stage

Transport or result projection

Relevant output or evidence

The reproduction's reduced output is:

plain exit=0
full exit=0
control exit=1
plain {"code":"artifact_integrity_mismatch"}
full {"ok":true,"data_code":"artifact_integrity_mismatch"}
control {"code":"invalid_request"}

The actual plain error includes details.partial_observation.kind: "web-artifact-acquisition" and analysis: "not-performed". Parsing that emitted document with the built analysisErrorProjectionSchema returns unrecognized_keys at details, naming partial_observation; removing only that member from an in-memory copy makes parsing succeed. These are observed CLI and schema checks, not a proposed fix.

Typed outcome or recovery expected

Keep artifact_integrity_mismatch, its digest coordinates, and the acquired-file receipt while reporting failure consistently through the process status and full-output envelope.

Analysis or suspected cause

Source-derived: src/browser/assets/LocalWebScriptArtifacts.ts:140-188 detects the mismatch and retains acquisition facts through WebArtifactAcquisition.retain in src/browser/assets/WebArtifactReads.ts:117-131. src/domain/analysisErrorProjection.ts:128-134 adds partial_observation, but src/contracts/errorSchemas.ts:49-72 rejects extra members in integrity details. The CLI's schema-based failure detector in src/cliLogging.ts:37-75 then misses the returned error, and src/cli/cliResultOutput.ts:357-374 takes the successful full-output branch.

Additional context

Closed #113 requested reliable nonzero status for structured failures, including integrity errors; this specific failure persists on current main. Closed #3020 concerns MCP error output schemas and explicitly observed CLI exit 1, a different transport failure. Open #4601 records historical Set-limit failures in JavaScript application analysis, rather than this web-trace integrity receipt. No open exact duplicate was found. Proposed restoration checks: rerun the fixture expecting exit 1 and a failure envelope, retain the invalid-index control, and verify an unchanged matching-digest fixture still traces successfully.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: The public CLI reports success for a refused trace, including an incorrect success envelope, while retaining the failure diagnostic and performing no analysis.
  • Workaround status: available
  • Workaround basis: The observed plain output retains code=artifact_integrity_mismatch and the observed full output retains the same code under data, so callers can explicitly detect it despite the incorrect process status and ok field.
Originally created by @coygeek on GitHub (Oct 27, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/7392 ## 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 CLI ## Observed behavior `node scripts/rea.mjs trace-web-module-imports <manifest> 0 --json` refuses a synthetic exported script with a mismatched SHA-256, emits `artifact_integrity_mismatch` with `analysis: "not-performed"`, and exits 0. Adding `--full-output` also exits 0 and places the error under `data` with `ok: true`. The invalid-index control exits 1. This was observed on the current-main build below; no target code was executed. ## Reproduction Use Node 24.22.1 and checkout commit `86d4a21215cb0fbafaf555b5c7d9fcda3f255d82`. For a fresh checkout, prepare the built runtime with `npm ci && npm run build:cached`; the verification checkout already had locked dependencies installed and its runtime was rebuilt. Then run this isolated synthetic fixture and the public CLI commands from the repository root: ```sh WORK=$(mktemp -d) export HOME="$WORK/home" XDG_CONFIG_HOME="$WORK/config" XDG_DATA_HOME="$WORK/data" export XDG_CACHE_HOME="$WORK/cache" XDG_STATE_HOME="$WORK/state" TMPDIR="$WORK/temp" mkdir -p "$HOME" "$XDG_CONFIG_HOME" "$XDG_DATA_HOME" "$XDG_CACHE_HOME" "$XDG_STATE_HOME" "$TMPDIR" node <<'JS' const fs = require('node:fs'); const path = require('node:path'); const {createHash} = require('node:crypto'); const work = process.env.TMPDIR; const directory = path.join(work, 'export'); const relative = 'origins/' + 'a'.repeat(64) + '/app/main.js'; const source = Buffer.from('export const a = 1;\n'); fs.mkdirSync(path.dirname(path.join(directory, 'files', relative)), {recursive:true}); fs.writeFileSync(path.join(directory, 'files', relative), source); const manifest = { capture_path:path.join(work,'capture.json'), capture_sha256:createHash('sha256').update(source).digest('hex'), capture_kind:'browser-scenario', source_evidence_id:null, capture_completeness:{status:'complete',equality_eligible:true,missing_sections:[],truncated_sections:[]}, output_directory:directory, analysis_input:{input_path:path.join(directory,'files'),format:'directory'}, scripts:[{source:{kind:'scenario-response',transaction_id:'request-1',request_sequence:1,response_sequence:2,status:200}, url:'https://example.test/app/main.js',content:{state:'exported',relative_path:relative,layout:'url-path',layout_reason:null,sha256:'0'.repeat(64),bytes:source.length,media_type:'text/javascript',redacted:false,representation:'browser-decoded-response-bytes'}}], limitations:[] }; const manifestPath = path.join(directory,'manifest.json'); fs.writeFileSync(manifestPath,JSON.stringify(manifest)); JS node scripts/rea.mjs trace-web-module-imports "$TMPDIR/export/manifest.json" 0 --json > "$WORK/plain.json" printf 'plain exit=%s\n' "$?" node scripts/rea.mjs trace-web-module-imports "$TMPDIR/export/manifest.json" 0 --json --full-output > "$WORK/full.json" printf 'full exit=%s\n' "$?" node scripts/rea.mjs trace-web-module-imports "$TMPDIR/export/manifest.json" 5 --json > "$WORK/control.json" printf 'control exit=%s\n' "$?" node - "$WORK" <<'JS' const fs = require('node:fs'); const path = require('node:path'); for (const name of ['plain', 'full', 'control']) { const value = JSON.parse(fs.readFileSync(path.join(process.argv[2], name + '.json'), 'utf8')); console.log(name, JSON.stringify({ok:value.ok, code:value.code, data_code:value.data?.code})); } JS ``` The plain, full-output, and invalid-index commands each reproduced the stated result twice, including one execution of the standalone shell block above. ## Expected behavior `docs/cli.md:940-1001` says exit 1 means the operation could not complete, exit 0 means it completed, and output controls do not change operation status. Both invocations should exit 1 and preserve the typed integrity refusal; full output should use a failure envelope. The same nonzero integrity-error expectation is encoded in `tests/process-global/cliLogging.test.ts:188-218` for a projection without the acquisition receipt. ## Impact and scope Callers relying on process status or `ok` can continue after a trace produced no result. This is a bounded P2 failure-reporting defect: byte verification still refuses the mismatched source, and callers can detect the failure through plain `code` or full-output `data.code`. A separate plain-JSON `trace-web-source-location` invocation against this fixture also emitted the integrity refusal and exited 0. MCP and matching-digest success were not tested. ## REA version Exact `node scripts/rea.mjs --version` output: `6.8.0`. Built source commit: `86d4a21215cb0fbafaf555b5c7d9fcda3f255d82`. ## Runtime and platform Node.js `v24.22.1`; macOS `27.0.1`; `uname -sm`: `Darwin arm64`. ## Target and artifact context A local synthetic `export_web_scripts` manifest and a 20-byte JavaScript source. The manifest declares 64 zeroes as the source SHA-256; the actual source SHA-256 is `037ecd1db38c230c248787e60fd7bfc0cb0101b187b59535b6e7483be762d350`. No capture, browser, engine, network request, or device is needed. ## Interface CLI ## Earliest failure stage Transport or result projection ## Relevant output or evidence The reproduction's reduced output is: ```text plain exit=0 full exit=0 control exit=1 plain {"code":"artifact_integrity_mismatch"} full {"ok":true,"data_code":"artifact_integrity_mismatch"} control {"code":"invalid_request"} ``` The actual plain error includes `details.partial_observation.kind: "web-artifact-acquisition"` and `analysis: "not-performed"`. Parsing that emitted document with the built `analysisErrorProjectionSchema` returns `unrecognized_keys` at `details`, naming `partial_observation`; removing only that member from an in-memory copy makes parsing succeed. These are observed CLI and schema checks, not a proposed fix. ## Typed outcome or recovery expected Keep `artifact_integrity_mismatch`, its digest coordinates, and the acquired-file receipt while reporting failure consistently through the process status and full-output envelope. ## Analysis or suspected cause Source-derived: `src/browser/assets/LocalWebScriptArtifacts.ts:140-188` detects the mismatch and retains acquisition facts through `WebArtifactAcquisition.retain` in `src/browser/assets/WebArtifactReads.ts:117-131`. `src/domain/analysisErrorProjection.ts:128-134` adds `partial_observation`, but `src/contracts/errorSchemas.ts:49-72` rejects extra members in integrity details. The CLI's schema-based failure detector in `src/cliLogging.ts:37-75` then misses the returned error, and `src/cli/cliResultOutput.ts:357-374` takes the successful full-output branch. ## Additional context Closed [#113](https://github.com/morluto/rea/issues/113) requested reliable nonzero status for structured failures, including integrity errors; this specific failure persists on current main. Closed [#3020](https://github.com/morluto/rea/issues/3020) concerns MCP error output schemas and explicitly observed CLI exit 1, a different transport failure. Open [#4601](https://github.com/morluto/rea/issues/4601) records historical Set-limit failures in JavaScript application analysis, rather than this web-trace integrity receipt. No open exact duplicate was found. Proposed restoration checks: rerun the fixture expecting exit 1 and a failure envelope, retain the invalid-index control, and verify an unchanged matching-digest fixture still traces successfully. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: The public CLI reports success for a refused trace, including an incorrect success envelope, while retaining the failure diagnostic and performing no analysis. - Workaround status: available - Workaround basis: The observed plain output retains code=artifact_integrity_mismatch and the observed full output retains the same code under data, so callers can explicitly detect it despite the incorrect process status and ok field.
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#4397
No description provided.