Trust as a vulnerability: supply chain attacks and dependencies
We are going to explore a topic that is very much in the spotlight and one you have probably already read about: supply chain attacks. However, we are going to give it a slightly different twist towards the end with a reflection inspired by a comment on X from a fairly well-known and respected developer in the community, which sparked a debate that is no less interesting.
But first things first, just in case anyone is still wondering...
What are supply chain attacks?
You use software. A lot of it. Every single day. Sometimes without even realising it. What we rarely stop to think about is how that software is built. Yes, we know that someone (or something, these days) developed the application you are using. But software is rarely built entirely from scratch. To speed up delivery times, developers reuse code extensively.
Reusable code usually comes in the form of libraries, frameworks or add-ons. In a way, building software resembles an industrial process where you add components to a system and orchestrate them for a specific purpose. Just as a car manufacturer does not need to build its own alternator, software developers simply choose a supplier and adapt the design to fit their engines.
But what would happen if the alternator had a flaw that caused it to catch fire under certain circumstances? What if that flaw had been introduced deliberately, with the malicious intent of harming customers and damaging the manufacturer’s reputation?
In much the same way, malware creators have found a vector through which they can inject malicious code without needing to distribute it through phishing campaigns and similar methods.
It makes sense. Why build an infrastructure capable of sending hundreds of thousands of fake emails with a success rate of just 0.0001% when you can infect a software dependency and simply sit back and wait for it to be distributed through an update?
The supply chain turns trust into an attack vector.
It is as if the Achaeans had poisoned Troy’s water supply with the Giardia lamblia parasite instead of building an enormous hollow horse as a gift (which, incidentally, is where the famous phrase Timeo Danaos et dona ferentes comes from).
Some notable attacks
There is an entire taxonomy of attacks and even MITRE lists several of them in its well-known ATT&CK framework.
Among other methods, attackers either infiltrate companies that produce software and infect their private codebases, or they approach open-source projects posing as contributors. One common factor stands out: the subtle use of social engineering aimed at carefully selected targets.
The attack that best exemplifies this, mainly because of its media impact, is the one that hit SolarWinds in 2020 at the hands of the APT-29 group, after infiltrating the company’s internal network and compromising the code of its Orion software. This software was used by countless companies and government agencies.
Compromising a library is more profitable than launching thousands of phishing campaigns.
In the open-source world, we cannot overlook the lesson learned from the attack on the XZ project, a compression utility widely used across all kinds of Linux distributions and the wider ecosystem. For two years, an entity operating under the name ‘Jia Tan’ contributed code and fixed bugs until they earned the trust of the project maintainers.
Once they believed that trust was strong enough, multiple accounts began creating the conditions needed to push through code that included malicious components concealed within legitimate code, eventually making their way into version 5.6.0 and later into version 5.6.1.
The compromised code remained in circulation for a month until Microsoft engineer Andrew Freund spotted it and raised the alarm. During that month, anyone incorporating the affected versions of XZ or liblzma into their systems was exposed to the trojan.
Software is not built from scratch: it is assembled from dependencies.
Let us remember: two years spent cultivating trust. Building relationships with the project’s developers and maintainers. Two years invested in crafting a carefully designed image to gain credibility and lower people’s guard.
■ The issue is not this particular example. The real issue is wondering whether the same thing might currently be happening across the thousands of active projects maintained today, most of them supported altruistically by hundreds of developers. Critical projects that lack the resources or backing they truly need.
We should remember this iconic XKCD image, which became something of a meme following the Log4j vulnerability and highlighted the imbalance between the importance of certain open-source projects and the limited resources available to them.
The elephant in the room: dependencies
If you have ever worked on a software project and used a package manager, you will have noticed that even when using a single library, the dependency tree can grow in unexpected ways.
For example, using npm in what appears to be a harmless Node.js project can easily result in several megabytes of dependencies and transitive dependencies. The community is well aware of this.
One particularly striking example is the ‘left-pad’ package incident. In 2016, the creator of the ‘left-pad’ package became involved in a dispute with a company over the name of another package developed by the same author. When the developer failed to get the outcome they wanted, they decided to remove their packages from the npm repositories.
This triggered a huge wave of errors across other projects, deployments and installations. Once a minor dependency disappeared, the project’s dependency tree could no longer compile. As a result, thousands of builds across many other projects failed.
The most astonishing part of this case is that the functionality provided by ‘left-pad’, which, once again, was a dependency for thousands upon thousands of projects, consisted of just a handful of lines of code.
And when we say a handful of lines of code, we do not mean hundreds or thousands. We mean literally eleven lines of code.
Source: Wikipedia
As a result, if we have a project with thousands of packages, then we effectively have a house with thousands of open windows that need protecting. Every time we add a dependency, along with its transitive dependencies, we are opening the door to code we may not fully understand.
The problem is not using dependencies, it is failing to question them.
Mitchell Hashimoto’s proposal
Mitchell Hashimoto is known as the founder of HashiCorp and the creator of Ghostty. He is a recognised and respected voice within the development community. A few days ago, Mitchell sparked a debate on X that gradually drew in more voices from across the industry and evolved into an interesting discussion with many different angles.
Hashimoto commented on X that, when it comes to supply chain attacks, he follows an approach designed to minimise exposure as much as possible.
His philosophy regarding dependencies is clear: “Fork dependencies, only use what you genuinely need and never update unless something breaks.” On updates, he added: “If you are updating a dependency, it is your responsibility to analyse every individual commit across the full transitive dependency set. If nothing looks compelling, don’t update.”
Although it may sound like an extreme position, there is logic behind it and it reinforces one of engineering’s most enduring principles: if it works, do not touch it.
A flaw in one component can escalate into a system-wide issue.
From a security standpoint, the approach aligns closely with core security principles: reducing the attack surface by using fewer dependencies, avoiding updates unless absolutely necessary and carefully reviewing code contributions.
Figures such as Salvatore San Filippo (creator of Redis) and Armin Ronacher (Pallets) commented that they held a similar view on dependencies.
From one perspective, this can certainly appear somewhat radical. Refusing external dependencies altogether, or reducing them to an absolute minimum, is a luxury only certain projects with strict security requirements can afford. Not every project has the resources needed to follow a philosophy that demands constant review work and surgical precision when untangling dependencies.
Could there be a middle ground that significantly reduces exposure to attacks while still allowing us to accelerate software development?
Readers paying close attention may have noticed that we have not yet mentioned AI. Well, now is the time to bring it into the discussion.
The ‘left-pad’ case clearly leaves us with an important lesson: if your dependency is an 11-line project, remove that dependency. But if your project relies on a cryptographic library such as OpenSSL, then you really have no choice but to use it and monitor contributions to that project closely.
Although the issue goes far beyond source code alone, the ideal approach is to freeze dependencies, review the commits that will ultimately become part of the project through updates, and only upgrade versions when we genuinely need the new features or security fixes.
Every dependency added expands the attack surface, even when we do not notice it.
This is where AI opens a new door. Who is realistically going to review hundreds of commits in an open-source project before upgrading a version in search of malicious code? Having a person do that is expensive. Expensive and exhausting.
Let us remember that in the case of the XZ trojan, it took a month before an external developer finally spotted the issue while reviewing the code. But AI can do this in seconds or minutes. It does not replace a rigorous human audit, but it can dramatically reduce the initial inspection effort. It can even industrialise this process within your own project: identify a minimal dependency, ask AI to rewrite it using proprietary code and reduce your exposure.
Likewise, if you need to update a third-party component, you can ask AI to review the commits from your current version up to the new release before integrating it. Naturally, this works with dependencies that are open-source projects and can therefore benefit from the transparency of publicly available code.
Conclusion
Supply chain attacks are here to stay and are becoming an increasingly exploited attack vector. Dependencies open the door to these attacks, yet getting rid of them is far from straightforward.
Monitoring dependencies closely and replacing them with proprietary code is expensive, but we now have a new ally on the roster that we can put to work for us and let it handle those tasks.
Without question, protecting ourselves against these attacks depends on reviewing, whenever possible, exactly what we are introducing into our projects. This is vital if we want to reduce uncertainty and minimise exposure.
For years, we automated the incorporation of third-party code. Perhaps the time has now come to automate distrust as well.
Hybrid Cloud
Cybersecurity
Data & AI
IoT & Connectivity
Business Applications
Intelligent Workplace
Consulting & Professional Services
Small Medium Enterprise
Health and Social Care
Industry
Retail
Tourism and Leisure
Transport & Logistics
Energy & Utilities
Banking and Finance
Smart Cities
Public Sector