[GH-ISSUE #3309] [Bug] Browser Worker strings resolve relative to the script instead of the document #1180

Closed
opened 2026-10-15 18:31:48 +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/3309

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 maps new Worker("./worker.js") in scripts/main.js to scripts/worker.js. When index.html loads that script, the browser actually loads worker.js relative to the document. REA reports the wrong file as resolved with high confidence. The explicit new URL("./worker.js", import.meta.url) form correctly uses the script directory, but the returned graph treats both forms alike.

The analysis exited successfully. The public CLI graph, both browser worker messages, and loopback-server requests were executed and inspected. The server received requests for both distinct worker files. A separate run containing only the string-worker script reproduced the same incorrect static mapping and the document-relative browser load.

Reproduction

  1. Start in a clean source checkout at revision 8dde6370d0809ad04cda3342d4e6f290f48aa063, install the locked dependencies, and build the runtime:
npm ci
npm run build:cached
  1. From the repository root, create the local application fixture and an isolated configuration home, run static analysis, and project its worker observations:
mkdir -p sample-worker/scripts repro-home
cat > sample-worker/index.html <<'HTML'
<!doctype html><script src="scripts/main.js"></script><script type="module" src="scripts/module.mjs"></script>
HTML
cat > sample-worker/scripts/main.js <<'JS'
const worker = new Worker("./worker.js");
worker.onmessage = event => { window.result = event.data; worker.terminate(); };
JS
cat > sample-worker/scripts/module.mjs <<'JS'
const worker = new Worker(new URL("./worker.js", import.meta.url));
worker.onmessage = event => { window.moduleResult = event.data; worker.terminate(); };
JS
cat > sample-worker/worker.js <<'JS'
postMessage({selected: "document", url: self.location.href});
JS
cat > sample-worker/scripts/worker.js <<'JS'
postMessage({selected: "script", url: self.location.href});
JS
HOME="$PWD/repro-home" node scripts/rea.mjs analyze-javascript-application "$PWD/sample-worker" --format json > worker-evidence.json
python3 - <<'PYTHON'
import json
result = json.load(open("worker-evidence.json"))["normalized_result"]
rows = []
for node in result["graph"]["nodes"]:
    if node["kind"] != "worker":
        continue
    for observation in node["observations"]:
        evidence = observation["evidence"]
        location = evidence["location"]
        properties = observation["properties"]
        rows.append((location["value"]["source"], properties["resolved_path"], properties["resolution_status"], evidence["confidence"]))
for row in sorted(rows):
    print(*row)
PYTHON
  1. Start a server for only this fixture:
python3 -m http.server 48761 --bind 127.0.0.1 --directory sample-worker
  1. Open http://127.0.0.1:48761/index.html in a Chromium-family browser. After both workers post a message, evaluate this expression in the page's developer console:
JSON.stringify({stringWorker: window.result, moduleWorker: window.moduleResult})
  1. Close the page and stop the server with Ctrl-C. Both fixture workers terminate after returning their selected path.

Expected behavior

The plain browser Worker string uses the document's base URL, whereas the explicit import.meta.url expression uses the module's URL. In this fixture, the string form must not acquire a resolved mapping to scripts/worker.js. If static analysis establishes the owning document, its target is worker.js; otherwise its base must remain unknown. The explicit module-relative control should still resolve to scripts/worker.js.

docs/javascript-artifact-reconstruction.md:215-223 includes worker recovery, and AGENTS.md:21 requires unresolved identity or applicability to remain unknown. The executed browser fixture establishes the two distinct URL bases for the supported literal and explicit module-relative forms.

Impact and scope

Impact level: P2. Assessment status: supported. Browser applications that load JavaScript from a different directory than their document can receive a resolved worker relationship to a different existing file. This can misdirect feature tracing and worker attribution. The demonstrated failure is a wrong analysis result; no browser execution or application mutation occurs during REA analysis.

Workaround status: unverified. Running the local application can identify the worker actually loaded, as demonstrated, but no verified static-analysis option restores the original input's worker mapping. Rewriting the application to use import.meta.url changes the input and does not restore this document-relative contract.

REA version

6.4.0 (main at 8dde6370d0)

Runtime and platform

Node.js v26.10.0 and macOS 27.0.1. CPU architecture was not recorded in the reproduction evidence. The local Chromium-family browser reported Chrome/154.0.0.0 in its user agent. Browser execution used only the shown loopback fixture in a private session; REA analysis remained static.

Target and artifact context

A local HTML/JavaScript application directory containing index.html, an external classic script at scripts/main.js, an external module at scripts/module.mjs, and distinct worker files at worker.js and scripts/worker.js. Both fixture workers terminate after returning their selected path. No external target or native analysis provider is involved.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

The successful CLI analysis and projection printed the first two lines below. The browser console expression returned the final line:

scripts/main.js scripts/worker.js resolved high
scripts/module.mjs scripts/worker.js resolved high
{"stringWorker":{"selected":"document","url":"http://127.0.0.1:48761/worker.js"},"moduleWorker":{"selected":"script","url":"http://127.0.0.1:48761/scripts/worker.js"}}

The CLI returned a high-confidence resolved_path: "scripts/worker.js" for scripts/main.js. Evidence provenance: observed. Local verification: reproduced. Reproduction completeness: complete.

Typed outcome or recovery expected

Repeat the static and browser calls on the same fixture. The string-worker observation must resolve to worker.js only when its document base is established, or remain explicitly unresolved with a base limitation. It must not map to scripts/worker.js. The module-relative control must retain its resolved mapping to scripts/worker.js; both runtime messages must continue to identify the distinct files. Unknown or multiple owning documents must not force a source-relative mapping.

Analysis or suspected cause

Source-derived reasoning: src/application/javascript/JavaScriptArtifactGraphFindings.ts:444-488 locates the worker-reference projection that supplies the source script as its URL base. That shared base is consistent with the returned graph treating the plain string and explicit module-relative forms alike, despite their distinct browser targets. This source inspection explains the observed result; no implementation repair was tested.

Additional context

The existing worker CLI fixture includes a plain string constructor next to a same-directory worker without an owning HTML document. That layout cannot distinguish document-relative loading from script-relative loading. The reproduction above supplies the owning document and distinct worker files, making the wrong target observable.

  • Related #883 proposes runtime worker/source ownership capture; this report concerns an incorrect existing static Worker relationship.
  • Related #1236 covers encoded/whitespace HTML script URLs and document bases; this report concerns which owner supplies the base for a Worker constructor in an external script.
  • Related #2678 covers unrelated callee names creating false renderer/service-worker entities; this report uses a genuine Worker constructor with an incorrectly resolved target.
  • Related #2561 covers Electron preload file-URL conversion; this report concerns plain browser Worker URL bases.
  • Related #1039 covers extension/directory fallback for HTML script assets; this report uses exact existing worker filenames and the wrong base.
  • Related #2827 covers HTML query/fragment pathname admission; this report uses an ordinary Worker pathname without query or fragment bytes.
  • Related #3214 covers relative ESM tab/newline preprocessing; this report concerns document-relative Worker strings.
  • Related #2890 covers locally shadowed require calls producing false imports; this report concerns a supported Worker constructor with the wrong URL base.
Originally created by @coygeek on GitHub (Oct 14, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3309 ### 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` maps `new Worker("./worker.js")` in `scripts/main.js` to `scripts/worker.js`. When `index.html` loads that script, the browser actually loads `worker.js` relative to the document. REA reports the wrong file as resolved with high confidence. The explicit `new URL("./worker.js", import.meta.url)` form correctly uses the script directory, but the returned graph treats both forms alike. The analysis exited successfully. The public CLI graph, both browser worker messages, and loopback-server requests were executed and inspected. The server received requests for both distinct worker files. A separate run containing only the string-worker script reproduced the same incorrect static mapping and the document-relative browser load. ### Reproduction 1. Start in a clean source checkout at revision `8dde6370d0809ad04cda3342d4e6f290f48aa063`, install the locked dependencies, and build the runtime: ```bash npm ci npm run build:cached ``` 2. From the repository root, create the local application fixture and an isolated configuration home, run static analysis, and project its worker observations: ```bash mkdir -p sample-worker/scripts repro-home cat > sample-worker/index.html <<'HTML' <!doctype html><script src="scripts/main.js"></script><script type="module" src="scripts/module.mjs"></script> HTML cat > sample-worker/scripts/main.js <<'JS' const worker = new Worker("./worker.js"); worker.onmessage = event => { window.result = event.data; worker.terminate(); }; JS cat > sample-worker/scripts/module.mjs <<'JS' const worker = new Worker(new URL("./worker.js", import.meta.url)); worker.onmessage = event => { window.moduleResult = event.data; worker.terminate(); }; JS cat > sample-worker/worker.js <<'JS' postMessage({selected: "document", url: self.location.href}); JS cat > sample-worker/scripts/worker.js <<'JS' postMessage({selected: "script", url: self.location.href}); JS HOME="$PWD/repro-home" node scripts/rea.mjs analyze-javascript-application "$PWD/sample-worker" --format json > worker-evidence.json python3 - <<'PYTHON' import json result = json.load(open("worker-evidence.json"))["normalized_result"] rows = [] for node in result["graph"]["nodes"]: if node["kind"] != "worker": continue for observation in node["observations"]: evidence = observation["evidence"] location = evidence["location"] properties = observation["properties"] rows.append((location["value"]["source"], properties["resolved_path"], properties["resolution_status"], evidence["confidence"])) for row in sorted(rows): print(*row) PYTHON ``` 3. Start a server for only this fixture: ```bash python3 -m http.server 48761 --bind 127.0.0.1 --directory sample-worker ``` 4. Open `http://127.0.0.1:48761/index.html` in a Chromium-family browser. After both workers post a message, evaluate this expression in the page's developer console: ```javascript JSON.stringify({stringWorker: window.result, moduleWorker: window.moduleResult}) ``` 5. Close the page and stop the server with Ctrl-C. Both fixture workers terminate after returning their selected path. ### Expected behavior The plain browser Worker string uses the document's base URL, whereas the explicit `import.meta.url` expression uses the module's URL. In this fixture, the string form must not acquire a resolved mapping to `scripts/worker.js`. If static analysis establishes the owning document, its target is `worker.js`; otherwise its base must remain unknown. The explicit module-relative control should still resolve to `scripts/worker.js`. `docs/javascript-artifact-reconstruction.md:215-223` includes worker recovery, and `AGENTS.md:21` requires unresolved identity or applicability to remain unknown. The executed browser fixture establishes the two distinct URL bases for the supported literal and explicit module-relative forms. ### Impact and scope Impact level: P2. Assessment status: supported. Browser applications that load JavaScript from a different directory than their document can receive a resolved worker relationship to a different existing file. This can misdirect feature tracing and worker attribution. The demonstrated failure is a wrong analysis result; no browser execution or application mutation occurs during REA analysis. Workaround status: unverified. Running the local application can identify the worker actually loaded, as demonstrated, but no verified static-analysis option restores the original input's worker mapping. Rewriting the application to use `import.meta.url` changes the input and does not restore this document-relative contract. ### REA version 6.4.0 (main at 8dde6370d0809ad04cda3342d4e6f290f48aa063) ### Runtime and platform Node.js v26.10.0 and macOS 27.0.1. CPU architecture was not recorded in the reproduction evidence. The local Chromium-family browser reported `Chrome/154.0.0.0` in its user agent. Browser execution used only the shown loopback fixture in a private session; REA analysis remained static. ### Target and artifact context A local HTML/JavaScript application directory containing `index.html`, an external classic script at `scripts/main.js`, an external module at `scripts/module.mjs`, and distinct worker files at `worker.js` and `scripts/worker.js`. Both fixture workers terminate after returning their selected path. No external target or native analysis provider is involved. ### Interface CLI ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence The successful CLI analysis and projection printed the first two lines below. The browser console expression returned the final line: ```text scripts/main.js scripts/worker.js resolved high scripts/module.mjs scripts/worker.js resolved high {"stringWorker":{"selected":"document","url":"http://127.0.0.1:48761/worker.js"},"moduleWorker":{"selected":"script","url":"http://127.0.0.1:48761/scripts/worker.js"}} ``` The CLI returned a high-confidence `resolved_path: "scripts/worker.js"` for `scripts/main.js`. Evidence provenance: observed. Local verification: reproduced. Reproduction completeness: complete. ### Typed outcome or recovery expected Repeat the static and browser calls on the same fixture. The string-worker observation must resolve to `worker.js` only when its document base is established, or remain explicitly unresolved with a base limitation. It must not map to `scripts/worker.js`. The module-relative control must retain its resolved mapping to `scripts/worker.js`; both runtime messages must continue to identify the distinct files. Unknown or multiple owning documents must not force a source-relative mapping. ### Analysis or suspected cause Source-derived reasoning: `src/application/javascript/JavaScriptArtifactGraphFindings.ts:444-488` locates the worker-reference projection that supplies the source script as its URL base. That shared base is consistent with the returned graph treating the plain string and explicit module-relative forms alike, despite their distinct browser targets. This source inspection explains the observed result; no implementation repair was tested. ### Additional context The existing worker CLI fixture includes a plain string constructor next to a same-directory worker without an owning HTML document. That layout cannot distinguish document-relative loading from script-relative loading. The reproduction above supplies the owning document and distinct worker files, making the wrong target observable. - Related [#883](https://github.com/morluto/rea/issues/883) proposes runtime worker/source ownership capture; this report concerns an incorrect existing static Worker relationship. - Related [#1236](https://github.com/morluto/rea/issues/1236) covers encoded/whitespace HTML script URLs and document bases; this report concerns which owner supplies the base for a Worker constructor in an external script. - Related [#2678](https://github.com/morluto/rea/issues/2678) covers unrelated callee names creating false renderer/service-worker entities; this report uses a genuine Worker constructor with an incorrectly resolved target. - Related [#2561](https://github.com/morluto/rea/issues/2561) covers Electron preload file-URL conversion; this report concerns plain browser Worker URL bases. - Related [#1039](https://github.com/morluto/rea/issues/1039) covers extension/directory fallback for HTML script assets; this report uses exact existing worker filenames and the wrong base. - Related [#2827](https://github.com/morluto/rea/issues/2827) covers HTML query/fragment pathname admission; this report uses an ordinary Worker pathname without query or fragment bytes. - Related [#3214](https://github.com/morluto/rea/issues/3214) covers relative ESM tab/newline preprocessing; this report concerns document-relative Worker strings. - Related [#2890](https://github.com/morluto/rea/issues/2890) covers locally shadowed require calls producing false imports; this report concerns a supported Worker constructor with the wrong URL base.
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#1180
No description provided.