Skip to main content

Delivery and supply chain · For Platform engineers, security architects, open-source maintainers

Supply-chain boundaries: what you build on is part of your system

Dependencies, base images and CI actions run with your privileges but are written by other owners. How to control what enters, know what you shipped, prove how it was built, and be a good upstream.

Most of the code you run in production was written by people you have never met. Your architecture should say where that trust begins and ends.

What you build on is part of your system

A modern service is mostly other people's code: hundreds of open-source packages, a base image, language runtimes, build tools, CI actions, and SDKs for every vendor it talks to. Each of them runs with your service's privileges. Each was written and published by an owner you do not control. In trust boundary terms, every one is an administrative-domain boundary that your code crosses at build time and keeps crossing at runtime.

Supply-chain attacks exploit exactly that: a maintainer's account taken over, a malicious version published to a public registry, a package name chosen to be confused with an internal one, a build system altered to inject code that never appears in the source. The defences are architectural. They decide where third-party code enters, what is allowed through, and what evidence travels with the result.

Draw the supply chain as boundaries

Draw the paths by which other people's code reaches production, the same way you would draw a request path.

Where other owners' code enters, and what travels with itOTHER OWNERSYOUR INTAKEYOUR BUILD AND RUNTIMEPackagemaintainerspublic registriesImage andaction authorsmutable tagsRegistry proxyapproved, cachedDigest pinsreviewed updatesBuild joblock files, noscriptsSigned artifactSBOM andprovenanceProductionverifies, thenrunspull throughpinresolvebaseimageattestsamedigest
Where other owners' code enters, and what travels with it. Packages enter through a registry proxy you control and are resolved from lock files; base images and CI actions are pinned by digest. The build attaches an SBOM and signed provenance to the artifact, and production verifies the signature before running it.
Read the diagram as text
  • Other owners: Package maintainers, Image and action authors
  • Your intake: Registry proxy, Digest pins
  • Your build and runtime: Build job, Signed artifact, Production
  1. Package maintainers → Registry proxy: pull through
  2. Image and action authors → Digest pins: pin
  3. Registry proxy → Build job: resolve
  4. Digest pins → Build job: base image
  5. Build job → Signed artifact: attest
  6. Signed artifact → Production: same digest

Control what enters

The intake column is where most supply-chain risk can be cut cheaply.

  • Resolve through a registry you control. A proxy or mirror in front of public registries gives you one place to record what was fetched, block known-bad versions and keep builds working when upstream disappears.
  • Lock everything. Lock files for packages, digests for base images, commit hashes for CI actions. A mutable tag is an invitation for someone else to change your build.
  • Reserve your internal names. Dependency confusion works when a build that expects an internal package can be satisfied by a public one with the same name. Scope internal packages to a namespace you own and configure resolvers to fetch them only from your registry.
  • Treat install-time scripts as code execution. Disable them where the ecosystem allows, and run builds without credentials that matter, as in our guide to securing CI/CD.
  • Update deliberately. Automated update proposals on a regular cadence, reviewed like any other change, beat both "never update" and "always take the latest the moment it appears".
  • Hold coding agents to the same intake. Agents that write code also choose packages and edit build files. Write the rules down where they read them: approved registries, lock files only, and no new dependency without review.

Know what you shipped

When the next widely used library turns out to be vulnerable, the first question is where it runs. A software bill of materials generated for every built artifact, in a standard format such as SPDX or CycloneDX and stored against the artifact's digest, answers it in minutes. Pair SBOMs with vulnerability exploitability statements where you have established that a product is not affected, so the answer includes "and why we are not exposed".

Then use that inventory as described in from vulnerability to architectural impact: presence is the first question, not the last.

Prove how it was built

An SBOM says what is inside an artifact; provenance says where it came from. Build provenance records the source revision, the build definition and the builder that produced an artifact. The SLSA framework describes levels of assurance for that record, from provenance that simply exists to provenance generated by a hardened build platform that the build itself cannot forge.

Sign artifacts and their attestations, using keyless signing tied to the build's identity where you can, and verify signatures and provenance at deployment. Verification is the step teams most often skip, and without it signing changes nothing.

Be a good upstream

Every organisation that publishes code, an SDK, a library, a container image, is part of someone else's supply chain. The same practices apply in reverse:

  • Publish a SECURITY.md that explains how to report a vulnerability and what reporters can expect.
  • Require strong multi-factor authentication for everyone who can publish releases.
  • Sign releases and publish provenance, so your users can verify what they install.
  • Issue advisories when you fix vulnerabilities, so downstream teams can act.

Plan for the attacks your controls will miss

Intake controls stop the common cases: typosquatted names, confused internal packages, a known-malicious version. Two kinds of attack get past them, and your architecture should assume they will.

A trusted upstream turns hostile. In 2024 a backdoor was found in releases of the xz compression utilities, inserted by a contributor who had spent years earning maintainer trust. Pinning and proxies do not help when the legitimate release is the malicious one. What helps is containment: services that run with narrow identities, little outbound network access and no more privilege than their job needs, so that a compromised library inherits a small blast radius. See failure containment.

The build itself is compromised. When attackers alter a build system, the source code stays clean and the shipped artifact does not. Reviewing source cannot catch this. Isolated, ephemeral build environments, provenance generated by the build platform rather than the build script, and reproducible builds where you can afford them are the controls that do.

In both cases, the SBOM and provenance you recorded earlier become the incident response tools: they tell you which artifacts contain the affected component, where they run, and which builds produced them.

A short checklist

  1. A drawing of every way third-party code enters your builds and runtime.
  2. All packages resolved through a registry you control, from lock files.
  3. Base images and CI actions pinned by digest or commit.
  4. Internal package names reserved and resolved only internally.
  5. An SBOM per artifact, stored against its digest.
  6. Signed provenance, verified before deployment.
  7. A published vulnerability disclosure policy for every repository you release.

Sources

  1. SLSA specification v1.0

    OpenSSF

  2. Cybersecurity Supply Chain Risk Management Practices (SP 800-161 Rev. 1)

    NIST

  3. Secure Software Development Framework (SP 800-218)

    NIST

  4. Software Bill of Materials (SBOM)

    CISA

  5. CycloneDX specification

    OWASP CycloneDX

  6. SPDX specification

    The Linux Foundation

  7. Sigstore documentation

    Sigstore

  8. Vulnerable Dependency Management Cheat Sheet

    OWASP

  9. Reported supply chain compromise affecting XZ Utils (CVE-2024-3094)

    CISA

Delivery and supply chain · 4 min read

Securing CI/CD and the paths into production

Whoever controls your pipeline controls production. How to separate building untrusted code from holding deploy power, replace stored secrets with federation, and make the artifact the unit of trust.

  • CI/CD
  • OIDC
  • supply chain

Practice · 4 min read

From vulnerability to architectural impact

A CVSS score describes a bug. Whether it matters depends on your architecture: is the code present, reachable, and holding privileges worth stealing? Four questions that turn an advisory into a decision.

  • vulnerability management
  • CVE
  • CVSS

Foundations · 4 min read

Architecture decision records for security

Security decisions outlive the people who made them. A practical ADR template with security fields, a worked example, and how to keep decisions honest against the system you actually run.

  • ADRs
  • architecture governance
  • risk acceptance