How software supply chains get poisoned
Every binary that runs in production was built somewhere. Every build pulled dependencies from somewhere. Every dependency was authored by someone with commit access. A software supply chain is the entire pipeline from authorship to runtime, and an attacker can poison any link in it.
The pattern catalog below names where the poison goes in. The cases name where it has actually gone in.
The attack tree
Authorship compromise. Someone with commit access to an upstream package pushes code that does what they want it to do.
Account takeover. The upstream author lost their credentials. The attacker pushes code as them. The author may not know for weeks.
Typosquatting and dependency confusion. The attacker registers a package whose name is one keystroke off from a legitimate package, or whose internal-package name was discoverable and is now registered publicly with a higher version. Naive install paths pull the attacker's package. Alex Birsan's 2021 research demonstrated the dependency-confusion variant at scale; recurring PyPI typosquatting waves through 2023 and 2024 show the pattern stays profitable.
Build infrastructure compromise. The attacker compromises the CI/CD system that produces the artifact. The source repository may be untouched; the artifact is poisoned in flight.
Distribution channel compromise. The artifact was correct at build; the distribution server was tampered with.
Runtime poisoning. A legitimate dependency does something legitimate at install time that is malicious. npm postinstall scripts are a recurring entry point.
Each link is its own audit class. Most large incidents combined two or more.
Documented incidents
The case literature is where the pattern catalog comes from.
SolarWinds (2020). Build infrastructure compromise. Attackers gained access to the Orion build pipeline and injected backdoored code into a routine update signed by SolarWinds' own signing infrastructure. Eighteen thousand customer installs received the backdoor. The source repository was not directly modified; the artifact was.
Codecov (2021). A misconfigured Docker image in Codecov's build infrastructure exposed credentials that allowed modification of the Bash uploader script. The modified uploader exfiltrated environment variables from every CI run that used it. Build infrastructure compromise with downstream credential harvesting.
ua-parser-js (2021). Account takeover. The maintainer's npm account was compromised. The attacker published three malicious versions of a package with seven million weekly downloads. The versions contained crypto-mining and credential-stealing code in preinstall hooks.
event-stream (2018). Maintainer handoff. The original maintainer transferred ownership to a new maintainer who turned out to be the attacker. The new maintainer added a malicious dependency that targeted specific cryptocurrency wallets. This is the most-cited example of social engineering against open-source maintainership.
The late-2025 OtterCookie-attributed npm wave. A campaign tracked by Red Asgard and corroborated by Socket.dev and The Hacker News published roughly 197 malicious npm packages with combined downloads on the order of 31,000. The packages were authored under a single GitHub account and chained into developer machines through fake-interview lures. Details and attribution are in our Inside the Machine research article.
xz-utils (2024). Long-running social engineering. An attacker contributed legitimate-looking work to the xz-utils project over roughly two years, built trust, gained maintainer rights, and inserted a backdoor in the build script (not the source) that introduced an SSH server backdoor when the package was built in specific conditions. Discovered before mass deployment.
Where to look
A reviewer hunting for supply-chain risk in a real deployment looks at the following surfaces.
The dependency graph. Every direct dependency, every transitive dependency, the pinning state of each, and the gap between the version in the lockfile and the latest published version. Floating versions are an open door.
The build pipeline. Every script that runs at build time. Every credential that pipeline has access to. Every artifact-signing key. Every external service the pipeline calls.
The install-time scripts. postinstall, preinstall, prepare, build for npm; setup.py for PyPI; Cargo build scripts for Rust. Anything that runs code on the developer's machine before the developer runs the package.
The maintainer set. Who has commit access. Who can publish. Whether commit access uses hardware-key 2FA. Whether publish access uses npm provenance, Sigstore signing, or equivalent attestation.
Recently changed packages. A package that has not changed in two years and suddenly publishes a 0.0.1 increment with new postinstall behavior is a probable account takeover.
Defensive primitives
Dependency pinning to exact versions in lockfiles. SemVer floating ranges (^1.2.3) accept new minor versions automatically. Pinning to exact versions (1.2.3) forces a deliberate update.
SBOM generation in CI. Software Bill of Materials lists every component in the build. An SBOM does not prevent an attack; it makes detection possible after the fact. OpenSSF Scorecard complements SBOMs by automating a checker for repository security posture (branch protection, signed releases, dependency-update discipline) and producing a comparable score across dependencies.
Reproducible builds. A build is reproducible if anyone with the source can produce a byte-identical artifact. SolarWinds was not detected through reproducibility; reproducible builds would have made detection mechanical.
Sigstore and npm provenance. Cryptographic attestation that an artifact was built from a specific source by a specific build pipeline. Adoption is uneven; where present, it eliminates the "artifact poisoned in flight" attack class.
OIDC for CI credentials. Replacing long-lived API tokens in CI with short-lived OIDC-issued credentials limits the blast radius of a build-pipeline compromise.
Runtime egress controls. A build pipeline that can only connect to package registries and source control cannot exfiltrate to an attacker's collection server. Most pipelines have unrestricted egress.
Monitoring of *install script content. A diff of postinstall script behavior across versions of the same package is a high-signal alert.
SLSA framework alignment. The defensive primitives above map roughly to SLSA (Supply-chain Levels for Software Artifacts) levels 1 through 3. SLSA is the most widely-referenced maturity model for supply-chain defense; pinning a deployment to a target SLSA level converts ad-hoc hardening into a measurable engineering goal.
What this looks like as an engagement
Supply-chain reviews at Red Asgard are an extension of the broader threat-intelligence work documented in our Hunting Lazarus research series and the Inside the Machine consolidated report. The campaign documented there targets developer machines through the supply chain we are describing here; the npm wave is one example of the dependency-graph compromise pattern above.
A supply-chain engagement scopes to the surfaces above. The deliverable is a risk inventory: dependency-graph audit, build-pipeline audit, install-time script audit, maintainer-set audit, and prioritized remediation. Where the engagement scope includes Red Asgard's active threat intelligence, the report includes current indicators relevant to the deployment.
What clean means
A clean supply-chain audit means the deployment's current surfaces are mapped and the obvious risks are remediated. It does not mean a future zero-day in a dependency cannot land. We say so explicitly. The defenses above are not insurance; they are friction against an attacker who would otherwise have free movement.