The advice is always the same when a vulnerability tool flags a CVE: upgrade. Move to the patched version so you can close the ticket and move on. It’s become such a reflex that nobody stops to ask whether it’ll actually work.
The proposed fix fails in three specific ways. There's no version to upgrade to and won't ever be, the patched version hasn't shipped yet, or the fix ships and breaks your application.
The TL;DR is that best practices and every framework from SOC2 to ISO27001 tells you to keep your dependencies up-to-date. But upgrading does not equate to fixing, and yet it is the only answer many tools have.

It wasn’t always the wrong instinct. Maintainers (thankfully) have been fixing vulnerabilities in the latest version, and for years that was sufficient.
CVEs get reported > maintainers patch them > you upgrade > you get the fix.
But maintainers don’t generally go back and fix older versions. So teams got conditioned to always upgrade to the version when they could, and attackers noticed that pattern and learned to exploit it. Now you upgrade to get the CVE fixed, there is some chance that somewhere in the update is malware, so pulling the latest version is no longer automatically safe.
Earlier this year, attackers compromised a maintainer account responsible for some of the most downloaded packages on npm. chalk and debug, between them over two billion downloads a week. They published malicious versions through the official channel, and every pipeline set to auto-update pulled them straight in. Teams following best practice were shipping malware within minutes.
It’s a lose-lose situation. You stay current and risk pulling in malware, or you freeze and accumulate security debt. This is what we call the upgrade trap. The version you run and the security fixes applied to it don't have to be the same decision.
The three ways upgrading fails
Of course, “upgrade” works a lot of the time, and is still important. But there are three situations where it falls apart.
The first is when there’s no fixed version to move to (and won’t ever be). Take request, one of the most-used HTTP clients in Node, that has been deprecated since 2020. And yet, it still turns up everywhere, usually dragged in by some parent package nobody’s touched in donkey’s years. When you run an SCA tool over request, the tool comes up with a CVE for server-side request forgery (SSRF) mitigation bypass. Guess what the fix is here according to the SCA tool? Yup, to upgrade.
But the kicker is there’s nothing to upgrade to, and there never will be. request hasn’t been maintained in years. The advisory all but says so, noting the flaw only affects versions the maintainer no longer supports (which is every version). So in this instance, “upgrade” actually means ripping request out and moving to something like axios or node-fetch. That’s a rewrite and a migration, and it means that until someone has time for it, the CVE just sits there.
The second type of upgrade is when the fix doesn’t exist yet even though the package is alive. The popular library lodash spent a stretch of time last year with two disclosed vulnerabilities affecting every published version up to and including 4.17.23. Since that was the latest version, there was nothing to upgrade to. npm audit flagged the CVEs, and reported no fix available. So even though the advice to upgrade was sound, it wasn’t actually feasible because the patched release hadn’t shipped.
The third is when the fix ships and breaks your application. CVE-2026-48937 in Node is a recent example. The fix for the vulnerability came bundled with a SEMVER-MAJOR update to the nghttp2 dependency and the removal of HTTP/2 priority signaling support. The vulnerable behavior and the feature being removed were part of the same underlying code, so there was no way to take the security fix without taking the breaking change alongside it. Users had to grep for setPriority and .priority() and remove them before they could upgrade at all.
This is the risk with any upgrade. A new release can change how a package behaves enough to break something that was working, force other dependencies to upgrade alongside it, or arrive as a major version that needs a migration before it'll even install.
And to go a step further, if you bump one package, you might have to bump five others. One of those might break something your system depends on. Engineering then has that choice between a roadmap item that generates revenue and the upgrade. Guess which one wins 😬. You then end up with tech debt, and a CVE pile up.

How AI changed the stakes
Most companies have a CVE backlog that’s too big to work through, and they’re nervous because the latest frontier models can find vulnerabilities faster. But what’s more of a threat they can chain together medium CVEs in a way that turns them into a critical exploit pattern. The current model of scanning, triaging and trying to dispatch that work out to engineers doesn’t scale quickly enough to manage this problem. AI is effectively compounding an existing problem, and making it more urgent for organizations to fix.
What the industry does about it
Upgrading was the only realistic answer for a long time. The CVE backlogs and broken builds that followed were a predictable consequence of the limited set of choices. There are broadly three approaches to this problem. Two of them trade one issue for another. The third actually closes the CVE without risking a new one.
The first is screening at the point of consumption. Before a package reaches your build, i gets checked: is this dependency safe to install? It’s a necessary layer, especially after attacks like chalk and debug. But it doesn’t fit vulnerable packages already in production. It’s a gate rather than a fix and outside that scope the answer remains to upgrade.
The second is moving teams onto a hardened replacement stack. Instead of fixing what teams already run, they get pushed onto someone else’s proprietary ecosystem. It can work for greenfield apps, but for production systems pinned to specific libraries and base images, it turns remediation into a migration project. Once you're in, getting out is its own project.
The third is backporting the fix to the version already in use. Most organizations don't have the capacity, expertise, or inclination to do this. When a CVE is fixed upstream, the thinnest slice of code that fixes it gets isolated from the newer release and applied cleanly to the older version you're already running. Validating that nothing else changed takes real expertise and ongoing maintenance. Distros have done versions of this for years. Doing it across application libraries, automatically, at the pace CVEs land, is a different problem entirely. This approach means you keep the version your application already trusts, apply only the fix, and you’re not then accumulating CVEs against a frozen version or pulling the latest release and whatever comes with it.
Think of your software stack as a house you’ve been living in for years. Most tools either screen what comes through the door, hand you a list of repairs and tell you to sort it, or ask you to move into a new, smaller house they built. Fixing the house you're already living in is what most teams actually want.
There's now an option that does exactly that: fix vulnerabilities in the open source packages your application depends on without upgrading with Aikido Libraries.

