{"id":"CVE-2026-102992","aliases":["AZL-105657","CLSA-2026-1790888307","CLSA-2026-1790937670","CLSA-2026-1791028065","CLSA-2026-1792472733","CLSA-2026-1793206864","EUVD-2026-90364","GHSA-67c8-pqhq-4rmx","ROOT-APP-NPM-CVE-2026-102992"],"status":"published","title":"piscina: Prototype-pollution gadget in ThreadPool.options allows RCE via execArgv / loadBalancer / env","description":"piscina is a node.js worker pool implementation. Prior to 4.9.4, 5.3.2, and 6.0.0-rc.5, Piscina stores ThreadPool.options in src/index.ts as a plain object that inherits from Object.prototype. Applications with a separate prototype-pollution primitive can therefore supply inherited values for security-sensitive options that do not have own defaults. An inherited execArgv value is passed to the Node.js Worker constructor and can preload attacker-controlled code in worker threads, an inherited loadBalancer function can execute during task scheduling, and inherited env values can alter worker environments. This issue is fixed in versions 4.9.4, 5.3.2, and 6.0.0-rc.5.","severity":{"version":"4.0","score":9.2,"vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","level":"critical","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-12","score":0.00512,"percentile":0.44183,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-1321"],"published":"2026-09-30T19:50:18Z","modified":"2026-11-04T02:47:05Z","fixes":[{"ecosystem":"npm","package":"piscina","fixed_in":["5.3.2","4.9.4","6.0.0-rc.5"],"sources":["ghsa","osv"]},{"ecosystem":"TuxCare:npm","package":"piscina","fixed_in":["4.8.0-tuxcare.1","4.6.1-tuxcare.2","4.4.0-tuxcare.1","5.2.0-tuxcare.1"],"sources":["osv"]},{"ecosystem":"Root:npm","package":"piscina","fixed_in":["4.9.3-aikido.1","5.3.1-aikido.1"],"sources":["osv"]},{"ecosystem":"Root:npm","package":"@rootio/piscina","fixed_in":["4.9.3-root.io.1","5.3.1-root.io.1"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"npm","package":"piscina","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"5.0.0"},{"fixed":"5.3.2"}]}]},{"source":"ghsa","ecosystem":"npm","package":"piscina","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"4.9.4"}]}]},{"source":"ghsa","ecosystem":"npm","package":"piscina","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"6.0.0-rc.1"},{"fixed":"6.0.0-rc.5"}]}]},{"source":"osv","ecosystem":"Azure Linux:3","package":"python-tensorboard","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"2.16.2-6"}]}]},{"source":"osv","ecosystem":"TuxCare:npm","package":"piscina","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"4.8.0-tuxcare.0"},{"fixed":"4.8.0-tuxcare.1"}]}],"versions":["4.8.0"]},{"source":"osv","ecosystem":"TuxCare:npm","package":"piscina","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"4.6.1-tuxcare.0"},{"fixed":"4.6.1-tuxcare.2"}]}],"versions":["4.6.1","4.6.1-tuxcare.1"]},{"source":"osv","ecosystem":"TuxCare:npm","package":"piscina","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"4.4.0-tuxcare.0"},{"fixed":"4.4.0-tuxcare.1"}]}],"versions":["4.4.0"]},{"source":"osv","ecosystem":"TuxCare:npm","package":"piscina","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"5.2.0-tuxcare.0"},{"fixed":"5.2.0-tuxcare.1"}]}],"versions":["5.2.0"]},{"source":"osv","ecosystem":"TuxCare:npm","package":"piscina","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"3.2.0-tuxcare.0"},{"fixed":"3.2.0-tuxcare.1"}]}],"versions":["3.2.0"],"bundled":2},{"source":"osv","ecosystem":"npm","package":"piscina","ranges":[{"type":"SEMVER","events":[{"introduced":"5.0.0"},{"fixed":"5.3.2"}]}]},{"source":"osv","ecosystem":"npm","package":"piscina","ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"4.9.4"}]}]},{"source":"osv","ecosystem":"npm","package":"piscina","ranges":[{"type":"SEMVER","events":[{"introduced":"6.0.0-rc.1"},{"fixed":"6.0.0-rc.5"}]}]},{"source":"osv","ecosystem":"Root:npm","package":"piscina","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"4.9.3-aikido.1"},{"fixed":"5.3.1-aikido.1"}]}]},{"source":"osv","ecosystem":"Root:npm","package":"@rootio/piscina","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"4.9.3-root.io.1"},{"fixed":"5.3.1-root.io.1"}]}]}],"products":[{"vendor":"piscinajs","product":"piscina"}],"references":[{"url":"https://github.com/piscinajs/piscina/security/advisories/GHSA-67c8-pqhq-4rmx","tags":["x_refsource_CONFIRM","exploit","advisory","web"]},{"url":"https://github.com/piscinajs/piscina/commit/0cb12fca37f526065b072592afe954574dcc656f","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/piscinajs/piscina/commit/2f69f67159a0e48b38fd61fa4a91c2fdc19fff72","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/piscinajs/piscina/commit/5be7bbb19e3787bb698862cd516121a578d690f7","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/piscinajs/piscina/commit/bebbda255c2981cecddd36b171b94be2fd41c9a6","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/piscinajs/piscina/releases/tag/v4.9.4","tags":["x_refsource_MISC","web"]},{"url":"https://github.com/piscinajs/piscina/releases/tag/v5.3.2","tags":["x_refsource_MISC","web"]},{"url":"https://github.com/piscinajs/piscina/releases/tag/v6.0.0-rc.5","tags":["x_refsource_MISC","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-102992","tags":["web","advisory"]},{"url":"https://github.com/advisories/GHSA-67c8-pqhq-4rmx"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/102xxx/CVE-2026-102992.json","tags":["advisory"]},{"url":"https://github.com/piscinajs/piscina","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-102992","title":"piscina: Prototype-pollution gadget in ThreadPool.options allows RCE via execArgv / loadBalancer / env","description":"piscina is a node.js worker pool implementation. Prior to 4.9.4, 5.3.2, and 6.0.0-rc.5, Piscina stores ThreadPool.options in src/index.ts as a plain object that inherits from Object.prototype. Applications with a separate prototype-pollution primitive can therefore supply inherited values for security-sensitive options that do not have own defaults. An inherited execArgv value is passed to the Node.js Worker constructor and can preload attacker-controlled code in worker threads, an inherited loadBalancer function can execute during task scheduling, and inherited env values can alter worker environments. This issue is fixed in versions 4.9.4, 5.3.2, and 6.0.0-rc.5.","status":"published","published":"2026-09-30T19:50:18.036Z","modified":"2026-09-30T20:47:49.562Z","cvss":[{"version":"4.0","score":9.2,"vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","level":"critical","assessor":"cna"}],"cwes":["CWE-1321"],"updated_at":"2026-11-02T13:38:54.394896Z"}],"euvd":[{"id":"EUVD-2026-90364","description":"piscina is a node.js worker pool implementation. Prior to 4.9.4, 5.3.2, and 6.0.0-rc.5, Piscina stores ThreadPool.options in src/index.ts as a plain object that inherits from Object.prototype. Applications with a separate prototype-pollution primitive can therefore supply inherited values for security-sensitive options that do not have own defaults. An inherited execArgv value is passed to the Node.js Worker constructor and can preload attacker-controlled code in worker threads, an inherited loadBalancer function can execute during task scheduling, and inherited env values can alter worker environments. This issue is fixed in versions 4.9.4, 5.3.2, and 6.0.0-rc.5.","status":"published","published":"2026-09-30T19:50:18Z","modified":"2026-09-30T20:47:49Z","cvss":[{"version":"4.0","score":9.2,"vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","level":"critical"}],"updated_at":"2026-11-02T17:27:47.293232Z"}],"ghsa":[{"id":"GHSA-67c8-pqhq-4rmx","title":"piscina: Prototype-pollution gadget in ThreadPool.options allows RCE via execArgv / loadBalancer / env","description":"### Summary\n\nA 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.\n\n### Details\n\nIn `src/index.ts` the `ThreadPool` constructor builds the resolved options object as a plain object:\n\n```ts\nthis.options = { ...kDefaultOptions, ...options, filename, maxQueue: 0 }\n```\n\nBecause 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`.\n\nThe 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.\n\nThis 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`.\n\n### PoC\n\n```js\nimport { resolve } from 'node:path'\nimport Piscina from 'piscina'\n\nObject.prototype.execArgv = ['--require', '/tmp/attacker.js']\n\nconst pool = new Piscina({\n  filename: resolve(import.meta.dirname, 'worker.js'),\n  minThreads: 1,\n  maxThreads: 1,\n})\n\nawait pool.run(1)\n```\n\n`/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.\n\n### Impact\n\n- **Remote Code Execution**: via `execArgv` (arbitrary `--require` module preloaded in every worker on spawn).\n- **Arbitrary code execution in the main thread**: via `loadBalancer`, an attacker-supplied function that is called during task scheduling.\n- **Environment/CLI option injection**: via `env`, which is passed to each worker constructor.\n- **Denial of Service / unexpected behavior**: via other reachable options such as `workerData`, `resourceLimits`, `niceIncrement`, `closeTimeout`, `recordTiming`, etc.\n\nAnyone using Piscina in an application where `Object.prototype` can be polluted (e.g. through a dependency with a prototype-pollution vulnerability) is impacted.","status":"published","published":"2026-10-01T15:03:33Z","modified":"2026-10-01T15:03:34Z","cvss":[{"version":"4.0","score":9.2,"vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","level":"critical"}],"cwes":["CWE-1321"],"updated_at":"2026-11-02T14:41:23.783773Z"}],"nvd":[{"id":"CVE-2026-102992","description":"piscina is a node.js worker pool implementation. Prior to 4.9.4, 5.3.2, and 6.0.0-rc.5, Piscina stores ThreadPool.options in src/index.ts as a plain object that inherits from Object.prototype. Applications with a separate prototype-pollution primitive can therefore supply inherited values for security-sensitive options that do not have own defaults. An inherited execArgv value is passed to the Node.js Worker constructor and can preload attacker-controlled code in worker threads, an inherited loadBalancer function can execute during task scheduling, and inherited env values can alter worker environments. This issue is fixed in versions 4.9.4, 5.3.2, and 6.0.0-rc.5.","status":"published","published":"2026-09-30T20:17:27.08Z","modified":"2026-09-30T21:17:06.45Z","cvss":[{"version":"4.0","score":9.2,"vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"critical","assessor":"secondary"}],"cwes":["CWE-1321"],"updated_at":"2026-11-02T14:12:53.097633Z"}],"osv":[{"id":"CVE-2026-102992","title":"piscina: Prototype-pollution gadget in ThreadPool.options allows RCE via execArgv / loadBalancer / env","description":"piscina is a node.js worker pool implementation. Prior to 4.9.4, 5.3.2, and 6.0.0-rc.5, Piscina stores ThreadPool.options in src/index.ts as a plain object that inherits from Object.prototype. Applications with a separate prototype-pollution primitive can therefore supply inherited values for security-sensitive options that do not have own defaults. An inherited execArgv value is passed to the Node.js Worker constructor and can preload attacker-controlled code in worker threads, an inherited loadBalancer function can execute during task scheduling, and inherited env values can alter worker environments. This issue is fixed in versions 4.9.4, 5.3.2, and 6.0.0-rc.5.","status":"published","published":"2026-09-30T19:50:18.036Z","modified":"2026-11-04T02:47:05.153797161Z","cvss":[{"version":"4.0","score":9.2,"vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","level":"critical"}],"cwes":["CWE-1321"],"updated_at":"2026-11-04T02:53:57.551196Z"},{"id":"GHSA-67c8-pqhq-4rmx","title":"piscina: Prototype-pollution gadget in ThreadPool.options allows RCE via execArgv / loadBalancer / env","description":"### Summary\n\nA 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.\n\n### Details\n\nIn `src/index.ts` the `ThreadPool` constructor builds the resolved options object as a plain object:\n\n```ts\nthis.options = { ...kDefaultOptions, ...options, filename, maxQueue: 0 }\n```\n\nBecause 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`.\n\nThe 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.\n\nThis 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`.\n\n### PoC\n\n```js\nimport { resolve } from 'node:path'\nimport Piscina from 'piscina'\n\nObject.prototype.execArgv = ['--require', '/tmp/attacker.js']\n\nconst pool = new Piscina({\n  filename: resolve(import.meta.dirname, 'worker.js'),\n  minThreads: 1,\n  maxThreads: 1,\n})\n\nawait pool.run(1)\n```\n\n`/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.\n\n### Impact\n\n- **Remote Code Execution**: via `execArgv` (arbitrary `--require` module preloaded in every worker on spawn).\n- **Arbitrary code execution in the main thread**: via `loadBalancer`, an attacker-supplied function that is called during task scheduling.\n- **Environment/CLI option injection**: via `env`, which is passed to each worker constructor.\n- **Denial of Service / unexpected behavior**: via other reachable options such as `workerData`, `resourceLimits`, `niceIncrement`, `closeTimeout`, `recordTiming`, etc.\n\nAnyone using Piscina in an application where `Object.prototype` can be polluted (e.g. through a dependency with a prototype-pollution vulnerability) is impacted.","status":"published","published":"2026-10-01T15:03:33Z","modified":"2026-10-01T15:15:09.198862052Z","cvss":[{"version":"4.0","score":9.2,"vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","level":"critical"}],"cwes":["CWE-1321"],"updated_at":"2026-11-02T15:46:39.266402Z"},{"id":"AZL-105657","title":"CVE-2026-102992 affecting package python-tensorboard 2.16.2-6","description":"piscina is a node.js worker pool implementation. Prior to 4.9.4, 5.3.2, and 6.0.0-rc.5, Piscina stores ThreadPool.options in src/index.ts as a plain object that inherits from Object.prototype. Applications with a separate prototype-pollution primitive can therefore supply inherited values for security-sensitive options that do not have own defaults. An inherited execArgv value is passed to the Node.js Worker constructor and can preload attacker-controlled code in worker threads, an inherited loadBalancer function can execute during task scheduling, and inherited env values can alter worker environments. This issue is fixed in versions 4.9.4, 5.3.2, and 6.0.0-rc.5.","status":"published","published":"2026-09-30T20:17:27Z","modified":"2026-10-15T05:37:09Z","updated_at":"2026-11-02T15:28:01.09948Z"},{"id":"CLSA-2026-1790888307","title":"TuxCare security update for piscina (1 CVE)","description":"TuxCare rebuilt piscina to address 1 CVE. Fixed in 4.8.0-tuxcare.1.","status":"published","published":"2026-10-01T20:58:27Z","modified":"2026-10-15T09:47:57.77859151Z","updated_at":"2026-11-02T15:43:48.65893Z"},{"id":"CLSA-2026-1790937670","title":"TuxCare security update for piscina (1 CVE)","description":"TuxCare rebuilt piscina to address 1 CVE. Fixed in 4.6.1-tuxcare.2.","status":"published","published":"2026-10-02T10:41:10Z","modified":"2026-10-15T09:47:38.669231292Z","updated_at":"2026-11-02T15:43:49.016976Z"},{"id":"CLSA-2026-1791028065","title":"TuxCare security update for piscina (1 CVE)","description":"TuxCare rebuilt piscina to address 1 CVE. Fixed in 4.4.0-tuxcare.1.","status":"published","published":"2026-10-03T11:47:45Z","modified":"2026-10-15T09:47:57.925558612Z","updated_at":"2026-11-02T15:43:49.016976Z"},{"id":"CLSA-2026-1792472733","title":"TuxCare security update for piscina (1 CVE)","description":"TuxCare rebuilt piscina to address 1 CVE. Fixed in 5.2.0-tuxcare.1.","status":"published","published":"2026-10-20T05:05:33Z","modified":"2026-10-20T07:15:05.020821134Z","updated_at":"2026-11-02T15:43:54.378946Z"},{"id":"CLSA-2026-1793206864","title":"TuxCare security update for piscina (2 CVEs)","description":"TuxCare rebuilt piscina to address 2 CVEs. Fixed in 3.2.0-tuxcare.1.","status":"published","published":"2026-10-28T17:01:04Z","modified":"2026-10-28T19:45:12.039967702Z","updated_at":"2026-11-02T15:43:55.669514Z"},{"id":"ROOT-APP-NPM-CVE-2026-102992","title":"CVE-2026-102992 in piscina - Patched by Root","description":"Root has patched CVE-2026-102992 in the piscina package for Root:npm. Multiple fixed versions available.","status":"published","published":"2026-10-14T12:39:29Z","modified":"2026-10-14T13:15:03.442761392Z","updated_at":"2026-11-02T15:53:27.362922Z"}]}}
