Safeguard
Open Source

Is the Cheerio npm Package Safe? A Security Review

A practitioner's look at the cheerio npm package: what it does, where its real security risk lives, and how to use it safely in production scrapers and parsers.

Priya Mehta
Security Analyst
6 min read

The cheerio npm package itself is safe to use, but its history shows that the real risk in a library like cheerio lives in its transitive dependency tree rather than its own code. Cheerio is one of the most downloaded HTML-parsing libraries on the registry, and for most teams it does exactly what it promises: give you a jQuery-style API for traversing and manipulating markup on the server. The security questions worth asking are not about cheerio's own parser but about what it pulls in underneath.

What cheerio does

Cheerio takes a string of HTML or XML, builds a DOM-like tree, and lets you query and edit it with familiar selector syntax:

const cheerio = require('cheerio');
const $ = cheerio.load('<ul><li class="item">One</li><li>Two</li></ul>');
$('.item').text(); // "One"
$('li').length;    // 2

It does not run scripts, apply CSS, or execute a browser rendering engine. That narrower scope is a security feature in itself: unlike a headless browser, cheerio will not fetch remote resources or evaluate JavaScript embedded in the page you parse. If your goal is to extract structured data from static markup, that reduced surface is a good reason to reach for it over something heavier like Puppeteer.

Where the risk actually lives

Cheerio has had very few vulnerabilities in its own code. The notable security exposure came through the dependency chain. Cheerio depends on css-select, which historically depended on nth-check, and versions of nth-check before 2.0.1 carried a Regular Expression Denial of Service flaw tracked as CVE-2021-3803. A crafted selector could force the regular expression into catastrophic backtracking and stall the event loop.

That single transitive CVE is a good lesson. When you run npm install cheerio, you are not auditing one package; you are pulling a small tree. The fix arrived in nth-check 2.0.1, and cheerio adopted the patched chain starting with the 1.0.0-rc.5 prerelease. If you are still pinned to an old 0.22.x or an early release candidate, you may be dragging along the vulnerable resolution even though your top-level version looks fine.

This is exactly the class of problem software composition analysis is built for. An SCA tool resolves the full dependency graph and flags a ReDoS buried three levels down that a manual package.json review would never surface.

Checking what your install resolved

You do not need a scanner to see the shape of the problem. npm ls walks the resolved tree:

npm ls nth-check
npm ls css-select

If either resolves below the patched version, run npm audit to confirm and then upgrade. Because the vulnerable package sits below cheerio rather than in your direct dependencies, a plain npm update cheerio may not be enough; you sometimes need an overrides block in package.json to force the transitive dependency forward:

{
  "overrides": {
    "nth-check": ">=2.0.1"
  }
}

Re-run npm ls nth-check afterward and confirm the tree deduped to the safe version.

Installing cheerio the current way

For years the community lived on 1.0.0-rc.x prereleases, and a lot of copy-pasted advice still tells people to install a release candidate explicitly. That is no longer necessary. A stable 1.0.0 line shipped in 2024, so a plain install now gets you the maintained release:

npm install cheerio

The modern package ships as ESM-first with CommonJS compatibility, so both import { load } from 'cheerio' and require('cheerio') work depending on your project setup. If you are upgrading a project that pinned npm cheerio to an old rc build, test your selectors after the bump; the internal parser moved to parse5 and htmlparser2 handling that can treat malformed markup slightly differently than very old versions did.

Safe-usage patterns for untrusted HTML

Most cheerio usage involves parsing pages you do not control, which is the situation where input-handling discipline matters. A few habits reduce your exposure:

  • Treat every parsed string as attacker-controlled. Do not pass extracted content straight into eval, a shell command, or a SQL query without validation.
  • Cap the size of HTML you accept before parsing. A ReDoS or memory-exhaustion issue is far less dangerous if you refuse a 200 MB response up front.
  • When you scrape at scale, isolate the parsing worker so that a stalled event loop from a pathological input does not take down your whole service.
  • Keep the dependency current. The single most effective control is simply not running the version with the known transitive ReDoS.

Cheerio does not sanitize output for you. If you extract HTML and re-render it into your own page, you still own the cross-site scripting risk and need to escape or sanitize on the way out.

How cheerio compares for security posture

Against a full headless browser, cheerio is the safer default when you only need static markup: no JavaScript execution, no automatic resource fetching, and a smaller dependency footprint. Against lighter regex-based "parsing," cheerio is far safer because you are not hand-rolling brittle patterns that themselves invite ReDoS. The trade-off is that cheerio cannot see content rendered by client-side JavaScript, so for single-page apps you may still need a browser, and that reintroduces the larger attack surface you avoided.

For teams standardizing on dependency hygiene across many services, it helps to fold cheerio into the same pipeline you use for everything else rather than treating scrapers as a special case. Continuous scanning catches the day a fresh transitive advisory lands, which is when it matters. If you want a primer on reading and prioritizing these alerts, the Safeguard Academy walks through the workflow.

FAQ

Does the cheerio npm package have known vulnerabilities?

Cheerio's own code has had very few security issues. The most significant exposure was transitive: a ReDoS in nth-check (CVE-2021-3803) reached through css-select, fixed in nth-check 2.0.1 and adopted by cheerio from the 1.0.0-rc.5 line onward. Keep the package current and the practical risk is low.

How do I install cheerio safely?

Run npm install cheerio to get the stable release, then run npm ls nth-check and npm audit to confirm no vulnerable transitive versions resolved. Add an overrides entry if an old version is being pulled in by another dependency.

Is npm cheerio a security risk for web scraping?

The library itself is low risk because it does not execute JavaScript or fetch remote resources. The risk in scraping comes from how you handle the extracted data. Validate and sanitize anything you pull out before using it in queries, commands, or your own rendered output.

Can I detect cheerio's transitive issues automatically?

Yes. Software composition analysis resolves the full dependency graph and flags buried advisories like the nth-check ReDoS that a top-level package.json review would miss. Running it in CI catches new advisories as they are published.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.