Pavan Rangani

HomeBlogSupply Chain Security: Securing Your CI/CD Pipeline from Build to Deploy

Supply Chain Security: Securing Your CI/CD Pipeline from Build to Deploy

By Pavan Rangani · February 16, 2026 · Security

Supply Chain Security: Securing Your CI/CD Pipeline from Build to Deploy

Supply Chain Security for CI/CD Pipelines: Dependencies, SBOMs, and Artifact Signing

The SolarWinds attack proved that compromising a single build pipeline can infiltrate thousands of organizations simultaneously. Supply chain security in CI/CD is no longer optional — it’s a board-level concern. In 2026, regulatory frameworks like the EU Cyber Resilience Act require software producers to maintain Software Bills of Materials. Therefore, this guide covers the practical tools and techniques to secure your build pipeline from source code to deployed artifact. Crucially, the threat model has shifted: attackers no longer chase your production servers directly. Instead, they target the soft underbelly — the build system that has the credentials to push to every server you own.

To understand why this matters, consider the trust chain. When you deploy, you implicitly trust your dependencies, your build tools, your CI runner, your container registry, and the network paths between them. A single weak link compromises everything downstream. Consequently, modern defense focuses on shrinking that trust surface and making every link independently verifiable rather than assumed safe.

Dependency Scanning: Finding Vulnerabilities Before They Ship

Your application’s dependencies are its largest attack surface. A typical Node.js project pulls in 500-1500 transitive dependencies, and any one of them could contain a vulnerability or malicious code. Moreover, dependency confusion attacks — where an attacker publishes a package matching your internal package name on a public registry — have compromised major companies including Microsoft and Apple.

Integrate dependency scanning into every pull request, not just weekly audits. Tools like Snyk, Dependabot, and Trivy scan your lockfile against vulnerability databases and flag known CVEs with severity scores. Additionally, use lockfile pinning to prevent automatic dependency upgrades that could introduce compromised packages.

# GitHub Actions: dependency scanning on every PR
name: Security Scan
on: [pull_request]

jobs:
  dependency-scan:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write
    steps:
      - uses: actions/checkout@v4

      - name: Run Trivy vulnerability scanner
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'fs'
          scan-ref: '.'
          format: 'sarif'
          output: 'trivy-results.sarif'
          severity: 'CRITICAL,HIGH'

      - name: Upload to GitHub Security tab
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: 'trivy-results.sarif'

      - name: Fail on critical vulnerabilities
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'fs'
          severity: 'CRITICAL'
          exit-code: '1'

For private registries, configure scoped packages to prevent dependency confusion. Set your .npmrc to route @yourcompany/* packages to your private registry while everything else goes to the public npm registry. Consequently, an attacker cannot hijack your internal package names on the public registry.

Supply chain security scanning in CI/CD pipeline
Automated dependency scanning catches known vulnerabilities before they reach production

Beyond Known CVEs: Detecting Malicious and Typosquatted Packages

Scanning for known CVEs catches yesterday’s problems, but the most dangerous attacks are brand new. A freshly published malicious package has no CVE because nobody has discovered it yet. Therefore, you need a second layer of defense that flags suspicious behavior rather than known signatures. This is where install-time analysis tools earn their place.

Several specific patterns deserve automated blocking. Typosquatting — where reqeusts impersonates requests — relies on a single mistyped character slipping through review. Similarly, a legitimate package that suddenly gains a postinstall script or starts reaching out to an unfamiliar network endpoint is a strong signal of account takeover. Tools like Socket and OSV-Scanner analyze package behavior, install scripts, and metadata changes rather than just version numbers.

# Add OSV-Scanner and an allowlist gate to the PR pipeline
      - name: Run OSV-Scanner (catches packages without a CVE yet)
        uses: google/osv-scanner-action@v1
        with:
          scan-args: |-
            --lockfile=package-lock.json
            --recursive

      - name: Enforce a dependency allowlist
        run: |
          # Compare resolved packages against an approved list.
          # New transitive deps require explicit human approval.
          npm ls --all --json > resolved.json
          python3 .ci/check_allowlist.py resolved.json .ci/allowed-packages.txt

A practical policy that many platform teams adopt is “no new transitive dependency merges without a human reviewing it.” When a PR introduces an unfamiliar package three levels deep in the tree, that should surface in review, not slip in silently. As a result, the social engineering path of “compromise a tiny popular library and wait” becomes far harder to exploit.

Software Bill of Materials (SBOM): Know What You Ship

An SBOM is a complete inventory of every component in your software — libraries, frameworks, tools, and their versions. When a new vulnerability like Log4Shell drops, an SBOM lets you answer “are we affected?” in minutes instead of days. Furthermore, customers and regulators increasingly demand SBOMs as proof of supply chain diligence.

Generate SBOMs in standard formats (SPDX or CycloneDX) as part of your build pipeline. Attach them to your container images and release artifacts. For example, the CycloneDX tool generates SBOMs from lockfiles in seconds and outputs machine-readable JSON or XML.

# Generate SBOM from package lockfile
npx @cyclonedx/cyclonedx-npm --output-file sbom.json --output-format json

# Generate SBOM from container image
syft scan myapp:latest -o cyclonedx-json > container-sbom.json

# Verify SBOM against known vulnerabilities
grype sbom:./sbom.json --fail-on critical

# Attach SBOM to container image using cosign
cosign attach sbom --sbom sbom.json myregistry/myapp:v1.2.3

Store SBOMs alongside your release artifacts in a versioned repository. When a new CVE is published, you can search all your SBOMs programmatically to identify every affected deployment. As a result, your incident response time drops from days to minutes.

One nuance the docs emphasize: generate the SBOM from the built image, not just the source lockfile. The two differ more than you might expect. Base images contribute OS-level packages — OpenSSL, glibc, curl — that never appear in your application lockfile yet account for a large share of real-world CVEs. A source-only SBOM that omits the base image gives a false sense of completeness, so a common pattern is to generate both and merge them, or to scan the final image directly with Syft.

Artifact Signing and Verification with Sigstore

Signing your build artifacts cryptographically proves they haven’t been tampered with between your CI pipeline and production. Sigstore’s cosign tool makes this practical by eliminating the key management burden — it uses keyless signing tied to your CI provider’s OIDC identity.

In a GitHub Actions workflow, cosign signs container images using the workflow’s OIDC token. Anyone can verify the signature and confirm the image was built by your specific repository and workflow. However, you must also verify signatures during deployment — signing without verification is like having a lock you never use.

# Sign and verify container images in CI
jobs:
  build-and-sign:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write  # Required for keyless signing
      packages: write
    steps:
      - uses: sigstore/cosign-installer@v3

      - name: Build and push image
        run: |
          docker build -t ghcr.io/myorg/myapp:$GITHUB_SHA .
          docker push ghcr.io/myorg/myapp:$GITHUB_SHA

      - name: Sign image (keyless)
        run: cosign sign ghcr.io/myorg/myapp:$GITHUB_SHA

      # In deployment pipeline: verify before deploying
      - name: Verify signature
        run: |
          cosign verify ghcr.io/myorg/myapp:$GITHUB_SHA \
            --certificate-oidc-issuer https://token.actions.githubusercontent.com \
            --certificate-identity-regexp 'github.com/myorg/myapp'

The detail that trips teams up is the verification side. Signing is easy to bolt on and feels like progress, but a signature nobody checks provides zero security. Enforcement belongs at the deployment boundary, ideally in a Kubernetes admission controller such as Sigstore’s policy-controller or Kyverno. When the cluster refuses to run any image whose signature and certificate identity it cannot verify, you close the gap between “we sign things” and “only our signed things run.”

Artifact signing and verification for secure deployments
Sigstore keyless signing ensures every deployed artifact is traceable to a specific CI build

Hardening GitHub Actions Workflows

GitHub Actions workflows are a prime target because they have access to secrets, deploy credentials, and the ability to publish packages. Pin all action versions to full commit SHAs — not tags, which can be moved. Specifically, replace uses: actions/checkout@v4 with uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11.

Limit permissions using the principle of least privilege. Set the workflow-level permissions to contents: read and only elevate specific job permissions when needed. Additionally, use environment protection rules to require manual approval for production deployments.

Two further hardening steps are worth calling out because they address real, documented attack paths. First, beware the pull_request_target trigger: it runs with write permissions and access to secrets in the context of the base branch, so combining it with checking out untrusted PR code can hand an attacker your secrets. Second, prefer OIDC-based cloud authentication over long-lived static credentials stored as secrets. With OIDC, the runner exchanges a short-lived token for cloud access scoped to that exact workflow, so there is no standing secret for an attacker to exfiltrate. Where static secrets are unavoidable, scope them per-environment and rotate them on a schedule.

Provenance and the SLSA Framework

SLSA (Supply-chain Levels for Software Artifacts) defines a maturity model for supply chain security. At SLSA Level 3, your build process is fully defined, auditable, and resistant to tampering. GitHub now generates SLSA provenance attestations automatically for Actions workflows, proving exactly how an artifact was built.

Enable provenance generation in your workflow and verify it during deployment. This creates an auditable chain from source commit to deployed artifact. For example, Kubernetes admission controllers can reject any image without valid provenance, preventing unauthorized deployments.

It helps to read the levels as a ladder rather than a binary. Level 1 simply means provenance exists and is recorded. Level 2 adds a hosted, authenticated build service so provenance cannot be forged on a developer laptop. Level 3 hardens that build service against tampering and isolates builds from one another. Most teams reach a meaningful security posture at Level 2 and should not let the perfect be the enemy of the good — incremental adoption beats an all-or-nothing rewrite.

SLSA framework for supply chain security levels
SLSA provenance creates an auditable chain from source code to deployed production artifacts

When NOT to Over-Engineer: Trade-offs and Honest Limits

Every control here has a cost, and not every project needs the full stack on day one. Mandatory signature verification at the admission layer can block a critical hotfix at 3 a.m. if the signing step silently failed earlier — so always build a documented break-glass path before you turn enforcement on. Likewise, a strict allowlist that requires human approval for every transitive dependency will frustrate a fast-moving team if the approval queue is slow; staff it, or it becomes shadow-IT bait.

For a small internal tool with no external users, generating SBOMs and chasing SLSA Level 3 is probably premature; dependency scanning and SHA-pinned actions deliver most of the protection at a fraction of the effort. The reverse is also true: a widely distributed library or anything regulated under frameworks like the CRA genuinely needs the full chain, because your blast radius includes every downstream consumer. Match the rigor to the blast radius rather than applying maximum controls everywhere and burning out the team. Finally, remember that none of these tools stop an attacker who compromises a maintainer’s credentials and ships malicious code through your legitimate, fully signed pipeline — defense in depth reduces risk, it does not eliminate it.

Related Reading:

Resources:

In conclusion, supply chain security requires defense in depth — scanning dependencies, generating SBOMs, signing artifacts, and hardening your CI/CD environment. No single tool solves the problem. Start with dependency scanning and lockfile pinning, add SBOM generation and artifact signing, then work toward SLSA Level 3 provenance. The investment pays off when the next Log4Shell hits and you can answer “are we affected?” in five minutes.

← Back to all articles