← Back to results
Vulnerability record · public intelligence
CVE-2026-102992
CRITICAL

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

Published Sep 30, 2026 · Updated Nov 3, 2026

Severity · CVSSCRITICAL · Not scored
Known exploitationNot listed
EPSS / likelihood0.51%
QFinch risk scoreNot available

Original summary

Normalized public record · Updated Nov 3, 2026

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

Publisher and source text is retained separately from QFinch interpretation.

QFinch assessment

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.

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

Technical context

In src/index.ts the ThreadPool constructor builds the resolved options object as a plain object: 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.

Evidence: public source records

Affected products

Affected scope reported by the available public evidence
Affected package, product or rangeFix status
Consult the referenced vendor advisory for affected versions.Consult source advisory

Matching a version is only one part of assessing exposure. QFinch has not assessed your environment on this public page.

Remediation

Apply the vendor-supported fixed release or mitigation, then verify the affected component is no longer reachable.

  1. Confirm the affected product and reachable code path exist in your environment.
  2. Apply the vendor-supported patch or mitigation from the primary advisory.
  3. Rescan the exact deployed revision and retain verification evidence.
Review primary references

Evidence coverage

FIRST-EPSSCVE-LIST-V5
SSVC priority
MEDIUM
Confidence
Not available
Aliases
None reported

Scores appear only when corresponding evidence exists. Missing data is never interpreted as zero risk.

Sources and record history

  1. Security advisory ↗
  2. Patch commit ↗
  3. Patch commit ↗
  4. Patch commit ↗
  5. Patch commit ↗
  6. Source repository ↗
  7. Source repository ↗
  8. Source repository ↗

Record history

Published Sep 30, 2026 · Last refreshed Nov 3, 2026