Software Supply Chain Security
Knowing what is in your software and proving where it came from: SBOMs, dependency hygiene, build provenance, artifact signing, and verification at deploy time.
Most of the code you ship was written by someone else. A typical application is a small amount of first-party code sitting on a large dependency tree, built by a pipeline that pulls tools and base images from the internet. Supply chain security is about knowing what is in there and being able to prove how it got there.
The attack surface
flowchart TD
A[Source repository] --> B[Build system]
C[Third-party dependencies] --> B
D[Base images] --> B
E[Build tools and plugins] --> B
B --> F[(Artifact)]
F --> G[Registry]
G --> H[Deployment]
A -.->|"compromised commit,<br/>malicious contribution"| B
C -.->|"typosquat, hijacked maintainer,<br/>malicious update"| B
D -.->|"tampered or stale<br/>base image"| B
E -.->|"compromised build plugin"| B
B -.->|"compromised runner<br/>injects into the artifact"| F
G -.->|"artifact substitution<br/>in the registry"| H
Every arrow is a documented attack path. The defences fall into three groups: know what you have (SBOM), control what you pull in (dependency hygiene), and prove what you built (provenance and signing).
Software bills of materials
An SBOM is a machine-readable inventory of every component in an artifact. Its value is not compliance paperwork — it is the ability to answer “are we affected?” in minutes rather than days.
When a widely-used library has a critical vulnerability disclosed, the organizations that respond within hours are the ones that can query an SBOM inventory. The ones that spend a week are grepping repositories.
| Property | Requirement |
|---|---|
| Generated at build time | Reconstructing it later is guesswork; the build knows the exact resolved versions |
| Complete transitive tree | Direct dependencies are the minority of the risk |
| Standard format | SPDX or CycloneDX, so tooling is interchangeable |
| Stored with the artifact | Attached to the image or registry entry, keyed by digest |
| Queryable across the estate | “Which running services contain library X at version Y?” must be one query |
The last row is where most SBOM programmes fall short. Generating SBOMs and storing them in a bucket produces files, not answers. The inventory must be searchable across everything currently deployed.
Dependency hygiene
Pin everything, by digest where possible. A version range means your build is not reproducible and a compromised release lands automatically. A container base image referenced by tag can change underneath you; referenced by digest, it cannot.
Use lockfiles and commit them. The lockfile is the record of exactly what was resolved. Builds must fail rather than silently re-resolving when the lockfile does not match.
Update on a schedule, automatically. Automated dependency update pull requests keep the delta small and reviewable. The alternative — annual bulk upgrades — produces changes too large to review and too risky to ship.
Use a private proxy registry. Proxying public registries gives you caching, availability if the upstream disappears, and a control point where you can block packages by policy. It also protects against dependency-confusion attacks, where an attacker publishes a public package with the same name as your internal one.
Watch for the specific attack patterns:
| Pattern | Defence |
|---|---|
Typosquatting (reqeusts for requests) | Allowlist for new dependencies; review in the pull request |
| Dependency confusion | Private registry configured to never fall through to public for internal namespaces |
| Hijacked maintainer account | Pin by digest; delay adoption of brand-new versions |
| Malicious install scripts | Disable post-install scripts by default; build in a sandbox |
Build provenance
Provenance is a signed statement about how an artifact was produced: which source commit, which builder, which parameters. It lets a consumer verify that an artifact came from the pipeline it claims to.
sequenceDiagram
participant S as Source repo
participant B as Build system
participant A as Attestation service
participant R as Registry
participant D as Deploy admission
S->>B: commit abc123, triggered by a merge
B->>B: build in an isolated, ephemeral environment
B->>A: request signed provenance:<br/>source, commit, builder identity, inputs
A-->>B: signed attestation
B->>R: push artifact + attestation + SBOM
D->>R: fetch artifact and attestation
D->>D: verify signature, builder identity,<br/>source repository, and policy
alt verification fails
D-->>D: refuse to deploy
else verified
D-->>D: admit
end
The framework most organizations align to here defines increasing levels of build integrity — roughly: provenance exists, provenance is generated by the build platform rather than by the build script, and the build environment is isolated and non-falsifiable. The practical progression for most teams:
- Build only from version control, never from a developer machine.
- Ephemeral, isolated build environments — a fresh runner per build, so one build cannot influence the next.
- Provenance generated by the platform, not by a step the build itself controls.
- Verification enforced at deploy time, so unsigned or unverifiable artifacts are refused.
Step four is what makes the rest meaningful. Signing artifacts that nobody verifies is a filing exercise.
Signing and verification
Keyless signing — where a short-lived certificate is bound to the workload identity of the build, and the signature is recorded in a public transparency log — has largely replaced long-lived signing keys, and for good reason: there is no key to steal, and the transparency log gives you independent evidence.
Verification belongs at admission:
- Signature valid and traceable to your build system’s identity.
- Provenance matches expectations — expected source repository, expected builder, expected branch.
- SBOM present and free of policy-violating components.
- Not on a denylist of artifacts withdrawn for known vulnerabilities.
Continuous re-evaluation
An artifact that was clean at build time does not stay clean. New vulnerabilities are disclosed against dependencies that have not changed.
flowchart LR
A[Vulnerability disclosed] --> B[Match against the SBOM inventory<br/>of everything currently deployed]
B --> C{Affected services?}
C -->|None| D[Record the evaluation and move on]
C -->|Some| E[Assess exploitability:<br/>reachable? exposed? patch available?]
E --> F[Open prioritised tickets<br/>routed to owning teams]
F --> G[Automated dependency bump<br/>pull requests where a fix exists]
This loop is the payoff for the whole programme. Everything upstream — SBOM generation, inventory, ownership metadata — exists so that this query returns an answer in minutes.
Adoption checklist
- SBOMs are generated at build time, stored with the artifact, and queryable across all deployed services.
- Dependencies are pinned; lockfiles are committed and enforced.
- Automated dependency update pull requests run continuously.
- A private proxy registry protects against dependency confusion and upstream outages.
- Builds run only from version control, in ephemeral isolated environments.
- Provenance attestations are generated by the build platform.
- Artifacts are signed, and signature plus provenance are verified at admission.
- New CVEs are matched against the deployed inventory automatically.
Last updated 19 Aug 2026, 00:00 UTC.