What is a software supply chain attack?
A software supply chain attack is when an attacker compromises something you install rather than something you
wrote — a package, a dependency, an update — so their code runs on your machine as a trusted part of your project. You
never made a mistake in your own code; you just ran pip install, composer install or brew upgrade and pulled in
someone else’s compromise. OWASP ranks vulnerable and outdated components as A06 in its Top 10, and these attacks
have become one of the most common ways developers get owned.
This guide explains how supply chain attacks work, the main types, why the first hours after a release matter so much, and how to defend against them.
How a supply chain attack works
The install step runs code
The key fact most people miss: installing a package often runs its code. pip runs setup.py and post-install
hooks, Composer runs scripts, npm runs preinstall/postinstall, Homebrew runs formula code. So a malicious package
doesn’t wait for you to import it — it executes the moment it installs. By the time a post-install auditor
(pip-audit, composer audit, safety) runs, the code is already on disk and may already have run.
The main attack types
- Typosquatting — a package named one keystroke off a popular one, hoping you mistype or copy a bad command.
- Hijacked maintainer accounts — an attacker steals a real maintainer’s credentials and ships a malicious version of a legitimate, trusted package you already use.
- Dependency confusion — an attacker publishes a public package with the same name as one of your private internal packages, and the installer picks the public (malicious) one.
- Malicious updates to real packages — a previously-clean dependency turns bad in a new release, often deep in your tree as a transitive dependency you never chose directly.
Why the first hours matter most
When a malicious version is published, there is a window — often hours — before any advisory database indexes it. During that window, vulnerability scanners say “all clear,” because they only know about what has already been reported. The attack is most effective exactly when your tools are blindest.
This is why a freshness hold — refusing to install releases that are only hours or days old until they have had time to be vetted — blocks a whole class of attacks that no CVE database could catch in time.
Real incidents (as reported)
Two May 2026 campaigns make the pattern concrete:
- Laravel-Lang (Packagist) — attackers with stolen publish credentials pushed malicious releases across roughly
700 versions in minutes. The stealer ran through Composer’s post-install hooks, so by the time
composer auditcould flag anything, it had already executed. - TrapDoor — reported as the first simultaneous multi-ecosystem campaign: 34+ malicious packages across
PyPI, npm and Crates.io, auto-executing on import to steal SSH keys, cloud and GitHub credentials, and crypto
wallets. Every affected PyPI package was under 72 hours old when reported — a freshness hold alone would have
blocked all of them, no CVE required. TrapDoor also planted
CLAUDE.mdinjection payloads, tying it to prompt injection.
Further reading: OWASP — A06: Vulnerable & Outdated Components · TrapDoor (The Hacker News) · Laravel-Lang Packagist compromise (alert).
Who is at risk
Anyone who installs dependencies — which is everyone who ships software. Solo developers and small teams are hit just as hard as big companies, and often have less protecting them. If you let an AI coding assistant install packages for you, the risk compounds: the assistant installs with your credentials and access, so a malicious package runs as you, triggered by a tool acting on your behalf.
How to defend against supply chain attacks
Check before you install, not after
Move the security check to before the install runs, across the whole dependency tree — not just the package you named, but everything it pulls in. A post-install audit is a backstop; a pre-install gate is an actual gate.
Hold back the newest releases
Apply a freshness hold so brand-new versions wait a few days before they are allowed in. You give up nothing real — you almost never need a release that is hours old — and you close the window attackers depend on.
Fail closed
When a tool can’t verify something — an unreachable advisory feed, a version it can’t parse, a package it can’t age — it should hold, not wave it through. A gate that fails open gives false confidence, which is worse than no gate. Defaulting to “deny when unsure” is what makes a gate trustworthy.
The free tools 5bats builds for this
5bats turns those defences into pre-install gates you run locally, free, with zero third-party calls — across the package managers developers actually use (pip, Composer, Homebrew, and AI coding assistants):
- pip-cve-gate — a drop-in
pipwrapper that checks the full tree against OSV, the PyPI Advisory DB and the OSSF malicious-packages list, with a freshness hold, before pip runs anything. - composer-cve-gate — gates at
composer installfrom the lockfile (the gapcomposer auditleaves), across five signals. - homebrew-safe-upgrade — checks every formula and its dependencies, holds back too-fresh releases, verifies the download SHA, and fails closed.
- claude-code-cve-gate and mistral-code-cve-gate — intercept every install an AI assistant attempts, so it can’t pull a malicious package in with your access.
→ See how they fit together on the supply-chain gates page.
FAQ
What is the difference between this and pip-audit or composer audit?
Those are post-install audits — they run after the package’s code is already on your disk (and may have already
executed). A pre-install gate checks before anything runs, across the whole dependency tree, and can hold back
releases too new to have been vetted.
Why does 5bats not have an npm gate? By design. pnpm ships a built-in minimum-release-age setting and there are solid existing tools for the npm ecosystem, so 5bats doesn’t reinvent that. The gates focus on pip, Composer, Homebrew and AI coding assistants. npm still appears here as part of the threat picture — TrapDoor spanned it — just not as a 5bats tool.
Does a freshness hold slow me down? Rarely in any way that matters. You almost never need a release that is only hours old, and waiting a few days is what closes the window attackers rely on.
Spotted something wrong or missing?
5bats would rather be corrected than confidently wrong. If anything here is inaccurate, out of date, or a threat is missing, reach out over Session or email — corrections and additions are genuinely welcome.
Keep the tools free
This guide and the tools it points to are free and self-funded. If the work is useful, becoming a sponsor keeps it maintained, CVE-scanned and free for everyone — no account, no third-party tracker, just a plain link.
