quodeq / blog
← All posts By Victor Purcallas Marchesi

Your Dependencies Are Someone Else's Attack Surface

Someone spent two years writing helpful patches for a compression library. Good commit messages. Thoughtful code reviews. Patient, reliable, collegiate. The kind of contributor every open-source project dreams of.

The library was xz-utils. It ships inside OpenSSH. It runs on every major Linux distribution, every server you have ever used to deploy anything. The contributor called himself Jia Tan, and the patches contained a backdoor that would have given an attacker remote access to virtually any Linux machine on the planet.

It was discovered by accident. A Microsoft engineer named Andres Freund noticed that SSH logins were taking half a second longer than they should. He could have ignored it. He did not.

That was March 2024. Since then, something has changed. Not gradually. Sharply.

What happened in the last twelve months

In September 2025, a self-replicating worm called Shai-Hulud compromised over 500 npm packages. It started with phishing: developers received emails that looked like npm asking them to update their MFA settings. Once it had a developer's credentials, it automatically injected malicious code into every other package that person maintained, then used those packages to harvest more credentials. It spread without human intervention. CISA issued a formal alert.

Two months later, it came back. Shai-Hulud 2.0 hit over 25,000 GitHub repositories across 350 user accounts. It installed persistent backdoors via GitHub Actions runners, recycled stolen credentials across victims to build a botnet-like network, and included a dead man's switch designed to delete user data if anyone tried to contain it.

That same September, someone phished an npm maintainer through a convincing 2FA reset email sent from a fake domain. The result: 18 popular packages including debug and chalk, libraries that sit in the dependency tree of nearly every Node.js project in existence, were hijacked to deliver cryptocurrency wallet stealers.

In February 2026, legitimate packages belonging to the dYdX protocol on both npm and PyPI were compromised. Wallet stealers and remote access trojans pushed through official releases. The same month, over 1,000 packages using the "claw" naming pattern appeared on npm and PyPI, posing as AI training utilities, targeting Fortune 500 cloud environments.

In March 2026, Axios was compromised. Axios. Seventy million weekly downloads. The most used HTTP client in the JavaScript ecosystem. A North Korean state actor, identified by Microsoft as Sapphire Sleet, gained access to a maintainer's account and injected a malicious dependency that deployed a cross-platform remote access trojan. macOS, Windows, Linux. The malicious versions were up for hours before removal. The blast radius is still being assessed.

That same month, a campaign called TeamPCP cascaded across five ecosystems: GitHub Actions, Docker Hub, npm, OpenVSX, and PyPI. It compromised Trivy, a container security scanner that organizations use to protect themselves. Then LiteLLM, a widely used AI proxy library. Then Telnyx. It deployed the first known self-propagating npm worm with blockchain-based command and control infrastructure. The FBI confirmed the group was working through approximately 300 gigabytes of stolen credentials. And on systems configured for Iran, those with a Tehran timezone or Farsi language settings, it deployed a wiper called "kamikaze" that mounted the host filesystem, erased everything, and forced a reboot. Not a trojan. Not a miner. A weapon.

In April 2026, Socket identified over 1,700 malicious packages linked to North Korean hacking groups across npm, PyPI, Go, and Rust, published since the start of 2025.

This is not a curated selection of the worst cases over a decade. This is fifteen months.

The tree you never read

The event-stream hijack was 2018. Dependency confusion research goes back to 2021. Supply chain attacks are not new. What is new is the speed and the scale. Language models lower the barrier on the attacker side: generating a convincing typosquat with a plausible README, a passing test suite, and a realistic version history is easier than it has ever been. The kind of patience that took Jia Tan two years could, in principle, be partially automated and run across dozens of projects at once. We have not seen definitive proof that the attacks above used LLMs. But the capability is there, and the trend lines are converging.

Meanwhile, your project has maybe 40 direct dependencies. You chose those. You probably read the README, maybe glanced at the source. Those 40 packages depend on other packages. And those depend on others. A typical Node.js project resolves to 800 to 1,500 packages. Python, 100 to 400. The npm registry has over two million packages. PyPI has over half a million. Most have zero security review, zero funding, and a single maintainer with a day job.

You reviewed none of the transitive ones. Neither did anyone else.

When Axios was compromised, it was not the developers who chose Axios that were most at risk. It was the thousands of projects that depend on something that depends on something that depends on Axios. Three levels deep, invisible, and running in production. When a worm can spread to 500 packages in hours and the response depends on one person checking their email, the math does not work.

This is also a sovereignty question

In 2014, I wrote about free software and state sovereignty for United Explanations. The argument was straightforward: governments running proprietary software were ceding control over their own infrastructure to foreign corporations. They could not verify what the code did. They could not audit it. I cited Dorothee Belz, then vice president of Microsoft, testifying before the European Parliament about NSA mass surveillance:

There is no back door. Period. [...] If there were a back door, then I understand that I could not say so because it is part of the secrecy, of the rules I must respect in order not to talk, but I am telling you there is no back door.

Read that twice.

Twelve years later the layer has shifted but the argument is the same. When a government agency runs an application that depends on 1,200 npm packages maintained by anonymous individuals across 40 countries, it has no meaningful control over its own systems. When a hospital's software pulls in a Python library last updated by someone who may or may not still have access to their credentials, the supply chain stops being a technical abstraction and becomes a policy failure with consequences. And when a wiper deployed through a compromised PyPI package can target systems by timezone and language, this is no longer just a software problem. It is a geopolitical one.

Countries that moved early on this, Germany, France, Brazil, invested in free software infrastructure. But the dependency problem runs deeper than what operating system you use. It runs through every layer of the stack. No procurement policy currently addresses it.

What you can do

The awareness is catching up. CISA issued alerts. Maintainers are adopting stronger authentication. Registries are improving detection. But the response cannot be limited to the organizations with the largest security budgets. There are things anyone can do right now.

Pin your dependencies. Lock files exist for a reason. Use them. Review the diff when you update. Run npm audit, pip-audit, cargo audit. Free, takes seconds.

Know what you actually depend on. npm ls or pip list, look at the number. If it surprises you, that is the point. Tools like Socket, deps.dev, and Snyk can map your transitive dependencies and flag known risks.

Watch for maintainer changes. The xz attack vector was a transfer of ownership. Axios was a compromised account. When a critical dependency changes hands or publishes an unexpected release, pay attention.

Evaluate your own code. The vulnerabilities in your dependencies are someone else's problem to fix. The vulnerabilities in your code are yours. Scan it, understand your exposure, prioritize what to address. Quodeq does this, open source, runs locally, maps findings to CWE identifiers against ISO 25010. But the specific tool matters less than the habit of looking.

And fund the maintainers. If your company depends on an open-source library, pay the person who maintains it. Not through a foundation. Directly. The xz-utils maintainer was one person, burning out. The event-stream maintainer handed his project to a stranger because nobody else was willing to help. As Stallman would put it, the first dose was free. The bill is coming due.

The window

We are in a specific moment. The attacks have escalated sharply. The tools to respond are better than they have ever been. The open-source community is paying attention. Governments are starting to ask the right questions.

This is the window where things can get better, if enough people decide to act on it. Not next quarter. Not after the next incident. Now.

Andres Freund noticed a half-second delay and pulled on the thread. Most people would not have. But the fact that one person did, and that it was enough, is the reason the infrastructure still works.