SLSA Level 2
SLSA Level 2 requires builds to run on a hosted build service (GitHub Actions, GitLab CI, Google Cloud Build) with tamper-evident build logs: achievable for most organizations; Level 3 adds hardened build environments with isolated execution
SRI hashes
Subresource Integrity (SRI) hashes in lock files (package-lock.json, Pipfile.lock, go.sum) pin each dependency to a specific content hash: any modification to a dependency package changes the hash and breaks the build, detecting tampered packages
OIDC tokens
Workload identity federation using OIDC tokens eliminates long-lived CI/CD secrets: GitHub Actions, GitLab CI, and Google Cloud Build can authenticate to cloud providers using short-lived OIDC tokens tied to the specific workflow run, preventing secret theft from CI logs
Sigstore
Sigstore's cosign tool signs container images and artifacts with ephemeral keys backed by a transparency log: enables verification that a specific container image was produced by a trusted pipeline before deployment

SponsoredRetool

Retool's new app builder is where AI-generated code ships safely

Building apps with AI is easy. Getting them to production safely is another story.

Start building for free today

The SolarWinds attack injected malicious code into a build pipeline, distributing compromised software to 18,000 customers who trusted the vendor's update signature. XZ Utils inserted a backdoor via a long-term social engineering attack on a maintainer. 3CX distributed a trojanized installer signed with a valid certificate after the build system was compromised. These attacks share a pattern: they target the trust relationship between software producers and consumers. Detection requires monitoring the build pipeline itself: not just the output artifacts: for integrity violations.

Pin and Verify All Dependencies

Unpinned dependencies are the primary supply chain attack vector. Dependency confusion attacks register malicious packages with names matching internal private packages: npm, PyPI, and RubyGems install the highest-version package when the private registry isn't configured correctly. Fix: (1) Pin all dependencies to exact versions with content hashes in lock files. For npm: package-lock.json with integrity fields containing SHA-512 hashes; run npm ci (not npm install) in CI to enforce lock file. For Python: pip install --require-hashes -r requirements.txt with --hash=sha256:... annotations per package. For Go: go.sum file pins module hashes: never delete or modify go.sum manually. (2) Configure private registry scoping explicitly: in .npmrc, set @myorg:registry=https://private-registry.internal AND registry=https://registry.npmjs.org: without scoping, npm may pull private package names from the public registry if they appear there at a higher version. (3) Run dependency integrity checks in CI: npm audit signatures validates that packages are signed by registry keys and that signatures are valid.

Audit CI/CD Secret Access

CI/CD pipeline secrets (API keys, signing certificates, deploy credentials) are high-value targets. Audit controls: (1) Enumerate all secrets stored in your CI/CD system (GitHub Actions Secrets, GitLab CI Variables, Jenkins Credentials Store): any secret that hasn't been rotated in 90 days or is no longer used is attack surface. (2) Review which workflows and jobs have access to which secrets: in GitHub Actions, secrets are available to all jobs in a workflow by default; restrict with environment protection rules that require manual approval before secrets are exposed to a job. (3) Replace long-lived secrets with OIDC workload identity: GitHub Actions can authenticate to AWS, GCP, Azure, and Vault using short-lived OIDC tokens tied to the workflow run. Configure in GitHub Actions:

permissions:
  id-token: write  # Required for OIDC
  contents: read
jobs:
  deploy:
    steps:
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789:role/github-actions-role
          aws-region: us-east-1
          # No access_key_id or secret_access_key needed

This eliminates AWS credentials from CI secrets entirely: the OIDC token is valid for one workflow run and cannot be extracted for reuse.

Free daily briefing

Briefings like this, every morning before 9am.

Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.

Implement SLSA Provenance Attestation

SLSA (Supply Chain Levels for Software Artifacts) defines build integrity requirements in four levels. Level 1 requires a provenance document (who built it, from what source, using what build system). Level 2 requires the build service to generate tamper-evident provenance signed by the build service itself. Level 3 requires a hardened build environment that cannot be tampered with by the build process. Achieve SLSA Level 2 with GitHub Actions using the SLSA GitHub Generator:

jobs:
  build:
    outputs:
      hashes: ${{ steps.hash.outputs.hashes }}
    steps:
      - name: Build
        run: make build
      - name: Hash artifacts
        id: hash
        run: |
          sha256sum ./dist/* > hashes.txt
          echo "hashes=$(base64 -w0 hashes.txt)" >> $GITHUB_OUTPUT

  provenance:
    needs: build
    permissions:
      actions: read
      id-token: write
      contents: write
    uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.10.0
    with:
      base64-subjects: ${{ needs.build.outputs.hashes }}

This generates a SLSA provenance attestation signed by GitHub's Sigstore instance. Verify before deployment: slsa-verifier verify-artifact ./dist/binary --provenance-path provenance.intoto.jsonl --source-uri github.com/myorg/myrepo.

Sign and Verify Build Artifacts

Code signing establishes a verifiable chain between the artifact and the build pipeline that produced it. For container images, use Sigstore's cosign:

# Sign during CI (after build)
cosign sign --yes myregistry.io/myapp:v1.2.3

# Verify during deployment (in CD pipeline or admission controller)
cosign verify \
  --certificate-identity-regexp 'https://github.com/myorg/myrepo/.github/workflows/.*' \
  --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
  myregistry.io/myapp:v1.2.3

The --certificate-identity-regexp ensures the image was signed by a workflow in your specific repository: not just any GitHub Actions workflow. Enforce in Kubernetes using Sigstore's policy-controller admission webhook: any pod using an unsigned or unverified image is rejected. For binary artifacts (installers, CLIs), use the standard Sigstore bundle format: cosign sign-blob ./binary --bundle binary.sigstore. Distribute the .sigstore bundle alongside the binary and require users to verify before installation in your documentation.

Detect Build Environment Modifications

Build system compromise (like SolarWinds) modifies the build environment after the source code is committed but before compilation. Detection approaches: (1) Reproducible builds: configure your build to produce byte-identical output from the same source inputs (eliminate timestamps and environment variables from build artifacts). Build twice in independent environments and compare hashes: any difference indicates environmental tampering. (2) Build script change monitoring: alert on any modifications to Makefile, Dockerfile, GitHub Actions workflows, Jenkinsfile, or build tool configuration files in pull requests from contributors who haven't previously modified build scripts. (3) Artifact hash comparison: after each build, compare the output artifact hash against the hash produced by an independent rebuild in a clean environment. Cobalt Strike and other C2 tooling has been distributed via build environment compromise that injects code during compilation: the source code shows as unmodified in git, but the compiled binary contains the payload. Reproducible builds catch this because the independently-rebuilt artifact has a different hash.

Monitor for Dependency Confusion and Typosquatting

Proactive monitoring for supply chain threats targeting your dependencies. (1) Claim your package names on public registries: if you use internal packages named @mycompany/auth-lib, register mycompany-auth-lib on npm and mycompany_auth_lib on PyPI as empty placeholder packages with a note directing users to your private registry: this prevents confusion attacks. (2) Monitor for typosquatted dependencies: tools like socket.dev and SocketSecurity scan your dependency tree for packages with names similar to legitimate packages (e.g., requesst instead of requests). Integrate socket.dev as a GitHub App for automated PR scanning. (3) Subscribe to security advisories: GitHub Dependabot and npm audit provide CVE alerts for dependencies; also subscribe to package security mailing lists for languages you use. (4) Monitor new releases of your critical dependencies: a dependency that hasn't released a new version in 2 years suddenly releasing a new version with no release notes or GitHub activity is a potential account takeover scenario: validate the release is from the legitimate maintainer before updating.

The bottom line

Supply chain compromise detection requires three layers: dependency integrity (pin to content hashes in lock files, use npm ci and pip install --require-hashes), build pipeline hardening (replace long-lived secrets with OIDC tokens, implement SLSA provenance attestation), and artifact verification (sign with Sigstore cosign, enforce signature verification in deployment). Monitor for dependency confusion by claiming your package names on public registries and using socket.dev for PR scanning. Reproducible builds provide the strongest build environment compromise detection.

Frequently asked questions

How do I detect dependency confusion attacks in my build pipeline?

Dependency confusion attacks are prevented by explicitly scoping private package namespaces in your package manager configuration (e.g., .npmrc @scope:registry= pointing to your private registry) and pinning all dependencies to exact content hashes in lock files. For detection, run 'npm audit signatures' to verify package signatures, use socket.dev to scan for packages with characteristics of confusion attacks (newly published, high version numbers, no prior history), and monitor your CI/CD logs for unexpected registry sources in package download logs. Claim your internal package names on public registries as empty placeholder packages to prevent attackers from registering them.

What is SLSA and why does it matter for supply chain security?

SLSA (Supply Chain Levels for Software Artifacts) is a framework that defines build integrity requirements in four levels. SLSA Level 2, achievable with GitHub Actions, requires a signed provenance document stating what source code was built, by which build service, and when: making it possible to verify that a binary artifact corresponds to specific reviewed source code. This directly addresses SolarWinds-style attacks where build environments are modified after code review: if the artifact's SLSA provenance is verified, you can confirm it was produced by an unmodified build process from a specific git commit.

How do I audit third-party code in my software build pipeline for supply chain risks?

Third-party code audit for supply chain security: (1) Generate a Software Bill of Materials (SBOM) for every build using Syft (open-source) or your build system's native SBOM generation — SBOMs enumerate every dependency, version, and license. (2) Scan the SBOM against vulnerability databases with Grype or Trivy to identify known CVEs in dependencies. (3) Use socket.dev or Phylum to detect behavioral risk in new npm/PyPI packages: newly published packages with install scripts, packages that suddenly gain dependencies, and packages with high-entropy strings in their code (indicators of obfuscated malicious logic). (4) Pin all dependencies to exact content hashes (lockfiles for npm, pip, and cargo), not semantic version ranges — version ranges allow automatic inclusion of new malicious versions.

How do I secure secrets in CI/CD pipelines to prevent build pipeline compromise?

CI/CD secrets security: (1) Never store secrets in code repositories — use the CI/CD platform's secrets store (GitHub Actions Secrets, GitLab CI Variables, Jenkins Credentials). (2) Use OpenID Connect (OIDC) for cloud authentication: configure GitHub Actions, GitLab CI, and similar platforms to exchange short-lived OIDC tokens for cloud provider credentials (AWS, Azure, GCP) instead of storing long-lived API keys — no secrets to steal. (3) Audit pipeline secrets regularly: remove unused secrets, rotate active secrets quarterly, and review which pipelines have access to production credentials. (4) Use separate secrets per environment: the CI/CD pipeline for dev should use dev credentials and cannot accidentally (or maliciously) deploy to production using shared credentials. (5) Log all secret access: most CI/CD platforms log when secrets are accessed — alert on access from unexpected pipelines or branches.

What was the SolarWinds supply chain attack and what lessons apply to enterprise build pipelines?

The SolarWinds Orion attack (discovered December 2020) compromised SolarWinds' build system to inject malicious code (SUNBURST backdoor) into legitimate Orion software updates signed with SolarWinds' valid code signing certificate. Approximately 18,000 organizations installed the trojanized update. Key lessons for enterprise build security: (1) Build environment isolation — SolarWinds' build system was accessible from the corporate network; production build systems should be network-isolated. (2) Build process integrity — the build server itself was modified; implement binary reproducibility (the same source produces the same binary) to detect build server tampering. (3) Update verification — recipients had no mechanism to verify Orion was unmodified beyond the code signature; SLSA provenance would have provided source-to-binary traceability. (4) Least privilege build credentials — the build system had excessive access to production infrastructure.

How do I implement artifact signing and verification in a GitHub Actions pipeline without storing long-lived signing keys?

Use Sigstore's keyless signing via GitHub Actions OIDC tokens to avoid storing any long-lived signing keys in secrets. In your workflow, add a build step that runs 'cosign sign --yes' immediately after the Docker build or binary compilation step -- the '--yes' flag uses the ambient OIDC identity from the GitHub Actions runner rather than prompting for a key. The signature is recorded in the Sigstore transparency log (Rekor) and bound to the workflow's OIDC token, which encodes the repository, branch, and commit SHA. Verification in your deployment pipeline uses 'cosign verify' with '--certificate-identity-regexp' set to your repository's workflow URL pattern and '--certificate-oidc-issuer' set to 'https://token.actions.githubusercontent.com': this ensures only artifacts signed by a workflow in your specific repository are accepted. Enforce signature verification in Kubernetes using the Sigstore policy-controller admission webhook, which rejects any pod referencing an unsigned or unverified image before it is scheduled. Test the full sign-verify cycle in a staging environment before enabling enforcement in production -- a misconfigured verification policy that rejects all images will prevent any deployments until corrected.

Sources & references

  1. SLSA Supply Chain Levels for Software Artifacts
  2. CISA Defending Against Software Supply Chain Attacks
  3. NIST SSDF: Secure Software Development Framework

Free resources

25
Free download

Critical CVE Reference Card 2025–2026

25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.

No spam. Unsubscribe anytime.

Free download

Ransomware Incident Response Playbook

Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.

No spam. Unsubscribe anytime.

Free newsletter

Get threat intel before your inbox does.

50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.

Unsubscribe anytime. We never sell your data.

Eric Bang
Author

Founder & Cybersecurity Evangelist, Decryption Digest

Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.

Black Hat Giveaway

Win a $2,495 Black Hat pass.

Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.

Joins Decryption Digest daily briefing. Unsubscribe anytime.

Giveaway: Black Hat USA 2026 Full-Access Pass ($2,495 value)

Details →
Daily Briefing

Subscribe to enter the giveaway

Every subscriber is automatically entered. You also get daily threat intel every morning: zero-days, ransomware, and nation-state campaigns. Free. No spam.

Already subscribed? You're already entered.

Giveaway

Win a $2,495 Black Hat USA 2026 pass.