[GH-ISSUE #2841] [Bug] analyze_web_bundle drops dynamic imports written as template literals (import(./chunk.js)) #1012

Closed
opened 2026-10-15 18:30:21 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @shubhamahuja9999 on GitHub (Oct 13, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/2841

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_web_bundle (and rea analyze-web-bundle) reports no dynamic_import chunk edges for OWASP Juice Shop v20.3.0. The Angular bundle (built with rolldown) writes every lazy chunk import with an expression-free template literal:

import(`./score-board.component-XXXX.js`)

The captured-bundle inspector only accepts a quoted string literal as an import() source, so these statically known chunk edges are silently dropped and the chunk graph looks as if the app had no lazy-loaded modules.

This is the captured-bundle counterpart of #2599, which reports the same gap for source-map original sources (WebSourceMapFetcher). It is related to #2805 (template-literal route properties), but that issue and #2823 cover only route path/route properties, not import() specifiers.

Reproduction

  1. Run OWASP Juice Shop v20.3.0 locally (official juice-shop-20.3.0_node24_win32_x64.zip release, bound to 127.0.0.1:3011).
  2. Start Chrome with --remote-debugging-port=9333 --remote-debugging-address=127.0.0.1 and open http://127.0.0.1:3011/.
  3. rea list-browser-targets http://127.0.0.1:9333
  4. rea analyze-web-bundle http://127.0.0.1:9333 <targetId> --observation-ms 3000 --json
  5. Count normalized_result.observations.chunks.edges with kind: "dynamic_import".

Minimal source-level reproduction through analyzeCapturedWebBundle:

import(`./lazy.js`);    // no edge (expected one)
import("./quoted.js");  // edge
import(`./${name}.js`); // no edge (correct: computed)

Expected behavior

An import() whose source is a template literal with no substitutions has a known specifier. It should produce the same dynamic_import edge (specifier, resolved URL, location) as the equivalent quoted string. Template literals with substitutions must stay unrecognized.

Impact and scope

  • Measured on Juice Shop v20.3.0 at upstream 48a683ac8: 0 dynamic_import edges. Reading template literals as exact strings yields 8: hacking-instructor, confetti, faucet.module, wallet-web3.module, web3-sandbox.module, coding-challenge-page.component, recycle.component, about.component (hashed ./*.js chunks).
  • Affects any bundle whose bundler emits plain constants as template literals (rolldown/esbuild-style output, as in Angular 20 builds).
  • completeness.status stays complete, so callers have no signal that statically known edges were skipped.
  • No Evidence-integrity or isolation impact.

REA version

Source checkout at 48a683ac8 (6.4.0 + main).

Runtime and platform

Windows 11, Node.js 24.18.0, npm 11.16.0, Chrome 155.0.8059.39

Target and artifact context

OWASP Juice Shop v20.3.0 (public, deliberately vulnerable app, run locally on loopback); main.js is 1,242,633 bytes.

Interface

CLI analyze-web-bundle; the MCP analyze_web_bundle tool shares the same path.

Earliest failure stage

Static inspection of captured scripts (inspectNode in src/domain/webBundleAnalyzerInspection.ts).

Relevant output or evidence

Before (upstream 48a683ac8):

"chunks": { "edges": [] }   // no dynamic_import edges among 8 analyzed scripts

Analysis or suspected cause

src/domain/webBundleAnalyzerInspection.ts accepts only t.isStringLiteral(node.source) for ImportExpression. The shared exact-literal reader stringValue (javascriptStaticAnalysisHelpers, already imported in that file for call arguments) accepts expression-free template literals. src/browser/WebSourceMapFetcher.ts originalDependency has the same restriction for import() and require() (#2599). I have a fix for both sites with regression tests and will link a PR.

Originally created by @shubhamahuja9999 on GitHub (Oct 13, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/2841 ### 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_web_bundle` (and `rea analyze-web-bundle`) reports **no** `dynamic_import` chunk edges for OWASP Juice Shop v20.3.0. The Angular bundle (built with rolldown) writes every lazy chunk import with an expression-free template literal: ```js import(`./score-board.component-XXXX.js`) ``` The captured-bundle inspector only accepts a quoted string literal as an `import()` source, so these statically known chunk edges are silently dropped and the chunk graph looks as if the app had no lazy-loaded modules. This is the captured-bundle counterpart of #2599, which reports the same gap for source-map original sources (`WebSourceMapFetcher`). It is related to #2805 (template-literal **route** properties), but that issue and #2823 cover only route `path`/`route` properties, not `import()` specifiers. ### Reproduction 1. Run OWASP Juice Shop v20.3.0 locally (official `juice-shop-20.3.0_node24_win32_x64.zip` release, bound to `127.0.0.1:3011`). 2. Start Chrome with `--remote-debugging-port=9333 --remote-debugging-address=127.0.0.1` and open `http://127.0.0.1:3011/`. 3. `rea list-browser-targets http://127.0.0.1:9333` 4. `rea analyze-web-bundle http://127.0.0.1:9333 <targetId> --observation-ms 3000 --json` 5. Count `normalized_result.observations.chunks.edges` with `kind: "dynamic_import"`. Minimal source-level reproduction through `analyzeCapturedWebBundle`: ```js import(`./lazy.js`); // no edge (expected one) import("./quoted.js"); // edge import(`./${name}.js`); // no edge (correct: computed) ``` ### Expected behavior An `import()` whose source is a template literal with no substitutions has a known specifier. It should produce the same `dynamic_import` edge (specifier, resolved URL, location) as the equivalent quoted string. Template literals with substitutions must stay unrecognized. ### Impact and scope - Measured on Juice Shop v20.3.0 at upstream `48a683ac8`: **0** `dynamic_import` edges. Reading template literals as exact strings yields **8**: `hacking-instructor`, `confetti`, `faucet.module`, `wallet-web3.module`, `web3-sandbox.module`, `coding-challenge-page.component`, `recycle.component`, `about.component` (hashed `./*.js` chunks). - Affects any bundle whose bundler emits plain constants as template literals (rolldown/esbuild-style output, as in Angular 20 builds). - `completeness.status` stays `complete`, so callers have no signal that statically known edges were skipped. - No Evidence-integrity or isolation impact. ### REA version Source checkout at `48a683ac8` (6.4.0 + main). ### Runtime and platform Windows 11, Node.js 24.18.0, npm 11.16.0, Chrome 155.0.8059.39 ### Target and artifact context OWASP Juice Shop v20.3.0 (public, deliberately vulnerable app, run locally on loopback); `main.js` is 1,242,633 bytes. ### Interface CLI `analyze-web-bundle`; the MCP `analyze_web_bundle` tool shares the same path. ### Earliest failure stage Static inspection of captured scripts (`inspectNode` in `src/domain/webBundleAnalyzerInspection.ts`). ### Relevant output or evidence Before (upstream `48a683ac8`): ```json "chunks": { "edges": [] } // no dynamic_import edges among 8 analyzed scripts ``` ### Analysis or suspected cause `src/domain/webBundleAnalyzerInspection.ts` accepts only `t.isStringLiteral(node.source)` for `ImportExpression`. The shared exact-literal reader `stringValue` (`javascriptStaticAnalysisHelpers`, already imported in that file for call arguments) accepts expression-free template literals. `src/browser/WebSourceMapFetcher.ts` `originalDependency` has the same restriction for `import()` and `require()` (#2599). I have a fix for both sites with regression tests and will link a PR.
bsakaguchi 2026-10-15 18:30:21 +00:00
  • closed this issue
  • added the
    bug
    label
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#1012
No description provided.