Load Path Research The mechanism you have vs the property you need

Blog

Your Software Supply Chain Includes the Build Server

The malicious code in the xz utils release was not in the git repository. It was in the release tarball, injected by a modified build script during the packaging process. The diff the community reviewed was not the diff that shipped. The backdoor was discovered because a developer noticed anomalous CPU and login latency on a Debian testing machine, not because a scanner flagged a suspicious dependency.

That discovery path does not scale. The load-bearing lesson from the incident is that the artifact differed from the reviewed source, and the difference happened at build time. For an enterprise, this means the gap between the git commit and the deployed binary is where the actual supply chain lives.

Source code represents intent. The compiled binary represents reality. The build server is the mechanism that translates one to the other, yet it is usually treated as plumbing. It is the only machine in the estate with keys to both the source of truth and production. It holds deployment credentials, signing keys, and write access to the artifact repository. We lock down developer laptops with endpoint agents and hardware keys, then hand root access to production to an unpatched Jenkins controller running in a forgotten sub-account.

Most enterprises can name the people authorized to deploy to production. Fewer can name everyone authorized to change the job that deploys there. “Only maintainers can merge” is an incomplete control when a shared build template, a reusable marketplace action, or a base image can change independently of the application repository. During a release review, the application repository might show two approved commits, but if the release job fetched the current version of a mutable CI template, nobody in the review can say which revision actually ran.

When a supply chain incident hits, the first question from the steering committee is whether the organization is affected. The security team runs a Software Bill of Materials scan. An SBOM tells you what components an artifact claims to contain. It does not tell you who built it, from which inputs, or whether anyone changed it in between. An SBOM generated by a compromised build server is just a forged shipping manifest.

Static analysis and Software Composition Analysis tools look for known vulnerabilities in declared dependencies. They do not catch build-time injections. If a malicious script modifies a binary during compilation, or if the build server pulls an undeclared dependency at runtime, the scanner will not see it. Signed commits prove that a specific developer authored a specific change in the source repository. They do not prove that the deployed binary was generated exclusively from that source code.

Inventory tells you where to look. Provenance tells you which artifact you are looking at.

In a typical enterprise build environment, a junior engineer submits a pull request. The CI server automatically runs unit tests, pulling untrusted code into a persistent runner environment that also holds the deployment keys for the core billing application. A deployment credential is restricted to the release job, but a contributor who cannot read the credential can edit a script that the release job executes after approval.

In the artifact repository, overwrite is enabled. Version 1.4.2 was rebuilt with a hotfix and re-pushed under the same version string. There are now two different binaries sharing one version string, and no record of the swap. A Dockerfile pipes a remote install script into a shell, from a URL that now redirects somewhere else. The build server runs code from a hundred vendors—CI plugins, base images, package mirrors. It is the most third-party-dense machine in the estate and the least inventoried.

If your build server can push to production, your build server is production. It just doesn’t page anyone.

Securing this path means treating the pipeline with the same access controls as a production database. The first step is separating the build identity from the deploy identity. The build system should publish the artifact; a separate deploy system should retrieve it. The build agent should never hold production write credentials. Move from persistent credentials to short-lived, workload-identity tokens that expire immediately after the run.

Pipeline definitions must live in a repository that receives the same branch protection and review bar as production code. Those protections must apply to bot accounts and admin tokens, not just humans. Inventory every principal that can modify the pipeline: humans, service accounts, webhooks, deploy keys, and the CI vendor’s own support path.

Builds must be hermetic. Base images must be pinned by digest, not by mutable tags like ubuntu:22.04, which resolve to a different digest every month. Dependencies must be vendored or locked, and network access disabled at build time. Build the artifact once and promote that exact digest through every environment. If you rebuild per environment, what you tested in staging is not what you shipped to production.

Provenance is a claim about a build. Attestation is a claim someone else can check. The provenance record must be generated by the build platform, not by developer tooling, and stored in a tamper-evident log where the artifact’s owner cannot quietly edit it. Signing key custody must be separated from build system execution. A job that runs third-party code should not automatically receive the credential that makes its output trusted. A signature is theater if the key lives in a secret store a hundred people can read.

Reproducible builds are a property, not a project. You either have them or you have a hope. If you cannot independently compile the source code on a separate machine and match the cryptographic hash of the deployed binary, you cannot prove the build environment was clean.

The next supply chain compromise will not be caught by a dependency scanner. It will be caught when the cryptographic proof of what is running in production does not match the record of what was built. Most enterprises cannot produce that record.

Takeaways

  1. The xz utils backdoor was injected at build time, not in the reviewed source repository
  2. SBOMs and SCA scanners cannot detect build-time injection or mutable pipeline components
  3. Build systems must be separated from deploy credentials and treated as production systems
  4. Hermetic, reproducible builds with tamper-evident provenance are the only way to prove what shipped

Questions

Why didn't the xz utils backdoor show up in the source code review?
The malicious code was not in the git repository. It was injected into the release tarball by a modified build script during packaging, so the diff the community reviewed was not the diff that shipped.
Does an SBOM protect against build-time supply chain attacks?
No. An SBOM tells you what components an artifact claims to contain, but it does not tell you who built it, from which inputs, or whether anyone changed it in between. An SBOM generated by a compromised build server is just a forged shipping manifest.
What is the first step to securing a build pipeline?
Separate the build identity from the deploy identity. The build system should publish the artifact and a separate deploy system should retrieve it. The build agent should never hold production write credentials.

Tags

  • supply chain
  • ci cd
  • secrets
  • privileged access