Key Takeaways
- lodash 4.17.21 is no longer the safe endpoint it was treated as for years, and it now carries three confirmed flaws — CVE-2025-13465, CVE-2026-2950, and CVE-2026-4800 — resolved only in 4.17.23 and the 4.18.x line.
- The hard part of remediation is organizational rather than technical, so mapping real call sites, hardening input at the boundary, and monitoring for exploitation are what buy time when an immediate upgrade isn’t realistic.
- Backported fixes through a service like TuxCare’s ELS for Libraries let teams secure the known-good 4.17.21 build while planning a deliberate migration to 4.18.x instead of rushing a risky one.
Your team knows about the vulnerabilities. The ticket has been open for nine months. Nobody has touched it. This is about why – and what to do instead of waiting.
There is a particular kind of vulnerability that does not get fixed. Not because engineers are careless, not because security teams are asleep – but because the fix is technically possible yet operationally terrifying. lodash version 4.17.21 is the canonical example of this pattern in modern JavaScript projects, and it has been sitting in production environments across the industry for years.
This piece is not a CVE breakdown. You can look those up. This is about the gap between knowing something is wrong and being able to do anything about it – and the practical ways teams navigate that gap without pretending it does not exist.
How lodash 4.17.21 Got Here and Stayed
lodash became one of the most downloaded JavaScript packages in history because it solved a real problem: JavaScript’s standard library was thin, inconsistent across environments, and browser compatibility was a minefield. At its peak, lodash was used in everything from startup side projects to Fortune 500 enterprise platforms.
Then modern JavaScript matured. Native array methods improved. ES6 and beyond absorbed many of the utility patterns lodash had pioneered. Newer projects began shipping without it. But existing projects – particularly large, multi-year codebases – did not suddenly remove lodash. They had accumulated thousands of call sites. Refactoring them was an enormous undertaking with uncertain payoff.
Version 4.17.21, released in February 2021 to patch a high-severity command-injection flaw in _.template (CVE-2021-23337) alongside a regular-expression denial-of-service issue (CVE-2020-28500), became the pinned version of record for much of the ecosystem. And there it stayed for nearly five years, with no further releases. Not because 4.17.21 is permanently safe: a new governance team has since disclosed fresh flaws that do affect it — prototype pollution in _.unset and _.omit (CVE-2025-13465 and CVE-2026-2950) and a code-injection bypass in _.template (CVE-2026-4800). The fixes now live in the lodash 4.18.x line, but upgrading or removing lodash entirely is expensive, and the cost-benefit calculation rarely comes out in favor of doing it right now.
The transitive dependency problem
Many teams running lodash 4.17.21 are not even doing so directly. The vulnerability lives three layers down, inside a dependency of a dependency of a framework they installed two years ago. Run npm ls lodash in a mid-sized Node.js project and you will often find a branching tree of indirect references. The application code itself may never import lodash at all.
This makes the ownership question deeply uncomfortable. Who is responsible for fixing a transitive dependency that no team member introduced intentionally? Security says it belongs to engineering. Engineering says it belongs to the team that owns the upstream package. That team says it is technically not exploitable in the current deployment context. The ticket stays open.
The Psychology of the Known Vulnerability
Security literature focuses heavily on the unknown threat – the zero-day, the novel attack vector, the adversary that moves faster than defenders. But in practice, a significant share of real-world incidents trace back to vulnerabilities that were known, flagged, and consciously deferred.
Engineers describe the same dynamic again and again in post-incident reviews. They knew about the issue, but couldn’t get approval to touch a part of the codebase that had been stable for years, and nobody wanted to be the person who broke it.
This is not negligence. This is a rational response to organizational incentives. Shipping features generates visible value. Refactoring a dependency graph to resolve a vulnerability that has not yet caused an incident generates invisible value, and carries concrete downside risk in the form of regressions, delays, and blame if something breaks in production.
The result is what risk professionals sometimes call risk tolerance by default: not a deliberate decision to accept the risk, but an accumulation of deferrals until the risk becomes ambient. Teams stop seeing it clearly because it has always been there. The scanner flags it, the ticket is updated, the sprint ends, and lodash 4.17.21 remains.
Small Library, Large Attack Surface
lodash feels small. It is a utility library. It does not touch authentication, authorization, or network communication – the components most people associate with security-sensitive code. This perception contributes to the deferral problem.
But the attack surface is not about function. It is about exposure. lodash functions are invoked throughout application logic, often with data that has traversed trust boundaries. The prototype pollution vulnerabilities – where an attacker can manipulate JavaScript’s object inheritance chain by supplying crafted input – can affect application behavior in ways that are non-obvious and context-dependent.
A _.merge() call deep inside a configuration-handling utility, processing user-supplied JSON, is not obviously dangerous at first glance. But it is exactly the kind of call site that, depending on the surrounding application logic, can contribute to access-control bypasses, application-state tampering, or even code-execution paths. The function’s apparent innocuousness is part of why it is dangerous.
| Vulnerability | Affected methods | Exploitability / impact | Severity |
| Code injection (CVE-2026-4800) | _.template (options.imports) | Remote arbitrary code execution at template-compile time; incomplete fix of CVE-2021-23337 | High (8.1) |
| Prototype pollution (CVE-2026-2950) | _.unset, _.omit | Deletes methods from global prototypes via array-wrapped paths; bypasses the CVE-2025-13465 fix | Medium (5.3) |
| Prototype pollution (CVE-2025-13465) | _.unset, _.omit | Deletes methods from global prototypes via crafted string paths; can cause denial of service | Medium (5.3) |
Exploitability depends heavily on context. An application that never passes user-controlled data to the affected methods is effectively not exposed. But how many teams have mapped every call site in a large codebase and can confidently make that claim? In most organizations, the honest answer is: not many.
Real-World Constraints That Make This Hard
The obvious answer – upgrade lodash, or remove it – is correct in the abstract and difficult in practice. Specifically, it runs into three structural problems that recur across organizations of every size.
Release cycles and stability requirements
Many production environments operate on change control processes that require extensive testing, sign-off from multiple stakeholders, and a minimum bake time before deployment. A dependency bump that triggers unexpected behavior in a suite of integration tests can derail a release timeline by weeks. In regulated environments – financial services, healthcare, critical infrastructure – the barrier is even higher. Security improvements arrive through the same process as every other change, competing for attention and approval with features that generate measurable business value.
Regression risk in established codebases
Although lodash 4.x stayed API-stable for years, removing lodash or replacing it with native methods requires auditing every call site, and even moving to the newly released 4.18.x line can introduce subtle behavioral changes.
For a codebase that has accumulated lodash calls over five or more years, this is not a weekend task. Test coverage is often uneven – the oldest and most lodash-heavy parts of a codebase are frequently the least well-tested, precisely because they predate the organization’s modern testing practices.
Ownership and organizational gaps
In microservice architectures and large engineering organizations, codebases have owners – but ownership is sometimes ambiguous for legacy services that predate current team structures. A service that was built by a team that has since been reorganized may have no clear owner. Security will flag the vulnerability. Operations will note the service in the report. Nobody will have the authority and the context to make the change. This is more common than most organizations care to acknowledge.
STRUCTURAL INSIGHT
The vulnerability is not the hard problem. The hard problem is organizational: identifying who has ownership, building the case for prioritization, securing time in a sprint, and absorbing the regression risk. Technical complexity is often the smallest part of the challenge.
What Teams Actually Do And What Works
Given these constraints, sophisticated engineering organizations do not simply ignore lodash 4.17.21 and hope for the best. They apply a range of risk-reduction strategies that acknowledge the upgrade cannot happen immediately while reducing exposure in the interim. None of these are perfect. All of them are better than nothing.
Isolating and mapping exposure
The first step is honest assessment. Not every application that includes lodash in its dependency tree is equally exposed. Teams that invest time in mapping actual call sites — specifically the methods tied to known 4.17.21 risk, such as _.template, _.unset, and _.omit — can produce a realistic risk ranking. A service that uses lodash only for _.isEqual and _.cloneDeep on internal data structures is categorically different from one that compiles _.template output from user-controlled input.
This mapping work is valuable even if it does not immediately result in a fix. It converts ambient, uniform anxiety about lodash into a prioritized list of actual risk, which makes planning remediation more tractable.
Input validation and hardening at the boundary
Prototype pollution and command injection vulnerabilities typically require attacker-controlled input to reach the vulnerable function. Hardening input validation at application boundaries – enforcing schema validation on incoming JSON, rejecting inputs with suspicious key patterns like __proto__ or constructor, sanitizing strings before they enter template functions – reduces exploitability without touching the underlying dependency.
This is a compensating control, not a fix. The vulnerability remains. But limiting the pathways by which attacker input can reach vulnerable code is a defensible interim position, and it is the kind of change that can often be made without triggering a full release cycle.
Monitoring and detection
Runtime application self-protection (RASP) tools and web application firewalls can be configured to detect and block prototype pollution attempt patterns. Logging and alerting on suspicious object key patterns in incoming requests provides visibility into whether the vulnerability is being actively probed, even if it cannot yet be patched.
Detection does not eliminate risk, but it changes the risk profile. A known vulnerability with active monitoring is substantially less dangerous than the same vulnerability in a system generating no alerts. At minimum, detection supports faster incident response if exploitation occurs.
Extending support selectively
For organizations managing multiple applications with legacy dependencies, one increasingly practical approach is to treat support extension as an explicit risk management tool. Rather than forcing a fragile upgrade on a timeline that creates its own risks, teams can use services like TuxCare’s ELS for Libraries to receive backported security fixes for specific library versions, maintaining the stability of the known-good configuration while getting patches applied to the vulnerabilities that matter most.
This is not a permanent solution, and it should not be framed as one. The goal is to create breathing room: time to plan and execute a proper migration without running naked in the interim. For teams operating under strict change control, or managing services with poorly understood dependencies, this approach aligns security improvement with operational reality rather than demanding an upgrade the organization is not ready to absorb.
Incremental migration over big-bang rewrites
Where full lodash removal is the eventual goal, incremental approaches reduce the risk of the migration itself. Replacing lodash utilities method by method, starting with the highest-risk call sites and moving outward, avoids the big-bang rewrite that tends to fail. Teams track progress with dependency audits, retire lodash usage service by service, and treat each removed usage as a measurable risk reduction rather than a task on an undifferentiated backlog.
The Honest Accounting
The uncomfortable truth about lodash 4.17.21 is not that it is in production. It is that it will stay in production for a long time across most organizations, regardless of what security scanners report or what advisory notices say. Engineering capacity is finite. Risk tolerance is a real organizational phenomenon. The calculus of “upgrade now” versus “manage carefully while we plan a real migration” will, in many contexts, continue to favor the latter.
The response to this reality is not resignation. It is honest risk management: knowing which call sites are exposed, hardening boundaries, monitoring for exploitation, and – where the upgrade timeline is genuinely long – using the tools available to narrow the vulnerability window without pretending the underlying problem is solved.
Security maturity is not about having zero known vulnerabilities. It is about having visibility into risk, treating deferrals as deliberate decisions rather than invisible accumulation, and closing gaps systematically. lodash 4.17.21 is a reasonable test case for whether an organization can do that honestly.
The ticket has been open for nine months. It is time to decide what you are actually doing with it.

