The Build Pipeline Is the New Perimeter Nobody Is Defending
The SolarWinds breach in 2020 demonstrated something the security industry had discussed in theoretical terms for years and then, apparently, decided to continue treating as theoretical. An attacker with access to a software vendor's build pipeline can ship malicious code to every customer who trusts that vendor's updates. Not by breaking into the customer directly. By breaking into the thing that builds what the customer runs.
That lesson is still not reflected in how most organizations think about their attack surface. Production environments get hardened. Endpoints get EDR. Networks get segmented. And then the engineer opens a terminal and runs "npm install," and three packages from a public registry land in the codebase without anyone checking what they actually contain.
How Dependency Confusion Works
Dependency confusion is not a complicated attack. It is embarrassingly simple, which is part of why it kept working well into 2021 after researcher Alex Birsan published his findings against Apple, Microsoft, and PayPal, among others.
Here is the mechanism. An organization uses internal packages distributed from a private registry. Those packages have names that are also valid names in public registries like npm, PyPI, or RubyGems. If the package manager's configuration does not explicitly pin to the private registry, it will in some cases prefer the public registry, on the assumption that higher version numbers mean newer packages.
An attacker registers those internal package names on the public registry, uploads a version with a higher number than the internal one, and waits. Engineers run builds. The build tooling silently fetches the attacker's version instead of the legitimate internal one. The attacker's code executes in the build environment, often with the same access and secrets available to the legitimate build process.
No phishing. No exploitation. Just a cleverly named package sitting in a registry that the build tooling trusted without being told to.
The Registry Is Only Part of the Problem
Dependency confusion gets attention because it has a clear mechanism and good press coverage. The broader problem is that the build pipeline as a whole is underdefended in ways most organizations have not audited.
CI/CD systems run with significant privilege. They need to pull source code, build artifacts, push to registries, deploy to environments, and access secrets for all of these operations. That means the build agent typically holds credentials for cloud accounts, container registries, package repositories, and deployment targets. Compromise the build system and you have access to all of it, plus whatever runs next time a pipeline executes.
Build systems are also persistent and networked in ways that make them valuable lateral movement targets. A build server with outbound internet access (most do), authentication to production (many do), and a long list of stored environment variables containing API keys and tokens is a significant target sitting entirely outside the normal attack surface mental model most defenders work from.
The pipeline configuration files themselves are an attack surface. A malicious pull request that modifies a GitHub Actions workflow or a Jenkins Jenkinsfile can inject commands that run during CI with full pipeline credentials. In repositories with insufficient branch protection, or in open-source projects where external contributors can trigger builds, this is a practical path to credential theft with no exploit required.
What an SBOM Does and Does Not Do
Software Bill of Materials documents have received significant regulatory attention since executive order 14028 in 2021 made them a requirement for software sold to the federal government. The concept is sound: know what is in your software so you can reason about its risk. The execution is still maturing.
An SBOM tells you what components your software contains. It does not tell you whether those components are malicious, whether they came from the source you intended, or whether the build process that assembled them was trustworthy. A perfectly accurate SBOM produced from a compromised build is an accurate record of a problem, not a solution to one.
SBOMs are most useful in combination with other controls. Continuous vulnerability monitoring against the component list is the main practical application today. Knowing that a specific library is in your software means you can act when a critical CVE lands against it, rather than spending two days hunting through repositories to find out where you are exposed.
What SBOMs are not, despite some of the policy language around them, is a supply chain security solution. They are an inventory tool. Inventory is necessary but not sufficient.
What Actually Changes the Exposure
Private registries with explicit scope configuration close the dependency confusion path. When build tooling is configured to only look at the internal registry for internal package names, and that configuration is enforced rather than advisory, the attack requires compromising the registry itself rather than registering a public package.
Build environment isolation limits blast radius. A build agent with exactly the access it needs for one pipeline, no persistent credentials, and no access to production systems is a smaller target than a shared build server with ambient cloud permissions. Ephemeral build environments that spin up, run one job, and disappear take this further. The attacker has to compromise every run rather than persisting in the build infrastructure.
Code signing on build outputs creates verifiability. If you can confirm that a container image or binary was produced by your build system from the source you intended, you have a check that passive monitoring cannot provide. Projects like Sigstore have made artifact signing significantly more accessible than it was five years ago.
Dependency pinning - locking exact versions and verifying checksums rather than accepting any version that satisfies a range - prevents silent substitution. It adds operational friction when you need to update dependencies. That friction is the correct amount of friction for this part of the pipeline.
The Mental Model That Is Missing
The gap is not tooling. It is that most organizations have not extended their threat model to include "someone compromised the thing that builds our software" as a realistic scenario worth defending.
Production environments get attacker-model threat modeling. The build pipeline gets a policy about not hardcoding secrets. Those are not the same level of scrutiny applied to systems with comparable or greater effective access.
The SolarWinds attacker did not get into 18,000 customer networks by compromising 18,000 perimeters. They compromised one build pipeline and let the software distribution process carry the payload the rest of the way.
If your threat model stops at your production boundary, it stops before the attack started.