Aikido

Software supply chain security requires decisions rather than defaults

Every upgrade has the potential to be a supply chain event.

Written by

A bridge stays in service for fifty years on a fixed inspection schedule, load-tested and maintained the entire time. A jet engine flies the same design for decades under continuous regulatory oversight. In most engineering disciplines, a stable, proven design paired with active maintenance is the goal. Newer designs are treated with far more scrutiny, because they’ve never been truly tested.

But for some reason, in software engineering, the opposite is true. The newest release is treated as the safest. And that instinct has backfired badly in the past. A backdoor sat inside two specific releases of xz-utils, versions 5.6.0 and 5.6.1, planted by an infiltrator who’d spent two to three years plotting up release authority. Anyone still running the older 5.4 x line was never exposed, but anyone who’d pulled the latest version, the thing security teams are usually told to do, was running a backdoored SSH path. CISA’s own guidance afterward was to downgrade not upgrade.

We’ve argued before, in what we call ‘the upgrade trap’, that patching the moment a CVE appears is a false economy; you’re choosing between freezing and accepting whatever changes come with the fix. xz-utils shows us the opposite side of the same coin. Staying put was the safe move, and the newest release was the trap. 

One way of testing this is to ask a security team their open CVE count, they’d be able to fire off an answer quickly. But if you ask why a specific package or container image is running the version it's running, you’re most likely going to get a “dunno”. It just is right? Usually it’s based on what was available when the project started, and upgrading felt like more work than it was worth. Taking it a step further, if you ask for a record of that decision anywhere in writing, you’re likely to get a more stern “why should we even be doing that?” kind of response. 

So where does this go wrong?

Often engineers are led to believe that a container image or package gets riskier when the version number gets old. But the xz-utils example proves this isn’t true; the older version was safe because nothing malicious had reached it yet, and the teams running it hadn’t rushed to adopt the newest release the moment it shipped. There is of course a chance many teams just didn’t get around to updating it, but wanted to. That’s still better than  not knowing what version you’re running, or why, which is the actual failure here.

Instead, teams have to consider that if they have a version of a library that has been tested, understood and is trusted, there is genuine value in keeping that stable. Monitoring begins at the point a developer, agent or build pipeline requests a container base image or an open-source package by evaluating it before it enters the environment. It’s the first decision in a chain of decisions that are made. 

An upgrade is also a decision 

At some point a new release will come out, but patching a CVE means accepting everything else that shipped in that release too. That includes things like transitive dependencies, changed defaults, code paths nobody on the team has read. Most of the time that risk is invisible and nothing goes wrong. But, and it’s a big but, it can go wrong. lodash spent a stretch of 2026 with a disclosed arbitrary code injection vulnerability affecting every published version. When the fix finally shipped as version 4.18.0, it broke immediately because the patch swapped in a different internal function that was never imported, causing real projects to fail within a day.

npm deprecated the release outright and told users to go back to lodash 4.17.21 instead. The breaking change had nothing to do with the vulnerability itself, it was a packaging mistake that came bundled with the security fix. The actual fix arrived in 4.18.1.

None of the options on the table were actually safe. Upgrading to 4.18.0 broke builds immediately. Staying on 4.17.21 kept the vulnerability open, while every version before 4.18.0 remained exploitable. Some teams downgraded back to 4.17.x specifically to escape the broken build, trading a working build for a live code injection path without necessarily realizing that’s the trade they’d made. 

In this case, the teams in the strongest position were the ones who knew whether their own application even called the affected _.template path with untrusted input, and could weigh that real exposure against the broken alternative, rather than defaulting to either move without knowing what it actually cost them.

Fixing the vulnerability without replacing everything around it

Backporting changes the equation. It takes the specific fix for the specific vulnerability and applies it to the version already running, rather than pulling in everything else that shipped alongside it upstream. Apply it to a container image, and the CVE gets fixed without forcing a new base image. Apply it to a package, and the same fix is made without a forced jump to the latest release, the exact jump that broke real projects running lodash 4.18.0. You're able to fix the security problem without changing everything around it. 

An SBOM should earn its place, not just exist

An SBOM, like so many compliance-related documents, is often taken once and filed away. That tells you what was in the system on the day someone ran the report. But the SBOM can do far more for your security posture than that. It can be the trusted software baseline and help you to continuously manage that baseline. You would do that by making sure it is kept current and checked against new discloses as they come in. That for me is the clearest sign an organization is still tracking the software it runs rather than coasting on whatever’s already there.

We want the SBOM to become not just an inventory or compliance artifact. It should be a governed, actively maintained surface.

So what needs to change?

All of these are decisions that need to be made. Some are about a specific software component at a given time, like evaluating a request, pinning your version, and backporting instead of forcing an upgrade. Others, like whether you keep an SBOM current, are about the processes you have in place. Each one is really a decision about what to trust. 

The easier thing to do is to just leave things where they are, because “it works”. But “it works” isn’t really true is it? Just because something functions, doesn’t mean it will always function, or that it is the most secure way to do things, or that it's the best way to operate. 

From our perspective, the most proactive, security-aware approach would be to control change at the point of entry - evaluate packages before a developer, agent or build system consumes them. From there, you should have the license to proactively decide what enters, pin what you trust, maintain it, and only change it when there is a reason.

Staying on a version shouldn't mean staying exposed. Aikido Libraries backports the fix to the version you're already running, no forced upgrade, no forced migration. Start patching here.

Share:

https://www.aikido.dev/blog/software-supply-chain-security-decisions-not-defaults

Subscribe for news

4.7/5
Tired of false positives?

Try Aikido like 100k others.
Start Now
Get a personalized walkthrough

Trusted by 100k+ teams

Book Now
Scan your app for IDORs and real attack paths

Trusted by 100k+ teams

Start Scanning
See how AI pentests your app

Trusted by 100k+ teams

Start Testing

Get secure now

Secure your code, cloud, and runtime in one central system.
Find and fix vulnerabilities fast automatically.

No credit card required | Scan results in 32secs.