November 2, 2026
We’re Building Software Faster Than We Can Secure It
I’ve been writing software for a while, and in that time I’ve read a lot of breach headlines, nodded at most of them, and moved on.

By Ejiro Onose
7 min read
But the headlines I saw this October were different. I read every one, and then I read them again.
Because in a single month, attackers went after the hosting platforms, package registries, and release pipelines that developers rely on every day. And the surprising thing is they didn't do it with clever exploits or with zero-days. They used stolen tokens, reused credentials, and hijacked accounts. The kind of things we all know we should guard but somehow never do.
So I'm writing this for the developer and non-developer who's about to ship something, the small team that can't afford a security team, and the solo founder who thinks this is someone else's problem. It isn't.
This is the story of what happened in October, why it keeps happening, and what we're supposed to do when the tools we build with become the attack surface.
To understand how we got here, it helps to start somewhere strange.
Agents
In July, an autonomous AI agent broke into Hugging Face's production infrastructure. Over one weekend, it logged more than 17,000 actions, exploited code-execution flaws in the dataset-processing pipeline, and harvested cloud credentials from a compromised worker. In case you missed the first part, let me repeat it: it was "autonomous", meaning nobody directed its individual steps; it did it all by itself.
Later reporting linked the model to OpenAI. In an August 26 post, OpenAI described it as an "unprecedented cyber incident" during an internal capability evaluation, in which a model escaped its evaluation environment and reached the internet. The precise degree of autonomy, intent, and coordination remains a matter of reporting and interpretation.
But then again, let's pause for a moment to really take that in… a model bypassed the security set for it and journeyed into the abyss of the internet.
The Hugging Face incident was a disturbing incident, and I'm not going to pretend otherwise. But what happened in October bothers me for a different reason, because the attackers didn't need an autonomous agent. All they needed were credentials that hadn't been rotated, a maintainer account they could take over, or a release process that let them replace a legitimate binary. They found those weaknesses in systems developers use every day.
The agent is the story we'll tell for years, and I'll explore in another piece, but this case of stolen credentials is the problem we keep leaving unfixed.
Four ways trust failed in October
I started looking at the October incidents side by side, and a few things were clear:
- The companies were different.
- The products were different.
- The technical details were different.
But in almost every case, the attack didn't need to defeat the system's idea of trust; it needed to borrow it. Let's explore what happened.
1. Stolen identity and reused credentials.
On October 21, Railway disclosed that an unauthorised party may have accessed customers' encrypted environment variables and decrypted some of them. A week earlier, an attacker used leaked internal credentials to read Zeabur's platform database. Zeabur had kept those credentials in plaintext and never rotated them after an August breach. It has since announced it will shut down by December 31.
2. Malicious packages and worms.
Then there's npm.
A malicious release of tensorlake carried the Shai-Hulud worm. It's a worm that can access environment variables, cloud credentials, SSH keys, browser stores, and cryptocurrency wallets.
But stealing credentials is only part of the problem. The worm can also use a victim's own npm packages and GitHub repositories to spread further. It even includes a dead-man's switch that wipes the home directory if its embedded token is revoked.
Earlier in the month, SubQuery's @subql/common package also shipped malware designed to hunt for npm logins, SSH keys, and AWS profiles.
This is what makes malicious packages particularly worrying. We install dependencies because we trust the people and projects behind them. We don't usually stop to consider that the package we just installed could be looking for the keys to everything else.
3. Release-pipeline and update-channel abuse.
Some of the October incidents were even more revealing because they showed how much damage someone could do without touching your source code.
On October 8, the GhostAction campaign used two compromised maintainer accounts to push a credential-stealing GitHub Actions workflow into 345 repositories across two short windows.
A newer variant went further, scanning entire Git histories for cloud and AI API keys rather than limiting itself to secrets stored in GitHub Actions.
Then, on October 19, an attacker hijacked a developer's GitHub account and replaced the binaries on five OVHcloud CLI releases.
These 3 attacks are interesting because they didn't touch the source code or the CI pipeline; they built the malware locally and uploaded it over existing releases. And GitHub allowed this because release immutability had not been enabled. The malicious builds remained available for about an hour and a half and were downloaded roughly 250 times.
On that same day, attackers hijacked LiteLLM's release pipeline using stolen GitHub credentials belonging to three engineers. They published a credential stealer and remote loader to PyPI and Docker Hub. The malicious release remained available for about two hours.
Notice the pattern here: You could review the source code, run your usual checks, and still end up distributing something malicious because the release process itself had been compromised.
4. Trust-chain weakness:
Browsers blocked eight of eMudhra's intermediate certificates after researchers showed their private keys could be recovered almost instantly because they were generated with insufficient randomness.
eMudhra says the affected intermediates had issued no end-entity certificates and that the root keys were not compromised.
This one shows how a single weak key-generation can put pressure on the web's trust chain even when the blast radius stays contained.
The base rate is the point
Now, I know what someone might say here: October was just a bad month. A collection of unrelated incidents doesn't automatically mean we're dealing with a growing problem.
That's fair.
But I think there's another question worth asking. Were these incidents unusual, or were they simply making a much bigger problem harder to ignore?
I think they do, and here's why:
- Researchers scanning public Docker Hub images have repeatedly found that somewhere between 2.5% and 3% of publicly listed images carry malicious payloads. That's hundreds of thousands of images on a registry that handles billions of pulls every month.
- A large ACM measurement of influential Docker images found that 93.7% contained known vulnerabilities, while thousands had leaked secrets.
- On npm, the Shai-Hulud worm alone has affected hundreds of package versions across multiple waves in 2026.
These are signs of a software ecosystem that has been carrying significant security risks for some time.
Ironically, October is a cyber-security month.
Now add AI on top
AI makes an existing problem harder to contain because we're giving more tools the ability to act.
An install script can already steal credentials. A GitHub workflow can already read secrets. A compromised maintainer account can already publish malicious code. None of that requires AI.
Coding agents change how much access we hand out. An agent that can edit a repository, run shell commands, install packages, and read cloud credentials has a lot of power. If it makes a mistake, follows malicious instructions, or runs in a compromised environment, that access gets used against you.
The Hugging Face incident is an extreme example. The everyday version is a coding tool with permissions nobody has properly considered because granting them was the quickest way to get it working.
The answer is to stop treating access as an afterthought. A tool that needs to read code doesn't automatically need permission to publish a release. A tool that can suggest changes doesn't necessarily need production credentials. Those boundaries matter whether the tool is human-operated or autonomous.
"Supply-chain risk isn't new"
True, and I've made that point myself. Package registries have been target practice for years. Build systems have been breached before. Stolen maintainer credentials are nothing new.
But just because a problem is old doesn't mean we've dealt with it thoroughly.
Modern software development relies on layers of dependencies and third-party services maintained by people we'll likely never meet. A single dependency pulls in dozens more. A single compromised account can push updates to hundreds of repositories, or a single malicious release can reach thousands of production machines before anyone realises what happened.
With AI now, we've made software exponentially easier to build and deploy, but security hasn't kept pace with that speed.
The standard checklist still matters:
- Review dependencies
- Protect secrets
- Keep systems patched
But it doesn't help much if someone can replace the binary after you've reviewed the code, or use a trusted maintainer's account to publish a malicious update.
We need to pay more attention to what happens between writing code and running it.
What this means in practice
If the risk sits in the things we trust rather than the code we write, the response has to move there too.
Treat release credentials as production secrets: A GitHub token that can publish a package is not a convenience. It is a production key. Rotate it, scope it, and assume it is a target.
Turn on release immutability: OVHcloud's incident happened because existing release assets could be overwritten. If your registry or forge supports immutable releases, enable it. If it does not, treat every mutable release channel as hostile.
Pin and verify, including images: Do not trust latest. Verify digests and signatures where the ecosystem supports them. A 2.5–3% malicious-image rate is low per pull and large at registry scale.
Assume maintainer accounts are compromised until proven otherwise: GhostAction did not exploit a bug. It used two accounts to push a workflow into hundreds of repositories. Review unexpected workflow commits, especially ones with reassuring names.
Audit what AI coding tools can read and run: The Hugging Face case is an extreme example, but the everyday version is simpler: an agent with repository write access, cloud credentials, and the ability to run install hooks is a supply-chain participant. Give it the minimum it needs, and log what it does.
Secure the release, not just the repo
We're not going to stop building software quickly, but securing the code is only one part of securing what we ship.
I spent October reading breach reports and recognising my own habits in them: trusting npm, trusting a project's GitHub releases, assuming credentials got rotated after a breach. That's how most of us get work done.
The systems we rely on are only as trustworthy as the credentials, permissions, and release processes behind them. When those fail, better code won't save us. Before your next release, check who can overwrite it