piscina: Prototype-pollution gadget in ThreadPool.options allows RCE via execArgv / loadBalancer / env
What fixes it
- 5.3.2piscina, npmGitHub
npm install piscina@5.3.2 - 4.9.4piscina, npmGitHub
npm install piscina@4.9.4 - 6.0.0-rc.5piscina, npmGitHub
npm install piscina@6.0.0-rc.5
A fixed version on one release line is not the upgrade target for every line. Check your exact version and any vendor backports.
Description
Summary
A prototype-pollution gadget in ThreadPool.options allows an attacker who can pollute Object.prototype to execute arbitrary code in Piscina worker threads, invoke arbitrary functions during task scheduling, or inject environment variables into workers. The root cause is that ThreadPool.options is created as a plain object inheriting from Object.prototype, so any option without an explicit default in kDefaultOptions can be supplied via the prototype chain.
Details
In src/index.ts the ThreadPool constructor builds the resolved options object as a plain object:
this.options = { ...kDefaultOptions, ...options, filename, maxQueue: 0 }Because this object has Object.prototype as its prototype, reads for properties that are not own properties of the object and are not present in kDefaultOptions fall back to Object.prototype. This means a prototype-pollution primitive (e.g. from a vulnerable merge() or JSON.parse merge elsewhere in the application) can inject values for execArgv, env, loadBalancer, argv, workerData, resourceLimits, niceIncrement, closeTimeout, recordTiming, stricterFIFO, workerHistogram, and trackUnmanagedFds.
The most serious gadget is execArgv, which is passed directly to new Worker(..., { execArgv }). An attacker can set Object.prototype.execArgv = ['--require', '/tmp/attacker.js'], causing every worker to preload and execute the attacker-controlled module on startup.
This issue survived the fix for GHSA-x9g3-xrwr-cwfg / CVE-2026-55388 ("Prototype Pollution Gadget → RCE via inherited options.filename"). That advisory hardened the Piscina constructor's filename read and run()'s filename/name reads, but ThreadPool.options itself was not created with a null prototype. The same class of attack is therefore still possible against any option without an explicit default in kDefaultOptions.
PoC
import { resolve } from 'node:path'
import Piscina from 'piscina'
Object.prototype.execArgv = ['--require', '/tmp/attacker.js']
const pool = new Piscina({
filename: resolve(import.meta.dirname, 'worker.js'),
minThreads: 1,
maxThreads: 1,
})
await pool.run(1)/tmp/attacker.js is executed in the worker on startup. A full reproduction repository with execArgv, loadBalancer, and env vectors is available at https://github.com/Fcmam5/piscina-pp-poc.
Impact
- Remote Code Execution: via
execArgv(arbitrary--requiremodule preloaded in every worker on spawn). - Arbitrary code execution in the main thread: via
loadBalancer, an attacker-supplied function that is called during task scheduling. - Environment/CLI option injection: via
env, which is passed to each worker constructor. - Denial of Service / unexpected behavior: via other reachable options such as
workerData,resourceLimits,niceIncrement,closeTimeout,recordTiming, etc.
Anyone using Piscina in an application where Object.prototype can be polluted (e.g. through a dependency with a prototype-pollution vulnerability) is impacted.
Text from GitHub Advisory Database
Affected versions 9
- piscina, npm>= 5.0.0, < 5.3.2GitHub
- piscina, npm< 4.9.4GitHub
- piscina, npm>= 6.0.0-rc.1, < 6.0.0-rc.5GitHub
- piscinaaffected: < 4.9.4CVE List
- piscinaaffected: >= 5.0.0, < 5.3.2CVE List
- piscinaaffected: >= 6.0.0-rc.1, < 6.0.0-rc.5CVE List
- piscina, npmfrom 5.0.0, fixed in 5.3.2OSV
- piscina, npmfrom first release, fixed in 4.9.4OSV
- piscina, npmfrom 6.0.0-rc.1, fixed in 6.0.0-rc.5OSV
References 10
- github.com/piscinajs/piscina/commit/0cb12fca37f526065b072592afe954574dcc656fFixvia CVE List
- github.com/piscinajs/piscina/commit/2f69f67159a0e48b38fd61fa4a91c2fdc19fff72Fixvia CVE List
- github.com/piscinajs/piscina/commit/5be7bbb19e3787bb698862cd516121a578d690f7Fixvia CVE List
- github.com/piscinajs/piscina/commit/bebbda255c2981cecddd36b171b94be2fd41c9a6Fixvia CVE List
- github.com/piscinajs/piscina/releases/tag/v4.9.4Fixvia CVE List
- github.com/piscinajs/piscina/releases/tag/v5.3.2Fixvia CVE List
- github.com/piscinajs/piscina/releases/tag/v6.0.0-rc.5Fixvia CVE List
- github.com/advisories/GHSA-67c8-pqhq-4rmxAdvisoryvia GitHub
- github.com/piscinajs/piscina/security/advisories/GHSA-67c8-pqhq-4rmxExploitvia CVE List
- nvd.nist.gov/vuln/detail/CVE-2026-102992Referencevia GitHub