Safeguard
Open Source

npm underscore Security Review: Is It Safe to Use in 2025?

A security review of the npm underscore package, the code-injection CVE in its template function, and whether you still need it in a modern JavaScript stack.

Safeguard Research Team
Research
5 min read

The npm underscore package is safe to use if you are on version 1.12.1 or later, but older versions carry an arbitrary code-injection flaw in the template function, and many projects only pull underscore in transitively without realizing it. underscore is one of the oldest JavaScript utility libraries, providing functional helpers like map, reduce, debounce, and template. It is stable and low-churn, which is both its appeal and the reason stale, vulnerable versions linger deep in dependency trees for years.

This review covers the one CVE that matters, how to check your version, and whether underscore npm still earns its place in a modern stack.

The Vulnerability: CVE-2021-23358

The notable security issue is CVE-2021-23358, an arbitrary code-injection vulnerability in underscore's template function. The flaw sits in how template handled the variable option. When that option was drawn from _.templateSettings and populated with attacker-controlled input, it was passed unsanitized into a new Function() call during template compilation, allowing injected JavaScript to execute.

The affected range is underscore before 1.12.1 (and the 1.13.0-0 through 1.13.0-2 prerelease line). The fix, in 1.12.1, added a bareIdentifier regex to validate the variable option so it can no longer smuggle in executable code. The practical exploit condition requires attacker influence over the template's variable setting, which is not the common usage pattern, but arbitrary code execution is serious enough that upgrading is not optional.

Check What Version You Have

underscore is frequently a transitive dependency, so checking your top-level package.json is not enough. Query the resolved tree:

npm ls underscore

That prints every path that resolves underscore and the exact version each one got. If any resolve below 1.12.1, you have the vulnerable code somewhere. To audit against known advisories directly:

npm audit

If the vulnerable copy is nested under another package you do not control, you may need npm overrides (or Yarn resolutions) to force a safe version:

{
  "overrides": {
    "underscore": ">=1.12.1"
  }
}

Use overrides carefully and test afterward, since you are pinning a version the parent package did not choose.

Fixing It

For a direct dependency, upgrade:

npm install underscore@latest

The template API is backward compatible across this fix, so upgrading from an older 1.x to 1.13.x is low risk for typical usage. Run your tests, especially anything that renders underscore templates, and redeploy. As always, confirm the lockfile updated; a manifest bump that never regenerated package-lock.json ships the old code.

Do You Still Need underscore?

Beyond the CVE, there is a design question. Much of what underscore historically provided is now native to JavaScript. Array.prototype.map, filter, reduce, find, and spread/rest syntax cover the everyday functional helpers without any dependency. For the pieces that are not native, the ecosystem has largely converged on lodash, which offers a superset of underscore's API and supports per-function imports to keep bundles small.

That said, ripping out a stable, working dependency for its own sake is not a security improvement; it is churn. The pragmatic stance:

  • If underscore is a direct dependency you actively use, keep it patched at 1.12.1 or later. There is no urgency to remove it.
  • If it is only pulled in transitively and you never call it, that is a dependency-hygiene signal worth tracking, but the fix is still just ensuring a safe version resolves.
  • If you are starting fresh, you probably do not need underscore at all. Native methods plus targeted lodash imports cover the ground.

The Transitive-Dependency Lesson

The more interesting story here is not one CVE in one utility library; it is how a low-profile package like underscore ends up in a build no one audited. It arrives underneath something else, sits at a version frozen years ago, and never surfaces in a review because nobody consciously depends on it. When a CVE lands, teams do not even know they are affected.

This is the everyday case for software composition analysis: mapping every resolved package, including deeply nested ones, against known advisories so a vulnerable underscore copy is flagged whether or not you chose it. Compared to running npm audit by hand on each repo, continuous scanning across every project is what actually catches the stale transitive copy. If you are weighing tooling options, our Snyk comparison walks through how these approaches differ.

FAQ

Is the npm underscore package safe to use?

Yes, on version 1.12.1 or later. Versions before that are vulnerable to CVE-2021-23358, an arbitrary code-injection flaw in the template function. Check your resolved version with npm ls underscore and upgrade if it is below 1.12.1.

What is CVE-2021-23358 in underscore?

It is an arbitrary code-injection vulnerability in underscore's template function. The variable option was passed unsanitized into a new Function() call, so attacker-controlled input in that option could execute injected JavaScript. It is fixed in version 1.12.1.

Should I replace underscore with lodash or native methods?

For new projects, native array methods and targeted lodash imports usually cover everything underscore did, so you may not need it. For an existing project where underscore works and is patched, replacing it is optional churn rather than a security fix.

How do I find underscore if it is a transitive dependency?

Run npm ls underscore to see every path in your dependency tree that resolves it and the exact version each got. If a nested vulnerable copy is not directly upgradeable, use npm overrides or Yarn resolutions to force a safe version, then test.

Never miss an update

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