# rolldown@1.2.11 security report (npm)

- Verdict: **Needs review** (risk level: medium)
- Scanned: 2026-10-06T14:24:45.000Z
- Files reviewed: 30
- Findings: 2 high, 9 medium, 13 low severity findings
- Report: https://security.togoder.click/npm/rolldown
- Source: Togoder Security (https://security.togoder.click), AI source-code review

## Summary

Togoder Security scanned the npm package rolldown@1.2.11 on Oct 6, 2026. An AI review of 30 source files produced 2 high, 9 medium, 13 low severity findings. The overall verdict is medium: the findings flag risky but common patterns (dynamic code, unsafe defaults, broad file or network access) rather than confirmed malware.

## Findings

### [high] Dynamic import with external input

Finding ID: `NPS-CB4CA2815594`

File: `dist/experimental-default-runtime.mjs:88`

The HMR update handler calls `import(data.path)` using the `path` field from a WebSocket message. If the WebSocket server (ws://$ADDR) is compromised or spoofed, this allows arbitrary module loading/execution from attacker-controlled paths. This is a dynamic import with computed/external input.

### [high] Remote script injection

Finding ID: `NPS-F2A43036C59C`

File: `dist/experimental-default-runtime.mjs:91`

In non-Node environments, `loadScript(data.url)` creates and appends a `<script>` element with `src` from the WebSocket message, executing remotely-supplied JavaScript. Combined with the insecure `ws://` scheme (not `wss://`), this enables remote code execution via MITM or hostile server.

### [medium] Insecure WebSocket connection

Finding ID: `NPS-0697663D6906`

File: `dist/experimental-default-runtime.mjs:66`

The WebSocket is established over unencrypted `ws://` (line 66: `new URL('ws://$ADDR')`), allowing network attackers to intercept or inject HMR messages (including `hmr:update` payloads) leading to code execution.

### [medium] Code execution via runtime message handling

Finding ID: `NPS-C9AE3FBEA6F0`

File: `dist/experimental-default-runtime.mjs:83`

Top-level code establishes a WebSocket listener that executes server-controlled module paths/URLs on receipt of `hmr:update` messages. While typical for HMR runtimes, the lack of origin/authentication validation makes this a powerful code-execution primitive.

### [medium] Dynamic import with computed input

Finding ID: `NPS-77EA1506F366`

File: `dist/parallel-plugin-worker.mjs:8`

The code dynamically imports a module using pluginInfo.fileUrl, which comes from workerData. This value is passed to the worker thread from the parent process and is not validated or restricted within this file. If an attacker can control workerData, they could load and execute arbitrary code.

### [medium] Network download and package installation at runtime

Finding ID: `NPS-29E3D70A3BD2`

File: `dist/shared/binding-BY0qR5iS.mjs`

The webcontainer fallback path, when running inside a WebContainer environment, deletes /tmp/rolldown-<version>, recreates it, and executes `pnpm i @rolldown/binding-wasm32-wasi@<version>` via childProcess.execFileSync to download and install a package from the registry at runtime. While this is a known fallback mechanism for WebContainer environments and uses the package manager rather than direct curl/wget, it performs network fetch and process spawning triggered by top-level/import-time code, which is a supply-chain risk if the registry entry is compromised.

### [medium] Dynamic module loading with environment variable path

Finding ID: `NPS-ECF18A27274C`

File: `dist/shared/binding-BY0qR5iS.mjs`

If process.env.NAPI_RS_NATIVE_LIBRARY_PATH is set, the code calls __require(process.env.NAPI_RS_NATIVE_LIBRARY_PATH) to load an arbitrary native module. This allows an attacker who can set that environment variable (or a malicious CI/dev environment) to load arbitrary code. This is an intentional escape hatch but is a risky pattern because it trusts an env var to specify a module path.

### [medium] Module hook registration at import time

Finding ID: `NPS-1C7F69324815`

File: `dist/shared/dist-DKbukT1H.mjs:84`

createOnThreadImporter() calls Module.registerHooks({ resolve }) at module load time (when createImporter() is first invoked). Module hooks allow interception and rewriting of all import resolutions in the process, which is a powerful capability that could be abused. The code propagates tracking queries through the resolve hook, mutating result.url in place for relative file imports.

### [medium] Dynamic module loading with computed input

Finding ID: `NPS-C15354C36C7A`

File: `dist/shared/dist-DKbukT1H.mjs:96`

The freshImport() function dynamically imports modules using a computed specifier built at runtime (specifier + formatTrackingQuery(...)). This pattern caches-busts Node's module cache and re-evaluates modules in a fresh graph. While this is the intended functionality of the 'fresh-import' package, dynamic imports with computed input can be abused to execute arbitrary code if the specifier is attacker-controlled.

### [medium] Dynamic code import and execution

Finding ID: `NPS-592DFA152D7E`

File: `dist/shared/load-config-iYtRcbXn.mjs`

The module dynamically imports JS/TS config files specified by the user and executes them. It also bundles TS configs via rolldown and writes the bundled output next to the original config file before importing it. While this is a legitimate config-loading feature used by Rolldown, it involves executing arbitrary code from config files and creating temporary files on disk.

### [medium] Dynamic code execution

Finding ID: `NPS-E3F7C0B8221C`

File: `dist/utils-index.mjs:1089`

The code uses `new Function(...)` inside `createMerger(fnCount)` to dynamically construct a function body from generated strings. Although the input `fnCount` is derived from the number of visitor functions (internal count, not directly attacker-controlled), this pattern is a dynamic code generation red flag. It is part of the legitimate oxc-parser visitor optimization, but it is worth noting.

### [low] Module compile cache manipulation

Finding ID: `NPS-9E6EEFC0920C`

File: `bin/cli.mjs:3`

The code uses module.enableCompileCache and module.flushCompileCache. These are experimental Node.js features that cache compiled JavaScript modules. While not inherently malicious, they can be used to persist code execution artifacts on disk and may be abused to load cached malicious code across runs. The try/catch blocks suppress errors, which is common but can hide failures.

### [low] Dynamic import

Finding ID: `NPS-2F44B3AFD7CF`

File: `bin/cli.mjs:10`

The file dynamically imports '../dist/cli.mjs' at the top level. While the path is static and relative, dynamic imports can be used to execute code that is not statically analyzable. This is a common pattern for CLI tools, but it is worth noting as it runs code at import time.

### [low] Top-level async execution

Finding ID: `NPS-651352286B35`

File: `dist/parallel-plugin-worker.mjs:6`

The code executes an async IIFE immediately on module import, registering plugins and communicating with the parent thread. This is expected for a worker entry point, but it runs automatically without any guard, which could be abused if the module is imported in an unexpected context.

### [low] Potential error information exposure

Finding ID: `NPS-14034D7277FA`

File: `dist/parallel-plugin-worker.mjs:16`

The catch block posts the entire error object to the parent thread. Depending on the nature of the error, this could leak sensitive internal details (e.g., stack traces, file paths) to the parent process.

### [low] Process spawning and shell command execution

Finding ID: `NPS-4D5E2DCA2AE3`

File: `dist/shared/binding-BY0qR5iS.mjs`

Uses child_process.execFileSync('pnpm', ['i', bindingPkg]) to install a package, and child_process.execSync('ldd --version') to detect musl libc. These are process-spawning operations, though the arguments are not derived from untrusted external input and are part of standard native binding loading logic.

### [low] File system manipulation outside package scope

Finding ID: `NPS-9F8415D2DDA2`

File: `dist/shared/binding-BY0qR5iS.mjs`

In the WebContainer fallback, the code performs fs.rmSync(baseDir, {recursive: true, force: true}) and fs.mkdirSync(baseDir, {recursive: true}) on /tmp/rolldown-<version>, which is outside the package directory. This is confined to a versioned tmp directory but still writes/removes files outside package scope.

### [low] Dynamic requires with computed/platform-specific specifiers

Finding ID: `NPS-117A09F4C043`

File: `dist/shared/binding-BY0qR5iS.mjs`

Numerous __require calls use dynamic platform/arch-derived specifiers (e.g., @rolldown/binding-linux-x64-gnu) and optional dependency loading. This is standard napi-rs binding loader behavior, but the pattern of resolving and loading many native .node binaries at import time is a supply-chain risk if any optional dependency is hijacked.

### [low] filesystem-read

Finding ID: `NPS-558A02E78BA9`

File: `dist/shared/bindingify-input-options-XrReX_BJ.mjs`

getDefaultDevRuntime reads runtime files via fs.readFileSync and fileURLToPath(import.meta.resolve(...)), but these paths are hardcoded package-internal runtime entries, not attacker-controlled or credential files.

### [low] filesystem-operations

Finding ID: `NPS-F90165C3772C`

File: `dist/shared/bindingify-input-options-XrReX_BJ.mjs`

A fsModule object exposes common async fs operations (readFile, writeFile, unlink, etc.) to plugin contexts. This is standard bundler plugin API surface for user-authored plugins, not autonomous package-install behavior.

### [low] import-time-execution

Finding ID: `NPS-8ACEFC7C45A3`

File: `dist/shared/bindingify-input-options-XrReX_BJ.mjs`

Top-level code includes Object.defineProperty patches on BindingMagicString.prototype and __decorate/lazyProp setup. These are local prototype enhancements within the package and do not perform network access, credential harvesting, or shell execution.

### [low] Dynamic string construction from runtime input

Finding ID: `NPS-0BD94FDBC3F1`

File: `dist/shared/dist-DKbukT1H.mjs:18`

buildQueryRE() builds a RegExp from queryName using escapeRegExp; escapeRegExp is properly implemented to escape regex metacharacters, so regex injection is mitigated. formatTrackingQuery() concatenates runtime values into a URL query. No direct injection risk given upstream handling, but flagged for review.

### [low] Data URL code execution via Module.register

Finding ID: `NPS-9BB06141E26F`

File: `dist/shared/dist-DKbukT1H.mjs:70`

loader_default is a data:text/javascript URL containing an embedded payload registered via Module.register() in a worker thread. While the payload is not obfuscated or encrypted (it is readable source), using a data URL as an ESM loader is an unconventional mechanism that evades normal file-based source review and could disguise malicious intent in modified versions.

### [low] File system manipulation outside package scope

Finding ID: `NPS-086994C197C3`

File: `dist/shared/load-config-iYtRcbXn.mjs`

bundleTsConfig writes a generated bundle file (with inline sourcemap) into the directory containing the user's config file and deletes it afterward. loadNativeConfig also appends a timestamp query to the import URL to bypass module caching. This is expected for a build tool's config loader but does modify the user's working directory.

## Files reviewed

- `bin/cli.mjs` (medium): The CLI entry point uses dynamic import and Node.js module compile cache APIs, which are legitimate but could be abused; no clear malicious behavior is evident.
- `dist/experimental-default-runtime.mjs` (medium): This HMR dev runtime accepts unauthenticated WebSocket messages over plaintext ws:// and executes server-supplied module paths or script URLs, which could lead to remote code execution if the HMR server or network is compromised.
- `dist/parallel-plugin-worker.mjs` (medium): The worker code dynamically imports modules from URLs provided via workerData and runs an async IIFE on import; while likely legitimate for a plugin system, the lack of validation on dynamic imports poses a moderate security risk if workerData is externally controllable.
- `dist/shared/binding-BY0qR5iS.mjs` (medium): This is a standard napi-rs native binding loader for rolldown with legitimate platform-specific dynamic requires and a WebContainer fallback that downloads and installs a WASI binding package at runtime; no data exfiltration, credential harvesting, obfuscation, or backdoor patterns were detected, but runtime package installation and env-var-controlled native library loading are noteworthy supply-chain/risk surfaces.
- `dist/shared/dist-DKbukT1H.mjs` (medium): The 'fresh-import' package purposefully registers ESM module hooks and performs dynamic imports with computed specifiers to re-evaluate modules in fresh graphs; while no overt malicious patterns (exfiltration, credential harvesting, backdoors, shells) are present, its powerful use of Module.registerHooks, data URL loaders, and dynamic imports warrants 'warning' risk for supply-chain review.
- `dist/shared/load-config-iYtRcbXn.mjs` (medium): This is Rolldown's config loader that intentionally executes user-provided config files and bundles TypeScript configs, which is expected behavior for a build tool but involves dynamic code execution and temporary file writes.
- `dist/utils-index.mjs` (medium): The file is a bundler-generated utility module for rolldown/oxc-parser AST visiting that is largely safe, with the only notable concern being a benign use of `new Function` for merging visitor callbacks.
- `dist/cli.mjs` (safe): No malicious patterns detected; this is the legitimate Rolldown CLI bundler entrypoint with standard argument parsing and build logic.
- `dist/config.mjs` (safe): The file only re-exports symbols from internal shared modules and contains no suspicious or malicious patterns.
- `dist/experimental-index.mjs` (safe): No malicious patterns detected in this rolldown experimental API barrel file; all imports are internal relative modules and the logic handles bundler/dev-engine configuration without exfiltration, credential harvesting, obfuscation, or process spawning.
- `dist/experimental-runtime-base.mjs` (safe): This file contains only standard esbuild/CommonJS/ESM interop helper functions and a base64-to-binary decoder with no network, filesystem, process, or dynamic code execution behavior.
- `dist/experimental-runtime.mjs` (safe): No malicious patterns detected; this is a module runtime implementation with no network, filesystem, process, or dynamic code execution behavior.
- `dist/filter-index.mjs` (safe): No malicious patterns detected; the file contains legitimate plugin filtering and filter-expression utilities for Rolldown/Vite with no data exfiltration, credential harvesting, obfuscation, dynamic execution, or process spawning.
- `dist/get-log-filter.mjs` (safe): Cleared by Jev triage; no further analysis needed
- `dist/index.mjs` (safe): No malicious patterns detected
- `dist/parallel-plugin.mjs` (safe): No malicious patterns detected
- `dist/parse-ast-index.mjs` (safe): No malicious patterns detected; the code is a straightforward AST parsing utility with no network, filesystem, process, or dynamic execution behavior.
- `dist/plugins-index.mjs` (safe): No malicious patterns detected; the file only exports a benign Rolldown builtin replace plugin and an esm external require plugin with no network, filesystem, process, or dynamic code execution behavior.
- `dist/shared/bindingify-input-options-XrReX_BJ.mjs` (safe): This is a legitimate Rolldown bundler internal module that wires plugin hooks, lazy properties, and binding APIs without any malicious exfiltration, credential access, shell spawning, or obfuscated payloads.
- `dist/shared/constructors-xEvVj3lN.mjs` (safe): No malicious patterns detected; the code only defines Vite/Rolldown builtin plugin constructors with no network, filesystem, process, or dynamic execution behavior.
- `dist/shared/define-config-Demdg3_4.mjs` (safe): No malicious patterns detected
- `dist/shared/error-C7pxws0W.mjs` (safe): No malicious patterns detected; the file contains only sourcemap type helpers and error normalization utilities for a build tool.
- `dist/shared/logs-D6uA606S.mjs` (safe): No malicious patterns detected; the file contains only local error/logging and code-frame utilities with no network, filesystem, process, or dynamic-execution behavior.
- `dist/shared/misc-DOSKtd97.mjs` (safe): This file contains only standard utility functions (array coercion, promise detection, error throwers, noop, and path fragment validation) with no malicious patterns such as network calls, credential harvesting, obfuscation, or dynamic execution.
- `dist/shared/normalize-string-or-regex-F14uvr6D.mjs` (safe): The code is a utility module for a Vite/Rolldown plugin system with no malicious patterns detected.
- `dist/shared/parse-WKtEKiA6.mjs` (safe): The file is a JavaScript parser wrapper around a native Rust binding (oxc-parser) that parses JS/TS ASTs; no exfiltration, credential harvesting, obfuscation, network, process, or filesystem abuse patterns were detected.
- `dist/shared/prompt-CH6TK0bC.mjs` (safe): This is a bundled consola prompt module that uses only standard Node.js APIs for terminal UI rendering with no malicious patterns detected.
- `dist/shared/resolve-tsconfig-DHwpIs5e.mjs` (safe): No malicious patterns detected; the code is a legitimate TypeScript/JavaScript transformation wrapper that delegates to a native binding.
- `dist/shared/rolldown-CgWItHdG.mjs` (safe): No malicious patterns detected; the code is a standard Rolldown bundler API wrapper with no data exfiltration, credential harvesting, obfuscation, or unauthorized system access.
- `dist/shared/watch-DUTFlQ6u.mjs` (safe): This file is a legitimate bundler watcher module (Rolldown) that manages signal handling and file watch events without any malicious patterns.

AI analysis is guidance, not a guarantee. Methodology: https://security.togoder.click/methodology
