[GH-ISSUE #8544] [Bug] Module tracing applies captured-source integrity to its replacement resolver script #6637

Closed
opened 2026-11-01 21:12:51 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @morluto on GitHub (Nov 1, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/8544

Before submitting

  • Searched open and closed issues for related reports.
  • No credentials, private samples or sensitive target data.

Area

Evidence, artifacts, or reconstruction

Observed behavior

trace-web-module-imports fails for a valid import map whose integrity member covers the selected importer. Chromium checks that hash against REA's replacement resolver script, which has different bytes from the captured source. The CLI exits 1 with unreadable_output instead of returning the module relationships.

An independent Chromium control loads the same map with the original fixture bytes and resolves dep to https://app.test/dep.js. Removing the integrity member, or putting it only on the dependency URL, also makes REA succeed. This is a harness-induced mismatch, not evidence that the selected source failed its recorded digest.

Reproduction

Executed the following matrix with real Chromium, through the source adapter and through the public CLI command handler:

  1. Create an exported-script fixture using webModuleArtifactsFixture from tests/fixtures/webModuleTrace.ts. Write its manifest under a temporary root and its source to files/modules/main.js; set the script URL to https://app.test/main.js. The source is:

    globalThis.__auditResult = import.meta.resolve("dep"); export function neverCalled() { return import("dep"); }
    

    It is 111 UTF-8 bytes, ending with one newline. SHA-256 is ad4ec46cf1db33cf8036e8aa20fbc5af3bd46ee223a5ea215a8fb7a48968c234. The literal dynamic import is parsed by REA; the function is never called in the independent browser control.

  2. Write this local import map:

    {
      "imports": { "dep": "./dep.js" },
      "integrity": {
        "https://app.test/main.js": "sha256-rU7EbPHbM8+ANuiqIPvFrzvUbuIjpeohWo+3pIlowjQ="
      }
    }
    
  3. Run the CLI with REA_BROWSER_EXECUTABLE selecting the installed Chrome:

    rea trace-web-module-imports /absolute/fixture/manifest.json 0 \
      --import-map-path /absolute/fixture/import-map.json \
      --import-map-base-url https://app.test/index.html --json
    

    The executed source invocation used a disposable wrapper importing createCli and closeCliResources from src/cli.ts, calling await createCli(process.env).serve(process.argv.slice(2)), and closing CLI resources in finally; it ran under node --import tsx. This exercised the public command parser/handler and current source, not the installed npm package or executable dispatcher.

Import map CLI result
imports only Exit 0; dep resolves to https://app.test/dep.js
Valid integrity for main.js Exit 1; trusted resolver stub blocked on hash mismatch
Integrity for dep.js only Exit 0; same resolved URL

Each CLI case ran once; the adapter matrix separately reproduced the same three outcomes. All requests in these controlled browser contexts were fulfilled locally or blocked. Browsers were closed. No captured third-party source or dependency executed and no website assets were fetched.

Expected behavior

Trace resolution with the selected map while keeping its exact input identity. Separate the integrity of REA's trusted bootstrap bytes from integrity declarations about captured modules. Do not execute the selected target script just to satisfy its hash, and do not report this bootstrap mismatch as target corruption or a reason to repair an otherwise healthy browser installation.

The module trace contract promises native resolution using the selected map and a trusted replacement script, without refetching website assets or running the selected source.

Impact and scope

Import maps carrying a valid integrity declaration for the importer prevent the trace from reaching native resolution. The returned partial observation correctly retains manifest/source/map identities, but contains no completed resolution. Production frequency and other browser versions/hosts are unknown.

REA version

Source aa48d494e9726d469a1b9ed917f021d8a1a4c7ea, package 6.8.0.

Runtime and platform

Node 24.22.1; Darwin arm64; installed Chrome 156.0.8078.74; repository-pinned playwright-core. This is new bounded macOS evidence, not qualification of the documented Linux workflow. NOT_ENOUGH_INFO: published-package, stdio MCP, Linux or Windows reproduction.

Target and artifact context

A generated exported-script manifest, the 111-byte source above, and local import-map JSON. No production target or captured application.

Interface

CLI; shared native resolver adapter also exercised directly.

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

The failing CLI output contains:

code: unreadable_output
category: execution_failure
remediation: Run `rea doctor` and review the reported details ...
details.reason: Trusted resolver stub could not be loaded in the selected context.
Native diagnostics: ... Failed to find a valid digest in the 'integrity' attribute
for resource 'https://app.test/main.js' ... The resource has been blocked.

The independent native control fulfills main.js with the original fixture bytes, using the same selected import map. It successfully returns https://app.test/dep.js from import.meta.resolve("dep") without fetching that dependency.

Analysis, sibling checks and external references

Source-derived: NativeModuleResolver.ts:370 installs the complete selected map. The owner then fulfills the importer URL with its trusted stub and creates a module script at that URL without a separate bootstrap integrity declaration. Chromium therefore applies the map's original-source hash to the replacement bytes.

Searched other import-map consumers and trusted module-script injection sites. This bootstrap is centralized in NativeModuleResolver; both CLI and MCP compose it through createWebModuleTraceService. No independent second implementation was established. The dependency-integrity control shows the failure is specific to loading the importer stub, not all maps with integrity. #7392 concerns refusal of an actual captured-source digest mismatch, a different boundary that must keep failing.

Context7/DeepWiki research was checked against primary sources. DeepWiki did not locate the relevant import-map tests; direct repository inspection did:

Proposed verification: real-browser public CLI/MCP regression with valid importer integrity, dependency-only integrity, relative integrity keys, scoped imports, query/fragment importer identities, and ordinary no-integrity maps. Keep original source/map identities and target-digest refusal coverage. No provider change or fixed-result test was implemented.

Originally created by @morluto on GitHub (Nov 1, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/8544 ### Before submitting - [x] Searched open and closed issues for related reports. - [x] No credentials, private samples or sensitive target data. ### Area Evidence, artifacts, or reconstruction ### Observed behavior `trace-web-module-imports` fails for a valid import map whose `integrity` member covers the selected importer. Chromium checks that hash against REA's replacement resolver script, which has different bytes from the captured source. The CLI exits 1 with `unreadable_output` instead of returning the module relationships. An independent Chromium control loads the same map with the original fixture bytes and resolves `dep` to `https://app.test/dep.js`. Removing the integrity member, or putting it only on the dependency URL, also makes REA succeed. This is a harness-induced mismatch, not evidence that the selected source failed its recorded digest. ### Reproduction Executed the following matrix with real Chromium, through the source adapter and through the public CLI command handler: 1. Create an exported-script fixture using `webModuleArtifactsFixture` from `tests/fixtures/webModuleTrace.ts`. Write its manifest under a temporary root and its source to `files/modules/main.js`; set the script URL to `https://app.test/main.js`. The source is: ```js globalThis.__auditResult = import.meta.resolve("dep"); export function neverCalled() { return import("dep"); } ``` It is 111 UTF-8 bytes, ending with one newline. SHA-256 is `ad4ec46cf1db33cf8036e8aa20fbc5af3bd46ee223a5ea215a8fb7a48968c234`. The literal dynamic import is parsed by REA; the function is never called in the independent browser control. 2. Write this local import map: ```json { "imports": { "dep": "./dep.js" }, "integrity": { "https://app.test/main.js": "sha256-rU7EbPHbM8+ANuiqIPvFrzvUbuIjpeohWo+3pIlowjQ=" } } ``` 3. Run the CLI with `REA_BROWSER_EXECUTABLE` selecting the installed Chrome: ```sh rea trace-web-module-imports /absolute/fixture/manifest.json 0 \ --import-map-path /absolute/fixture/import-map.json \ --import-map-base-url https://app.test/index.html --json ``` The executed source invocation used a disposable wrapper importing `createCli` and `closeCliResources` from `src/cli.ts`, calling `await createCli(process.env).serve(process.argv.slice(2))`, and closing CLI resources in `finally`; it ran under `node --import tsx`. This exercised the public command parser/handler and current source, not the installed npm package or executable dispatcher. | Import map | CLI result | | --- | --- | | `imports` only | Exit 0; `dep` resolves to `https://app.test/dep.js` | | Valid integrity for `main.js` | Exit 1; trusted resolver stub blocked on hash mismatch | | Integrity for `dep.js` only | Exit 0; same resolved URL | Each CLI case ran once; the adapter matrix separately reproduced the same three outcomes. All requests in these controlled browser contexts were fulfilled locally or blocked. Browsers were closed. No captured third-party source or dependency executed and no website assets were fetched. ### Expected behavior Trace resolution with the selected map while keeping its exact input identity. Separate the integrity of REA's trusted bootstrap bytes from integrity declarations about captured modules. Do not execute the selected target script just to satisfy its hash, and do not report this bootstrap mismatch as target corruption or a reason to repair an otherwise healthy browser installation. The [module trace contract](https://github.com/morluto/rea/blob/aa48d494e9726d469a1b9ed917f021d8a1a4c7ea/docs/website-module-trace.md) promises native resolution using the selected map and a trusted replacement script, without refetching website assets or running the selected source. ### Impact and scope Import maps carrying a valid integrity declaration for the importer prevent the trace from reaching native resolution. The returned partial observation correctly retains manifest/source/map identities, but contains no completed resolution. Production frequency and other browser versions/hosts are unknown. ### REA version Source `aa48d494e9726d469a1b9ed917f021d8a1a4c7ea`, package 6.8.0. ### Runtime and platform Node 24.22.1; Darwin arm64; installed Chrome 156.0.8078.74; repository-pinned `playwright-core`. This is new bounded macOS evidence, not qualification of the documented Linux workflow. NOT_ENOUGH_INFO: published-package, stdio MCP, Linux or Windows reproduction. ### Target and artifact context A generated exported-script manifest, the 111-byte source above, and local import-map JSON. No production target or captured application. ### Interface CLI; shared native resolver adapter also exercised directly. ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence The failing CLI output contains: ```text code: unreadable_output category: execution_failure remediation: Run `rea doctor` and review the reported details ... details.reason: Trusted resolver stub could not be loaded in the selected context. Native diagnostics: ... Failed to find a valid digest in the 'integrity' attribute for resource 'https://app.test/main.js' ... The resource has been blocked. ``` The independent native control fulfills `main.js` with the original fixture bytes, using the same selected import map. It successfully returns `https://app.test/dep.js` from `import.meta.resolve("dep")` without fetching that dependency. ### Analysis, sibling checks and external references Source-derived: [`NativeModuleResolver.ts:370`](https://github.com/morluto/rea/blob/aa48d494e9726d469a1b9ed917f021d8a1a4c7ea/src/browser/modules/NativeModuleResolver.ts#L370) installs the complete selected map. The owner then fulfills the importer URL with its trusted stub and creates a module script at that URL without a separate bootstrap integrity declaration. Chromium therefore applies the map's original-source hash to the replacement bytes. Searched other import-map consumers and trusted module-script injection sites. This bootstrap is centralized in `NativeModuleResolver`; both CLI and MCP compose it through `createWebModuleTraceService`. No independent second implementation was established. The dependency-integrity control shows the failure is specific to loading the importer stub, not all maps with integrity. #7392 concerns refusal of an actual captured-source digest mismatch, a different boundary that must keep failing. Context7/DeepWiki research was checked against primary sources. DeepWiki did not locate the relevant import-map tests; direct repository inspection did: - [HTML module integrity resolution](https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-integrity-metadata) keeps integrity lookup distinct from specifier resolution. - [WPT module-script integrity cases](https://github.com/web-platform-tests/wpt/blob/69f509c1c04a5a5a8f1c6bd38428cdfc4b7aa164/import-maps/nonimport-integrity.html#L38) cover an importer using the map's integrity fallback, and a script's explicit correct integrity taking precedence. This is useful bootstrap-design precedent, not an implemented REA fix. - [Playwright `route.fulfill`](https://playwright.dev/docs/api/class-route#route-fulfill) supplies the response body; it does not make that body the original captured source. Proposed verification: real-browser public CLI/MCP regression with valid importer integrity, dependency-only integrity, relative integrity keys, scoped imports, query/fragment importer identities, and ordinary no-integrity maps. Keep original source/map identities and target-digest refusal coverage. No provider change or fixed-result test was implemented.
Author
Owner

@SII-YDD commented on GitHub (Nov 2, 2026):

I’m working on a focused bootstrap fix. On current main d3ab0faf5, real Chromium 156.0.8078.74 reproduces the importer-integrity failure (both absolute and relative keys); no-integrity and dependency-only maps resolve correctly.

The local change builds the trusted stub once, hashes those exact response bytes, and supplies that SRI explicitly on its module script. The selected map is installed unchanged; captured-source verification stays with its existing owner. It does not execute captured source or fetch its dependencies.

Executed on macOS arm64, Node 24.22.1:

  • The existing real loading oracle and compiled public CLI/stdio MCP verifier, extended in place: 15 trace cases passed, including scoped/query/fragment identities, named advertised schemas, original map/source hashes, source-tamper refusal and same-connection ping/recovery.
  • With production restored to main, that verifier fails loading the trusted stub under the valid selected map; with the correction it passes. A separate one-byte mismatch between the stub hash and response rejects all five native controls, including map-free loading.
  • Seven related test files: 90 passed. Original lifecycle/cleanup and actual launch-environment controls remain.

This is still local work, not a published or fully qualified PR. Current-main gate failures tracked by #8592 / #8596 are being checked separately; Linux module-engine verification has not run. Codex assisted the investigation and implementation.

<!-- gh-comment-id:6567451627 --> @SII-YDD commented on GitHub (Nov 2, 2026): I’m working on a focused bootstrap fix. On current `main` `d3ab0faf5`, real Chromium 156.0.8078.74 reproduces the importer-integrity failure (both absolute and relative keys); no-integrity and dependency-only maps resolve correctly. The local change builds the trusted stub once, hashes those exact response bytes, and supplies that SRI explicitly on its module script. The selected map is installed unchanged; captured-source verification stays with its existing owner. It does not execute captured source or fetch its dependencies. Executed on macOS arm64, Node 24.22.1: - The existing real loading oracle and compiled public CLI/stdio MCP verifier, extended in place: 15 trace cases passed, including scoped/query/fragment identities, named advertised schemas, original map/source hashes, source-tamper refusal and same-connection ping/recovery. - With production restored to `main`, that verifier fails loading the trusted stub under the valid selected map; with the correction it passes. A separate one-byte mismatch between the stub hash and response rejects all five native controls, including map-free loading. - Seven related test files: 90 passed. Original lifecycle/cleanup and actual launch-environment controls remain. This is still local work, not a published or fully qualified PR. Current-main gate failures tracked by #8592 / #8596 are being checked separately; Linux module-engine verification has not run. Codex assisted the investigation and implementation.
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#6637
No description provided.