Tinypool 2.1.1 修補 worker 選項原型污染,可致 RCE
GitHub Advisory Database · 2026-10-05
Tinypool 把 worker 選項從一個繼承 Object.prototype 的普通物件上直接讀出來,只要任何地方污染了原型,攻擊者設定的 execArgv 與 env 就會原封不動傳給 new Worker()。這個缺陷編號 CVE-2026-104848(GHSA-5gmw-xhrv-c9v3),CVSS v4 為 9.5(Critical),影響 npm 套件 tinypool ≤ 2.1.0,2.1.1 已修補。
原本的問題
JavaScript 的普通物件讀取屬性時,找不到自有屬性就會沿著原型鏈往上找。只要程式把「沒設定」當成預設值,就等於信任了 Object.prototype。Tinypool 對 env、argv、execArgv、resourceLimits 這四個傳給 Worker 的選項都是這種寫法,而其中 execArgv 與 env 恰好能控制 Node.js 啟動時載入什麼程式。
這也是為什麼 --require 這類旗標會成為利用手段:Node.js 在 worker 啟動階段就會先載入指定檔案,早於任何應用程式碼。攻擊者不需要碰到 tinypool 的 API,只需要在同一個行程裡的任何依賴中找到一個能寫入 Object.prototype 的缺陷。
時間線
- 2026-08-23:PR #134 合併進主分支。
- 2026-08-30:GitHub 公告發布。
- 2026-10-02:NVD 收錄
CVE-2026-104848。 - 2026-10-05:公告資料庫更新。回報者為
ambushneupane。
修補在 8 月底就已存在,所以多數靠 Dependabot 或定期更新的專案可能已經升過;真正還暴露的是鎖死舊版 lockfile、或長期不更新測試工具鏈的 repo。
漏洞機制
原型污染本身通常只是「改掉別人沒設定的預設值」,要升級成 RCE 需要一個會讀取未設定屬性的 gadget。Tinypool 就是這個 gadget:公告指出它「passes worker options to new Worker() by reading them off a plain object whose prototype is Object.prototype」。
具體位置在 dist/index.js 第 508 至 511 行,公告列出的讀取方式如下:
env: this.options.env,
argv: this.options.argv,
execArgv: this.options.execArgv,
resourceLimits: this.options.resourceLimits,選項物件在第 470 行以 spread 語法建立,繼承來的屬性會被展開成自有屬性,所以就算使用者從未傳入 execArgv,被污染的值也會被當成合法設定。
公告列出兩條利用路徑:
- execArgv 污染:設定
Object.prototype.execArgv = ['--require', '/path/to/attacker.js'],所有 pool worker 啟動時都會載入攻擊者的檔案。 - env 污染:設定
Object.prototype.env = { NODE_OPTIONS: '--require /path/to/attacker.js' },透過環境變數注入達成同樣效果。
結果是「arbitrary code execution inside every worker the pool spawns, with the privileges of the host process」。
受影響版本
| 項目 | 內容 |
|---|---|
| 受影響 | tinypool ≤ 2.1.0 |
| 已修補 | 2.1.1 |
| 嚴重度 | CVSS v4 9.5,Critical |
| EPSS | 0.497%(第 40 百分位) |
公告提到 tinypool 是 Vitest 的底層依賴,Vitest 每週下載量約 4200 萬次,所以風險落在開發者機器與 CI/CD:被污染的測試依賴或測試輸入,可能讓攻擊者取得建置產物與機密。要注意的是,這個漏洞需要先有一個能污染原型的入口,單靠 tinypool 本身無法觸發;它的作用是把原本「只能改行為」的污染升級為程式碼執行。
影響範圍
公告強調的是 worker 的權限:注入的程式碼以宿主行程的權限執行,而不是被沙箱隔離的子環境。對開發者機器來說,這代表可讀取家目錄裡的 SSH 金鑰與 token;對 CI 來說,則是可讀取環境變數中的發佈憑證,甚至改動建置產物。
需要特別檢查的對象有三類:直接依賴 tinypool 的函式庫作者、使用 Vitest 的專案(透過間接依賴帶入)、以及會對外部貢獻者的 PR 自動跑測試的 CI 流程。第三類最危險,因為外部提交的測試碼本身就可能成為污染原型的入口。
修補與緩解
修補提交為 24df4e7,對應 PR #134「fix: guard worker options from proto pollution」。公告建議的方向是改用自有屬性語意,讓選項物件沒有原型:
// 舊:選項物件繼承 Object.prototype
this.options = { ...kDefaultOptions, ...options, filename, maxQueue: 0 }
// 公告建議
this.options = Object.assign(Object.create(null),
kDefaultOptions, options, { filename, maxQueue: 0 });上面「舊」的寫法是依公告對第 470 行 spread 的描述改寫,並非原始碼逐字引用;實際合併的 diff 公告頁與 PR 頁都沒有完整列出,細節以 PR 為準。
實務上該做的事:
- 用
npm ls tinypool或 lockfile 檢查是否有 ≤2.1.0,包含 Vitest 帶進來的間接依賴,升到2.1.1。 - CI 若會執行不受信任的 PR 或第三方測試碼,在升級前不要給它存取機密。
- 公告未提供不升級的緩解方式,這部分「公告未說明」。
原始來源:GitHub Advisory GHSA-5gmw-xhrv-c9v3、tinypool PR #134、tinypool v2.1.1