· 5 min read

Every CVE in lodash, Ranked by How Much You Should Actually Care

Kief Studio
Every CVE in lodash, Ranked by How Much You Should Actually Care

Good, I have everything I need. Writing the post now.


Every CVE in lodash, Ranked by How Much You Should Actually Care

Your scanner just lit up red again. lodash. Prototype pollution. CVSS 9.1. Critical.

You've seen this alert before. Probably dozens of times. And you're not sure if you should drop everything or just close the tab.

Here's the thing: lodash has exactly 10 CVEs across its entire history. Some of them are genuinely dangerous. Most of them are noise. And the CVSS scores are actively lying to you about which is which.

Let's go through all of them.

The ones that can actually hurt you

Three lodash CVEs can achieve code execution. That's it. Three out of ten.

CVE-2021-23337 (CVSS 7.2) and CVE-2026-4800 (CVSS 8.1) are both code injection through _.template(). The attack surface is the options.variable and options.imports parameters. If untrusted user input reaches those keys, an attacker can inject code into the Function() constructor that lodash uses internally.

CVE-2026-4800 is the interesting one. It's a bypass of the 2021 fix. The original patch guarded options.variable but left options.imports key names completely unprotected. Same sink, different entry point. Five years of incomplete patching.

Here's what the vulnerable pattern looks like:

// This is dangerous if userInput is attacker-controlled
const compiled = _.template('<%= data %>', {
  imports: { [userInput]: someValue }  // CVE-2026-4800
});

In practice? Most _.template() calls use hardcoded option keys. If you're building a CMS or templating engine that lets users define variable names, you have a problem. If you're using _.template() for email templates with static config, you don't.

CVE-2019-10744 (CVSS 9.1) is prototype pollution through defaultsDeep. This one matters more broadly because the pattern is common: merge user-submitted JSON with default config.

// If req.body comes from an attacker, this is exploitable
const config = _.defaultsDeep(req.body, defaultConfig);

// Attacker sends: {"__proto__": {"isAdmin": true}}
// Now every object in your process has isAdmin === true

If your API server merges request bodies through defaultsDeep or merge, update lodash. Full stop.

The prototype pollution crowd

Four more CVEs are prototype pollution variants. They all sound scary. The CVSS scores are high. And for most codebases, they're scan noise.

CVE-2018-16487 (CVSS 9.1) -- prototype pollution via merge and defaultsDeep. Fixed in 4.17.11.

CVE-2020-8203 (CVSS 7.5) -- prototype pollution via zipObjectDeep. Fixed in 4.17.20.

CVE-2025-13465 (CVSS 6.9) -- prototype pollution via _.unset and _.omit. Fixed in 4.17.23.

CVE-2026-2950 (CVSS 6.5) -- a bypass of the above using array-wrapped paths. Fixed in 4.18.0.

Notice the pattern? CVE-2025-13465 gets patched, then CVE-2026-2950 pops up because the fix only guarded string paths, not arrays. Same whack-a-mole as the template CVEs. Each patch narrows the attack surface without eliminating the root cause.

Here's the reality check on prototype pollution. Positive Security researched real-world exploitability and found "only low and medium severity vulnerabilities in real closed-source projects" and "did not immediately find any exploitable open-source applications." The vector is real, but weaponizing it requires a specific chain: attacker-controlled input hits a vulnerable merge function, AND the polluted prototype property gets consumed by security-sensitive code downstream.

No lodash prototype pollution CVE has documented exploitation in the wild.

Prototype pollution as a class HAS been exploited. CVE-2019-7609 in Kibana chained prototype pollution to RCE on Elasticsearch clusters. CVE-2026-34621 in Adobe Acrobat was actively exploited for four months before Adobe patched it. But those were in application-specific code, not lodash itself.

The ones you can ignore

Three CVEs round out the list. Unless you're running lodash 4.17.4 or earlier (released in 2017), these are already patched in your version.

CVE-2018-3721 (CVSS 5.3) -- the original prototype pollution that kicked off every other lodash CVE. Fixed in 4.17.5. Eight years ago.

CVE-2020-28500 (CVSS 5.3) -- ReDoS via toNumber and trim. You'd need to pass an absurdly long crafted string to trigger meaningful delay. Fixed in 4.17.21.

CVE-2019-1010266 (CVSS 5.3) -- DoS via a Date handler. Local attack vector. Fixed in 4.17.11.

The CVSS scores are lying

CVE-2018-16487 and CVE-2019-10744 both score 9.1 -- Critical. That score assumes the worst-case deployment: the vulnerable function receives untrusted input in a server-side context with no input validation.

But CVSS doesn't know your code. A React app using _.merge to combine two config objects that you wrote? That 9.1 is effectively a 0. A Node.js API endpoint that pipes req.body into _.defaultsDeep? Now that 9.1 is earned.

This is why your scanner lights up red and your security team panics while your engineers shrug. The score measures theoretical maximum impact, not your actual exposure.

You can check your own exposure in about 30 seconds:

# Find every lodash import in your codebase
grep -rn "require.*lodash\|from.*lodash" src/

# Then check if any of the dangerous functions touch user input
grep -rn "\.merge\|\.defaultsDeep\|\.template\|\.zipObjectDeep\|\.unset\|\.omit" src/

If the second command returns nothing, or only hits developer-controlled data, your CVE alerts are noise.

The real risk isn't the CVEs

lodash went effectively unmaintained from 2021 to late 2024. The project declared "issue bankruptcy" and closed every open issue and PR. For three years, 197,000+ npm packages that depend on lodash had no upgrade path for security fixes.

The project rebooted in 2025 with new patches. But the fix pattern -- where each patch spawns a bypass CVE -- suggests the codebase has accumulated enough complexity that patches are whack-a-mole rather than root-cause fixes.

That's the actual risk. Not any individual CVE, but the systemic fragility of depending on a package with this maintenance history, embedded this deep in the JavaScript ecosystem.

What to actually do

If you're on lodash < 4.17.21, update. That single version bump fixes the three actually-dangerous CVEs. If you can get to 4.18.1, you clear all ten.

If you're already on 4.17.21+, the remaining CVEs are prototype pollution variants that require attacker-controlled input flowing into specific functions. Grep your codebase. If you're not piping user data through merge, defaultsDeep, zipObjectDeep, unset, or omit, you're fine.

If you want to stop seeing these alerts forever, migrate to es-toolkit. It provides a es-toolkit/compat layer that's a near-drop-in replacement for lodash. Vite users can automate the switch:

npm install es-toolkit
# For Vite projects, automatic import rewriting:
npm install -D vite-plugin-es-toolkit
// vite.config.js
import { esToolkit } from 'vite-plugin-es-toolkit';

export default {
  plugins: [esToolkit()]
};

The migration is less work than triaging lodash alerts for the next year. And es-toolkit is actively maintained, tree-shakeable, and doesn't carry a decade of Function() constructor baggage.

If lodash is a transitive dependency you don't control (it's coming from some package three levels deep in your node_modules), you can check whether you're actually exposed:

# See which packages pull in lodash
npm ls lodash

# Scan your full dependency tree for known vulnerabilities
npx vekt scan

Vekt checks your lockfile against OSV.dev for both CVEs and MAL-* malicious package advisories across 12 ecosystems. If lodash is buried in your tree and you want to know if it matters, that's the fastest way to find out.

The bottom line

Ten CVEs. Three that matter. Zero confirmed exploits in the wild. CVSS scores that assume worst-case and ignore your actual code paths.

The scanner isn't wrong that the vulnerabilities exist. It's wrong about how much you should care -- because it can't read your code. You can. Spend five minutes with grep instead of five hours on a remediation sprint that doesn't change your risk profile.

And if you're tired of having this conversation every quarter, just migrate off lodash. The JavaScript standard library caught up years ago, and es-toolkit handles the rest. Your scanner will go quiet, and you can get back to building things.

meta_title: "Every CVE in lodash, Ranked by Real-World Risk"
meta_description: "All 10 lodash CVEs analyzed. Which ones can actually hurt you, which are scanner noise, and what to do about it."
tags: ["javascript", "security", "supply-chain", "npm", "vulnerability-management"]