Key Takeaways
- Lodash was dormant, not end of life, and its revival brought new CVEs against long-pinned versions.
- A single estate can carry several Lodash versions, most of them pulled in transitively.
- Finding a vulnerable copy is easy; proving which code path reaches it is the real work.
Nobody sits in a planning meeting and decides to adopt Lodash. It arrives. A senior dev pulls in one helper, a build tool lists it as a dependency, a charting library three levels down needs _.merge, and suddenly it rides along in the bundle of every app you ship. Quiet infrastructure, not a framework anyone picked.
It spreads everywhere a JavaScript file runs. Frontend code, backend services, build tooling, CLIs, automation scripts, and the abandoned internal package that one team wrote in 2019 and nobody has opened since. Sonatype’s 2024 report put npm alone at an estimated 4.5 trillion package requests for the year, and Lodash is one of the most widely used packages in that flood.
Then came the turn that neither the release notes nor most retros captured. Version 4.17.21 shipped in February 2021, and for years nothing followed. Teams read the silence as stability, pinned the version, and moved on.
That read was wrong. The absence of advisories was never evidence of safety. It said little about the actual exposure, and that became clear once the revived project started working through its security backlog.
Is Lodash still maintained?
Yes, again, after a long gap. Lodash spent years effectively dormant, and in 2025 it came back under active stewardship. The advisories that followed in 2026 are why old pins started lighting up.
Lodash began as a 2012 Underscore fork, and its creator moved through the Dojo and jQuery foundations before the project landed at the OpenJS Foundation, where it is now transitioning to the Feature-Complete maturity stage. The recent history is easier to read as a timeline.
| Date | What happened |
|---|---|
| Feb 2021 | 4.17.21 ships fixes for command injection in _.template and a ReDoS issue, then years of quiet |
| 2025 | OpenJS announces Sovereign Tech Agency funding, a governance reboot, and a new Technical Steering Committee |
| Jan 2026 | 4.17.23 patches prototype pollution in _.unset and _.omit (CVE-2025-13465), affecting 4.0.0 through 4.17.22 |
| Mar 2026 | 4.18.0 patches a _.template code injection via imports keys (CVE-2026-4800) and a bypass of the January _.unset and _.omit fix (CVE-2026-2950) |
| Apr 2026 | 4.18.1 becomes the current release, fixing a modular-build regression from 4.18.0 |
The reboot came with real operational plumbing: an incident response plan, a published threat model, GitHub Security Advisories, CodeQL scanning, Renovate for dependency updates, an OpenSSF Scorecard, and rebuilt CI.
Did You Know: The 4.17.23 advisory was filed through the OpenJS CVE Numbering Authority. It was the first Lodash security release in several years, and that kind of coordinated disclosure is the signature of a functioning security process rather than a dormant one.
Here is the part that matters for anyone running old code. That renewed stewardship keeps the current release line maintained and secure. It does not mean anyone is backporting new fixes to 3.10.1.
What a revived project does to a pinned version
The mechanism is simple and unforgiving. A new advisory can put an unchanged pin inside a newly identified affected range, without anybody touching your code. Your version number did not change. Its known risk profile did.
Three disclosures drive the point home.
Prototype pollution in _.unset and _.omit was patched in 4.17.23. CVE-2025-13465 covers 4.0.0 through 4.17.22, so a five-year-old pin at 4.17.21 sits squarely in range.
A bypass of that fix landed as CVE-2026-2950. The 4.17.23 patch guarded string path segments but not array-wrapped ones, so the same deletion attack survived. Lodash 4.17.23 and earlier are affected, and it was patched in 4.18.0.
Code injection in _.template through imports key names, CVE-2026-4800, stems from an incomplete fix for the older CVE-2021-23337. The original patch validated the variable option but not the imports key names, and both paths flow into the same Function() sink. It affects 4.0.0 through 4.17.23 and is patched on the 4.18 line.
Version selection here is not automatic. The 4.18.0 release shipped a modular-build regression, and npm now marks it deprecated with a notice that reads “Bad release. Please use [email protected] instead.” That sends people straight back to a version affected by all three CVEs above. 4.18.1 is the one you actually want. Some of the single-function lodash.* packages were regenerated on the new line too. lodash.unset now tracks 4.18.0, while others such as lodash.merge remain frozen at 4.6.2 from 2019, so older projects bundling those builds directly need to check each one individually.
Watch Out: A green scanner result on “lodash 4.17.21” from 2024 does not stay green – advisories published in 2026 apply retroactively to that exact build, and nothing in your lockfile will tell you until you rescan.
Picture a 2018-era Angular admin console with lodash 3.10.1 in the bundle and lodash.merge pulled in by two abandoned chart libraries. Nothing in the scanner report is wrong, and nothing in it is actionable either. That gap between accurate and useful is where the real work starts.
Prototype pollution changed how teams see utility libraries
A pure string helper that uppercases text does not cross the same security boundary. A function that merges objects or sets a value at a path does, and that difference is the whole story. Object merging and path assignment write into structures your code later trusts, which is a line _.capitalize never goes near.
The failure mode is not exotic. Feed a crafted key like __proto__ or constructor.prototype into a vulnerable merge or path setter, and you can reach the shared prototypes that ordinary objects inherit from, starting with Object.prototype. At the mild end you get denial of service when downstream code chokes on a polluted object. At the far end, with the right sink such as a template engine or a Function() call, prototype pollution becomes a foothold for code execution. One caveat for these particular Lodash advisories: the _.unset and _.omit flaws allow deleting properties from shared prototypes rather than overwriting them, so the realistic impact is breakage and denial of service rather than the classic pollution-to-RCE chain.
Precision matters, though. Not every call to an affected function is exploitable. The deciding factor is whether an attacker can influence the merged object or the key path. If both are fully developer-controlled and cannot be reached by untrusted input, exposure is low. If either can be influenced by a request body, a query string, or a parsed file, treat the path as potentially exploitable until you have ruled it out. That distinction is what separates a remediation plan from a flat CVE list telling you everything is on fire.
The dependency graph is the real remediation project
Finding a vulnerable Lodash copy can take one scan. Working out what to do about it takes considerably longer. The 2022 Snyk State of Open Source Security report found the average application in development carries 49 vulnerabilities across 69 dependencies, and utility libraries like Lodash are precisely the kind that arrive several layers deep.
The work itself is concrete enough. Start by identifying the parent dependency introducing each Lodash version, then work out whether deduplication is genuinely safe or whether two consumers really do need different majors. Decide whether overrides or resolutions are sustainable or just deferred debt. Test bundled and server-side copies separately, because they resolve independently. Then go hunting for hard-coded or manually copied builds that npm cannot see at all.
Lodash ships with zero runtime dependencies of its own, so the tree beneath it is empty. The problem is inverted. Lodash sits as a leaf in thousands of other trees, and that is where copies multiply. For supported versions, TuxCare ELS can replace vulnerable direct and transitive copies with patched builds, which is the point of the exercise when a scanner flags six different versions in one repo.
From the Trenches: Teams can “upgrade Lodash,” close the ticket, and still fail the next scan. They bumped the top-level dependency and left three transitive copies at 4.17.15 untouched, because a direct upgrade does not rewrite what a child package pinned.
Removal deserves an honest note. Optional chaining, spread syntax, and modern Array methods have made many Lodash helpers unnecessary. Deep merge semantics, cloneDeep edge cases, debounce behavior, and the fp module are a different matter.
Which brings up the decision. If Lodash sits behind a handful of simple call sites, removing it is usually the cleaner option. If _.merge or _.cloneDeep touch financial or reporting logic, treat removal as a behavior change with its own test plan, because that kind of change can pass CI and then break a month-end report.
Four remediation paths
There is no single fix, and pretending otherwise is how remediation stalls. Four paths cover the realistic options, and most estates use more than one.
| Path | Best when | Cost |
|---|---|---|
| Upgrade Lodash directly | You own the direct dependency and tests cover the call sites | Low, if nothing pinned it for a reason |
| Replace functions with native JS | A small number of simple call sites (chaining, spread, Array methods) | Moderate, higher for merge/clone logic |
| Upgrade the parent package | Lodash arrives transitively through a maintained dependency | Depends on the parent’s own breaking changes |
| Patch and hold the legacy release | You need a specific old version welded into the graph | Ongoing, but no forced migration |
That last path is where a vendor patch earns its place. TuxCare ELS for Libraries covers Lodash 1.3.1, 2.4.2, 3.2.0, 3.10.1, 4.5.0, 4.17.4, 4.17.5, 4.17.11, 4.17.15, 4.17.19, 4.17.21, and 4.18.1, so the version you cannot move off still receives the security intent of the upstream fix.
The mechanism is unglamorous, which is the point. You add an npm alias in your root package.json alongside overrides so transitive copies resolve to the patched build, or you run the TuxCare Patcher across a repo that carries several covered dependencies. That is the direct answer to the transitive-copy problem.
Insider Tip: Declare overrides in the root package.json, because npm ignores overrides declared inside installed dependencies. Point them at the aliased patched build so npm resolves matching instances of the vulnerable package name to the maintained replacement throughout the dependency tree.
How TuxCare secures older Lodash versions
Backporting a fix to a decade-old utility library is not a copy-paste job, and treating it that way is how you break the exact edge cases applications rely on. The process runs in four stages.
First, analyze whether each new disclosure actually reaches a covered release, rather than assuming a published version range describes exploitability in every application. Second, backport the security intent without dragging in unrelated behavioral change. That restraint matters more here than almost anywhere, because applications depend on Lodash edge-case behavior that a well-meaning refactor would quietly alter.
Third, regression test both the common functions and the specific affected paths, so a prototype-pollution fix does not change what _.merge returns for a legitimate nested object. Fourth, sign every built package and publish SBOM and VEX documents alongside each release, so your auditors get verifiable provenance instead of a promise.
This is stewardship work, not upstream innovation, and the distinction is deliberate. TuxCare specializes in keeping production code secure and stable after upstream priorities move on, with predictable, compatibility-first patching for systems expected to run for years.
Who paid for the Lodash reset, and why the model matters
Lodash is the clearest example of a pattern worth naming. Sovereign Tech Agency funding, administered with OpenJS Foundation support, paid for governance and security work on a library millions of systems depend on, and the OpenJS write-up is worth reading in full.
What that funding does not buy is indefinite maintenance of 3.10.1. It strengthened the current project; it did not commit volunteers to backport fixes to a release from 2015, and pretending otherwise is how maintainers burn out.
The Ecosystem Sustainability Program covers that gap commercially, with revenue share flowing back to participating projects. TuxCare is an OpenJS Foundation Gold member and one of the program’s providers, listed alongside HeroDevs and NodeSource on the Node.js end-of-life page.
Worth stating plainly, because it keeps everyone honest. A vendor patch for 4.17.21 is a vendor patch, not an upstream release, and good tooling makes that provenance explicit rather than hiding it. And when a copy cannot move at all, ELS for Lodash delivers the fixes through npm to the version you are actually running, for as long as you need to run it.
FAQ
Is Lodash end of life?
No. It was dormant for years, but it is actively maintained again under OpenJS, with a funded governance reboot and a current release on the 4.18 line.
Is 4.17.21 still safe?
Not against the vulnerabilities disclosed since the revival. It sits inside the affected range of three advisories: CVE-2025-13465 and CVE-2026-2950 (prototype pollution in _.unset and _.omit, including a bypass of the first fix) and CVE-2026-4800 (_.template code injection). Upgrading to 4.18.1 closes all three, and so does a maintained build carrying fixes for each of them.
Should I be on 4.18.1?
For actively maintained projects, yes. 4.18.1 is the current release and fixes the modular-build regression that shipped in 4.18.0. If a specific old version is welded into your graph, a patched-and-held release via TuxCare’s Endless Lifecycle Support is the alternative.
Do I still need Lodash at all?
Often not. Native syntax replaced many of the simple helpers, and structuredClone covers common deep-cloning cases, though it discards class prototypes and property metadata, and it throws on values it cannot clone, functions included. Deep merge, debounce, and the fp module still have no tidy one-line substitute.
Before you close the ticket
“Update Lodash” is not a remediation plan until every reachable copy is accounted for. You can track current advisories against every covered version on the TuxCare CVE tracker.
