In December 2020, attackers compromised the build pipeline of SolarWinds' Orion software and inserted malicious code into a legitimate, signed software update. That update was distributed to approximately 18,000 organisations — including US federal agencies and Fortune 500 companies — as a trusted software package.
SolarWinds was not the last of its kind. In 2024, a sophisticated two-year effort to backdoor the XZ Utils compression library nearly succeeded in compromising SSH daemons on millions of Linux servers. It was caught by accident.
These attacks share a characteristic: they didn't breach the target organisations directly. They compromised the supply chain — the tools, libraries, build systems, and distribution mechanisms that organisations trust to deliver software.
This is now one of the most important attack vectors in enterprise security.
Understanding the Attack Surface
Your software supply chain includes:
- Open-source dependencies — The npm packages, PyPI libraries, NuGet packages, and Maven artifacts your code depends on (and their transitive dependencies)
- Build tools and CI/CD systems — Your CI/CD platform, build scripts, test runners, code signing infrastructure
- Container base images — The OS images your containers are built from
- Infrastructure tooling — Terraform modules, Helm charts, Ansible playbooks sourced from external registries
- Developer machines and IDE plugins — Compromised developer environments or IDE extensions
- Third-party integrations — SDKs, webhooks, and APIs from external services embedded in your code
An attacker who compromises any of these can deliver malicious code to your production environment through your normal, trusted deployment pipeline.
SLSA: Supply-Chain Levels for Software Artifacts
SLSA (pronounced "salsa") is a security framework and graduated set of requirements for supply chain integrity, originally developed by Google and now governed by the Open Source Security Foundation (OpenSSF).
SLSA defines four levels, each building on the previous:
- ✓Build process documented
- ✓Automated build (no manual steps)
- ✓Provenance generated
- ✓Provenance available
- ✓Hosted CI/CD platform
- ✓Signed provenance
- ✓Immutable build service
- ✓Source code retention
- ✓Hardened build environment
- ✓Isolated build process
- ✓Non-falsifiable provenance
- ✓Retention of ephemeral credentials
- ✓Two-person review for all changes
- ✓Hermetic builds (no network access)
- ✓Reproducible builds
- ✓Comprehensive audit log
SLSA is a journey, not a binary. Level 1 is achievable immediately; Level 4 requires significant investment.
Where to start: SLSA Level 2 is the realistic target for most organisations within 3–6 months. It requires using a hosted CI/CD platform (GitHub Actions, Azure Pipelines) and generating signed provenance for your artifacts. This is achievable without rebuilding your pipeline.
SLSA Level 1 is achievable this week: Generate a provenance file (SLSA generator action for GitHub Actions) that documents what code was built, by what pipeline, at what time. This creates an auditable chain of custody for your builds.
Software Bills of Materials (SBOMs)
A Software Bill of Materials is a machine-readable inventory of all components in a software package: libraries, frameworks, their versions, their licenses, and their known vulnerabilities.
Think of it as the ingredient list for your software. Without an SBOM, you don't know what's in your product — and you can't know quickly which of your products is affected when a new vulnerability like Log4Shell is discovered.
SBOM formats:
- SPDX — Linux Foundation standard, ISO 5962 specification
- CycloneDX — OWASP standard, designed for security use cases
Generating SBOMs:
For container images: syft (Anchore) generates SBOMs from container images, filesystems, or lock files.
syft your-image:latest -o spdx-json > sbom.spdx.json
For application dependencies: Language-specific tools like cdxgen, spdx-sbom-generator, or integrations in your package manager.
SBOM-driven vulnerability tracking: Once you have an SBOM, you can feed it to a vulnerability intelligence tool to continuously monitor whether any component has a newly discovered CVE:
- Grype (Anchore) — scan SBOMs against the NVD and multiple vulnerability databases
- Trivy (Aqua Security) — container and filesystem scanning with SBOM support
- Dependency-Track — open-source platform for continuous SBOM analysis and vulnerability tracking
SBOM as a regulatory requirement
The US Executive Order 14028 (2021) now requires SBOMs from software vendors selling to the US federal government. EU CRA (Cyber Resilience Act) will require SBOMs for products sold in the EU. Even if you're not currently in scope, producing SBOMs now positions you for enterprise and government sales requirements.
Securing Your CI/CD Pipeline
Your CI/CD pipeline is production infrastructure. It has access to your secrets, your production environment, and your deployment mechanisms. A compromised CI/CD pipeline is as catastrophic as a compromised production server — often more so.
Principle 1: Least Privilege for CI/CD Credentials
CI/CD pipelines need credentials to deploy to production — but those credentials should have the minimum necessary permissions and be scoped to specific environments.
Azure Pipelines: Use Azure Managed Identities with Federated Credentials (OIDC) instead of service principal secrets. The pipeline proves its identity with a short-lived token; no long-lived secret is stored or can be stolen.
GitHub Actions: Use OpenID Connect (OIDC) with Azure, AWS, or GCP. The workflow exchanges a GitHub token for a short-lived cloud provider token, scoped to the specific repository and branch:
permissions:
id-token: write
contents: read
steps:
- uses: azure/login@v2
with:
client-id: ${{ vars.AZURE_CLIENT_ID }}
tenant-id: ${{ vars.AZURE_TENANT_ID }}
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
No client secret stored in GitHub Secrets. No secret to rotate. No secret to leak.
Principle 2: Pin Dependency Versions and Verify Integrity
For application dependencies: Use exact version pinning in lock files (package-lock.json, poetry.lock, Pipfile.lock). Never use version ranges in production. Commit your lock files.
For GitHub Actions: Pin action versions to specific commit SHAs, not tags. Tags are mutable — a compromised package can update a tag to point to malicious code. A SHA is immutable:
# Vulnerable — tag can be moved by an attacker
- uses: actions/checkout@v4
# Secure — pinned to an immutable commit SHA
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
Use a tool like pin-github-action to automate SHA pinning across your workflows.
Principle 3: Separate Build and Deploy Environments
Your build environment should not have access to your production environment. Build artifacts, test, then promote to production through a controlled deployment gate with separate credentials:
Build pipeline: source → compile → test → publish artifact to registry
Deploy pipeline: pull artifact from registry → security scan → deploy to staging → approve → deploy to production
The build pipeline has no access to production. The deploy pipeline fetches an immutable, verified artifact.
Principle 4: Artifact Signing and Verification
Sign every build artifact before pushing it to a registry. Verify the signature before deploying it.
Cosign (Sigstore) is the modern standard for container image signing:
# Sign the image after build
cosign sign --key cosign.key your-registry/your-image:sha-abc123
# Verify before deploy
cosign verify --key cosign.pub your-registry/your-image:sha-abc123
Azure Container Registry supports content trust (image signing) built in. GitHub Actions + GitHub Packages support artifact attestation using the Sigstore framework.
Principle 5: Secret Scanning and Detection
Secrets committed to source code are a critical and very common vulnerability. Implement:
- Pre-commit hooks:
detect-secrets,gitleaks— scan every commit before it's pushed - GitHub secret scanning: Enabled by default for public repos; enable for private repos. GitHub automatically alerts on common secret patterns.
- Periodic historical scan:
gitleaksscanned against your full git history will find secrets committed in the past
The Supply Chain Security Programme
Implementing supply chain security is not a single project — it's an ongoing programme:
| Practice | Frequency | Tooling |
|---|---|---|
| Dependency vulnerability scan | Every build | Snyk, Trivy, Grype |
| SBOM generation | Every release | Syft, cdxgen |
| SBOM vulnerability tracking | Continuous | Dependency-Track |
| Container image scan | Every build | Trivy, Grype |
| Secret scanning | Every commit | detect-secrets, GitHub scanning |
| Pipeline credential audit | Quarterly | Manual review |
| Dependency update review | Weekly | Dependabot, Renovate |
Prioritise based on exploitability, not just severity
Vulnerability scanners will surface hundreds of findings across your dependency tree. Most are not exploitable in your context. Focus remediation effort on vulnerabilities that are: (1) in code that actually executes, (2) reachable from an untrusted input, and (3) have a known exploit. Tools like Snyk's reachability analysis and Grype's VEX (Vulnerability Exploitability eXchange) support help prioritise.
Supply chain security is a rapidly evolving area that's increasingly required by enterprise customers and regulators. If you'd like to assess your current posture or implement a comprehensive DevSecOps programme, let's talk.