Integrating Software Supply Chain Security into Your DevSecOps Pipeline

Integrating software supply chain security into a DevSecOps pipeline means treating every input to your software as untrusted until verified. That covers source, dependencies, build systems, and artifacts. You then enforce that verification automatically at four control points: source control, dependency intake, the build, and deployment. In practice, you protect your repositories, pin and scan dependencies, generate a software bill of materials (SBOM) and signed provenance for every build, and refuse to deploy anything that cannot prove where it came from. The rest of this post walks through each control point with working configuration, a phased rollout plan, and how the work maps to established frameworks.
Why Software Supply Chain Security Belongs Inside the Pipeline
Most teams already scan their own code. The harder problem is everything they did not write: open source packages, base images, CI actions, build tooling, and the pipeline itself. Supply chain attacks target those inputs because one compromise propagates to every downstream consumer.
The public record shows several distinct attack paths:
- Build system compromise. The 2020 SolarWinds incident inserted malicious code during the build of the Orion product. The source repository looked clean, and the shipped artifact did not.
- CI tooling compromise. In 2021, attackers modified Codecov's Bash Uploader script, which many CI pipelines downloaded and executed, to exfiltrate environment variables and credentials.
- Maintainer-level compromise. The 2024 xz Utils backdoor (CVE-2024-3094) was introduced by a contributor who spent years gaining maintainer trust. It was caught shortly before reaching wide distribution.
- Dependency hijacking. The 2018
event-streamnpm incident added a malicious dependency after maintainership was handed to a new party.
The lesson we draw from these cases: perimeter scanning at release time is too late and too narrow. Controls have to live in the pipeline, run on every change, and produce evidence you can check mechanically. That is what secure-by-design means here. Security is a property of the delivery system, not a gate bolted onto the end.
The Four Control Points
We find it useful to model the pipeline as a chain of custody. Each link needs a control that prevents tampering and an artifact that proves the control ran.
1. Source: Protect What Goes In
Your repository is the root of trust. If an attacker can push to main, nothing downstream matters.
- Require branch protection on default and release branches: mandatory pull request review, required status checks, and no force pushes.
- Enforce signed commits where your platform supports it, so authorship is verifiable.
- Require MFA and short-lived credentials for everyone with write access. Audit and remove stale tokens and deploy keys.
- Use CODEOWNERS for sensitive paths such as pipeline definitions, Dockerfiles, and infrastructure code. A change to
.github/workflows/deserves the same scrutiny as a change to authentication logic.
The OpenSSF Scorecard project automates checks for many of these practices and can run as a scheduled job against your own repositories.
2. Dependencies: Control What You Pull
Dependency intake is where most organizations carry the most unexamined risk.
Pin everything, including CI actions. Floating tags like @v4 or latest let upstream changes flow into your build silently. Pin GitHub Actions to a full commit SHA and let a bot such as Dependabot or Renovate propose updates as reviewable pull requests.
# Pinned to an immutable commit; the comment records the human-readable version
- uses: actions/checkout@<full-40-char-commit-sha> # v4.x.x
Install from lockfiles with integrity checks. Use the commands that fail when the lockfile and manifest disagree:
# Node.js: fails if package-lock.json is out of sync
npm ci
# Python: rejects any package whose hash is not listed
pip install --require-hashes -r requirements.txt
Scan continuously, not just at merge. New vulnerabilities are disclosed against code you shipped months ago. Run software composition analysis on every pull request and on a schedule against your deployed SBOMs.
Route public packages through an internal proxy or registry. This gives you a single choke point for policy, caching, and blocking known-malicious packages. It also helps defend against dependency confusion, where a public package impersonates an internal name.
3. Build: Produce Evidence, Not Just Artifacts
This is the control point most pipelines lack entirely. A secure build does two things: it runs in an isolated, ephemeral environment, and it emits verifiable metadata describing what was built, from what, and by whom.
The SLSA framework (Supply-chain Levels for Software Artifacts) defines this as provenance. Combined with an SBOM and a cryptographic signature, provenance lets any consumer verify an artifact independently.
Here is a condensed GitHub Actions job that builds a container image, generates a CycloneDX SBOM with Syft, fails on high-severity findings with Grype, and signs the image and SBOM using Sigstore's keyless signing:
name: build-and-attest
on:
push:
branches: [main]
permissions:
contents: read
packages: write
id-token: write # required for keyless signing via OIDC
jobs:
build:
runs-on: ubuntu-latest
env:
IMAGE: ghcr.io/example-org/payments-api
steps:
- uses: actions/checkout@<pinned-sha> # v4
- name: Build and push
id: build
run: |
docker build -t "$IMAGE:${GITHUB_SHA}" .
docker push "$IMAGE:${GITHUB_SHA}"
DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' "$IMAGE:${GITHUB_SHA}")
echo "ref=$DIGEST" >> "$GITHUB_OUTPUT"
- name: Generate SBOM
run: syft "${{ steps.build.outputs.ref }}" -o cyclonedx-json=sbom.cdx.json
- name: Fail on high or critical vulnerabilities
run: grype sbom:./sbom.cdx.json --fail-on high
- name: Sign image and attach SBOM attestation
run: |
cosign sign --yes "${{ steps.build.outputs.ref }}"
cosign attest --yes \
--predicate sbom.cdx.json \
--type cyclonedx \
"${{ steps.build.outputs.ref }}"
A few details matter here:
- Sign by digest, never by tag. Tags are mutable, and digests are not.
- Keyless signing binds the signature to the workflow's OIDC identity and records it in the public Rekor transparency log. This removes the burden of managing long-lived signing keys.
- For SLSA build provenance, the slsa-github-generator project and GitHub's artifact attestation actions generate provenance from a trusted builder that the calling workflow cannot tamper with.
4. Deploy: Verify Before You Run
Signatures you never check are decoration. The final control point is an admission policy that rejects unsigned or unverifiable artifacts.
For a manual or scripted check:
cosign verify \
--certificate-identity-regexp '^https://github.com/example-org/.+' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
ghcr.io/example-org/payments-api@sha256:<digest>
In Kubernetes, enforce it at admission. A Kyverno policy is one option:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-signed-images
spec:
validationFailureAction: Enforce
webhookTimeoutSeconds: 30
rules:
- name: verify-signature
match:
any:
- resources:
kinds: [Pod]
verifyImages:
- imageReferences:
- "ghcr.io/example-org/*"
attestors:
- entries:
- keyless:
subject: "https://github.com/example-org/*"
issuer: "https://token.actions.githubusercontent.com"
rekor:
url: https://rekor.sigstore.dev
Sigstore's policy-controller and OPA Gatekeeper with external data providers are alternatives. Pick the one that fits your existing policy tooling.
Rolling It Out Without Stalling Delivery
Turning everything on at once usually produces a wall of failing builds and a team that disables the checks. We recommend a phased approach:
- Inventory and observe. Generate SBOMs for everything you ship and run scanners in report-only mode. You cannot prioritize what you cannot see.
- Lock down source and CI. Branch protection, pinned actions, least-privilege workflow
permissions, and removal of long-lived secrets in favor of OIDC federation to your cloud provider. - Sign and attest. Add signing and SBOM attestation to builds. Verify in staging only.
- Enforce progressively. Move admission policies to
Enforceone namespace or service at a time. Begin with failing on critical vulnerabilities and tighten thresholds as backlog shrinks. - Operationalize. Define an exception process with expiry dates, owners, and audit trails. Exceptions without expiry become permanent.
Measure your own baseline before and after each phase: percentage of deployed artifacts with verified signatures, time to remediate critical findings, and count of unpinned dependencies. Those numbers tell you whether the program is working.
Mapping the Work to Established Frameworks
Grounding the program in public frameworks helps with audit conversations and keeps priorities defensible:
- NIST SP 800-218, Secure Software Development Framework (SSDF): practices such as protecting software from tampering (PS) and producing well-secured software (PW) map directly to the controls above.
- SLSA: provides graduated build-integrity levels, useful for setting incremental targets.
- CISA Secure by Design: frames security as a manufacturer responsibility, which aligns with building controls into the pipeline rather than relying on customers to compensate.
- SBOM formats: CycloneDX and SPDX are the two widely adopted standards. Choose one and use it consistently.
Requirements vary by sector. Regulated environments such as financial services, healthcare, and the public sector often carry additional attestation obligations. Our overview of the industries we work with covers how those contexts shape priorities. For teams that want help designing or hardening this kind of pipeline, our engineering and security capabilities describe how we approach it.
FAQ
What is the difference between an SBOM and provenance?
An SBOM lists what is inside an artifact: its components and versions. Provenance describes how the artifact was produced: the source revision, build platform, and build steps. You need both. An SBOM helps you respond to a newly disclosed vulnerability, and provenance helps you detect a tampered build.
Do we need to reach SLSA Level 3 immediately?
No. SLSA is designed for incremental adoption. Generating provenance at all is a meaningful first step. Moving to hardened, isolated builders that the build definition cannot influence comes later, once signing and verification are working reliably.
Is pinning dependencies to exact versions enough?
Pinning prevents silent upstream changes, but it does not tell you whether the pinned version is safe. Combine pinning with integrity hashes, continuous vulnerability scanning, and automated update pull requests so pinned versions do not quietly go stale.
How does keyless signing work without managing keys?
Sigstore issues a short-lived certificate tied to an OIDC identity, such as a specific CI workflow. The signature and certificate are recorded in a public transparency log. Verifiers check that the signer's identity and issuer match an expected policy, so there is no long-lived private key to store, rotate, or leak.
Where should we start if our pipeline has none of these controls?
Start with visibility and source protection. Generate SBOMs for what you ship, enable branch protection, pin CI actions, and restrict workflow permissions. Those steps are low-friction and close several of the attack paths described above before you take on signing and admission enforcement.


