CVE-2026-55388 Overview
CVE-2026-55388 affects piscina, a Node.js worker pool implementation widely used to offload CPU-intensive work to background threads. Versions prior to 6.0.0-rc.2, 5.2.0, and 4.9.3 read the filename option through plain member access in both the constructor and the run() paths. When Object.prototype.filename is polluted upstream, the inherited value reaches worker_threads.Worker import, causing an attacker-controlled .mjs file to execute inside the worker. The flaw is classified under CWE-94: Improper Control of Generation of Code.
Critical Impact
A prototype pollution gadget in any dependency can be chained with piscina to achieve arbitrary code execution in Node.js worker threads.
Affected Products
- piscina versions prior to 4.9.3 (4.x branch)
- piscina versions prior to 5.2.0 (5.x branch)
- piscina versions prior to 6.0.0-rc.2 (6.x branch)
Discovery Timeline
- 2026-06-22 - CVE-2026-55388 published to NVD
- 2026-06-23 - Last updated in NVD database
Technical Details for CVE-2026-55388
Vulnerability Analysis
Piscina manages a pool of Node.js worker_threads.Worker instances and accepts a filename option specifying the module loaded inside each worker. The library reads this option through plain property access on the user-supplied options object. JavaScript property lookup walks the prototype chain when an own property is absent, so a missing filename falls through to Object.prototype.filename if one exists.
An attacker who can pollute Object.prototype through an unrelated sink, such as an unsafe merge or JSON.parse reviver in another dependency, can set Object.prototype.filename to a URL or path under their control. When the application later instantiates piscina or calls run() without explicitly supplying filename, the polluted value flows directly into the Worker import. The worker then loads and executes the attacker's .mjs file with the privileges of the Node.js process.
Network reachability is required to deliver the pollution payload, and successful exploitation depends on an existing prototype pollution primitive in the target application stack, reflected in the elevated attack complexity.
Root Cause
The root cause is unguarded prototype chain traversal during option resolution. Piscina did not use Object.hasOwn(), Object.create(null), or destructuring with explicit defaults to confine filename lookup to own properties. This permitted inherited properties on Object.prototype to satisfy the option read.
Attack Vector
Exploitation requires two preconditions: a prototype pollution sink reachable by the attacker and an application code path that invokes piscina without an explicit filename. The attacker pollutes Object.prototype.filename with a URL pointing to a malicious ES module. When piscina spawns a worker, Node.js imports the attacker module, achieving code execution inside the worker thread. No authentication or user interaction is required at the piscina layer.
A technical description is available in the piscina GitHub Security Advisory GHSA-x9g3-xrwr-cwfg.
Detection Methods for CVE-2026-55388
Indicators of Compromise
- Unexpected .mjs or remote module imports originating from Node.js worker threads in process telemetry.
- Outbound network connections from Node.js processes to untrusted hosts immediately before worker spawn events.
- Presence of vulnerable piscina versions (< 4.9.3, < 5.2.0, < 6.0.0-rc.2) in package-lock.json or node_modules.
Detection Strategies
- Run software composition analysis against project manifests to flag vulnerable piscina versions.
- Audit application code for unsafe object merge patterns, recursive Object.assign, and JSON.parse use that could enable prototype pollution.
- Inspect runtime worker spawn arguments and log the resolved filename value to identify unexpected module paths.
Monitoring Recommendations
- Enable Node.js process auditing to capture child worker creation and module load events.
- Alert on Node.js processes loading modules from filesystem paths outside the application root or from remote URLs.
- Track egress traffic from backend Node.js services for anomalies consistent with second-stage payload retrieval.
How to Mitigate CVE-2026-55388
Immediate Actions Required
- Upgrade piscina to 4.9.3, 5.2.0, or 6.0.0-rc.2 depending on the branch in use.
- Inventory transitive dependencies that bundle piscina and verify each resolves to a patched version.
- Review application code for prototype pollution sinks and remediate them in parallel with the piscina upgrade.
Patch Information
The piscina maintainers released fixes in versions 6.0.0-rc.2, 5.2.0, and 4.9.3. The patched releases constrain filename resolution to own properties, preventing inherited values from reaching worker_threads.Worker. Refer to the piscina GitHub Security Advisory GHSA-x9g3-xrwr-cwfg for release notes and commit references.
Workarounds
- Always pass an explicit filename option when constructing a Piscina instance or calling run(), eliminating reliance on default lookup.
- Freeze Object.prototype at application startup with Object.freeze(Object.prototype) to block prototype pollution writes.
- Replace unsafe deep-merge utilities with implementations that reject __proto__, constructor, and prototype keys.
# Upgrade piscina to a patched release
npm install piscina@4.9.3 # for the 4.x branch
npm install piscina@5.2.0 # for the 5.x branch
npm install piscina@6.0.0-rc.2 # for the 6.x release candidate
# Verify the resolved version across the dependency tree
npm ls piscina
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

