OFFLINE
Awaiting data
Security intelligence
CriticalCritical vulnerability

CVE-2026-102992: piscina: Prototype-pollution gadget in ThreadPool.options allows RCE via execArgv / loadBalancer / env

GitHub Advisories · officialPublished Oct 1, 2026Risk 50/100

### 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: ```ts 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 ```js 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 `--require` module 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.

Upgrade affected packages to a patched version: piscina 5.3.2, piscina 4.9.4, piscina 6.0.0-rc.5.

Vendor
Not specified
Product
piscina
Exploitation
none known
Evidence
official

This record is attributed to GitHub Advisories. Exploitation status and remediation guidance are kept separate from the vulnerability's technical severity.

Open primary source