Software Supply Chain Security: SBOMs, Sigstore & Dependency Scanning
Anatomy of supply chain attacks (xz-utils, SolarWinds, event-stream), SBOM generation with Syft and Trivy, Sigstore keyless signing, dependency scanning tools compared, and the SLSA framework.
Infrastructure engineer with 10+ years building production systems on AWS, GCP,…

A Decade of Supply Chain Attacks, in Order
Software supply chain attacks did not start with xz-utils. They have been escalating, year after year, through every link in the modern build pipeline. The timeline below is the pattern defenders kept missing -- each incident targeted a different layer, and each one succeeded because the industry was still focused on the layer below it.
| Year | Incident | Link attacked | What it proved |
|---|---|---|---|
| 2015 | XcodeGhost | Build toolchain (pirated Xcode) | Developer tools themselves are supply chain targets. |
| 2018 | event-stream / flatmap-stream | npm dependency transfer | Trust in package maintainers is transferable and narrowly targetable. |
| 2020 | SolarWinds SUNBURST | Build system / code-signing | A signed binary proves the signing key worked, not that the code is safe. |
| 2021 | Codecov Bash Uploader | CI/CD tooling | Any script curl-piped to bash in CI has your secrets. |
| 2021 | Log4Shell (CVE-2021-44228) | Ubiquitous transitive dependency | You cannot patch what you did not know you were shipping -- SBOMs matter. |
| 2023 | 3CX cascading compromise | Vendor-of-a-vendor | Supply chains are transitive; one compromise fans out globally. |
| 2024 | xz-utils CVE-2024-3094 | Long-game social engineering of an OSS maintainer | Automated scanners cannot see a two-year trust exploit. |
| 2024-2025 | Waves of PyPI and npm typosquat + post-install payloads | Public registries | Behavioral analysis, not just CVE lookups, is now table stakes. |
Read those rows top to bottom and the defensive response looks obvious in hindsight: SBOMs became mandatory after Log4Shell, Sigstore reached GA in 2022 as a direct response to SolarWinds, SLSA 1.0 shipped in April 2023 after Codecov, and the xz-utils disclosure in March 2024 pushed the industry toward behavioral package scanning (Socket, Phylum, Snyk Advisor) that can catch payloads before CVEs are even filed. The tools caught up. The problem is that most engineering organizations have not. A 2025 Sonatype report measured the median Fortune 500 company at SLSA Level 1 or below across its production services.
This guide is the practical catch-up plan. I cover SBOM generation with Syft and Trivy in both CycloneDX and SPDX formats, Sigstore/Cosign keyless image signing and Kyverno enforcement, the Dependabot/Renovate/Snyk decision, the four SLSA levels and how to reach Level 2 in a single afternoon, and the CI/CD hardening steps that would have stopped Codecov. The goal is the same goal the industry has been chasing since 2015 -- make every link in your supply chain verifiable, auditable, and tamper-resistant -- but 2026 is the first year you can actually get there with open-source tools and a weekend of engineering time.
Anatomy of Supply Chain Attacks
Supply chain attacks succeed because they exploit implicit trust. You trust your dependencies, your build system, your CI tools, and your package registries. Attackers target whichever link has the weakest verification. Four real-world attacks illustrate the main vectors:
xz-utils: Social Engineering a Maintainer
The attacker ("Jia Tan") contributed to xz-utils for two years, earning commit access through legitimate work. Pressure was applied to the original maintainer to hand over control. The backdoor was hidden in test fixture files and activated through a multi-stage obfuscation chain in the build system. Detection was accidental -- a performance regression spotted during unrelated benchmarking.
Lesson: Code review alone is not sufficient when an attacker has commit access and can modify build scripts, test data, and CI configuration over time.
SolarWinds: Build System Compromise
Attackers compromised SolarWinds' build infrastructure and injected malicious code into the Orion software update. The trojanized update was signed with SolarWinds' legitimate certificate and distributed to 18,000+ organizations, including US government agencies. The malware (SUNBURST) communicated via DNS to blend with normal traffic.
Lesson: If your build system is compromised, code signing is meaningless -- you are signing the attacker's code with your own key.
event-stream: Dependency Hijacking
A new maintainer took over the popular event-stream npm package from a burned-out original author. They added a dependency (flatmap-stream) containing encrypted malicious code that targeted the Copay Bitcoin wallet. The attack was narrowly scoped to avoid detection -- it only activated when imported by a specific application.
Lesson: Maintainer transitions in open-source projects are a critical vulnerability. Automated scanning may miss targeted payloads that only activate in specific contexts.
Codecov: CI Tool Manipulation
Attackers modified the Codecov Bash Uploader script to exfiltrate environment variables from CI pipelines. Since CI environments often contain secrets (API keys, tokens, credentials), this gave attackers access to the internal systems of Codecov's customers, including HashiCorp, Twitch, and others.
Lesson: Third-party scripts executed in CI have access to your secrets. Pin CI tool versions and verify checksums before execution.
SBOMs: Know What You Ship
A Software Bill of Materials (SBOM) is a machine-readable inventory of every component in your software -- direct dependencies, transitive dependencies, their versions, licenses, and suppliers. Think of it as a nutrition label for software. Without one, you cannot answer the basic question: "Are we affected by this new CVE?"
SPDX vs CycloneDX
| Feature | SPDX | CycloneDX |
|---|---|---|
| Maintained by | Linux Foundation | OWASP |
| ISO Standard | ISO/IEC 5962:2021 | ECMA-424 |
| Primary focus | License compliance + security | Security + risk analysis |
| Formats | JSON, RDF, YAML, tag-value | JSON, XML, Protobuf |
| VEX support | Via external documents | Inline VEX |
| Adoption | US government (EO 14028) | Broad industry |
Both formats are viable. CycloneDX is more security-focused and easier to generate. SPDX has broader government mandate support. Many organizations generate both.
Generating SBOMs with Syft and Trivy
# Generate SBOM with Syft (CycloneDX JSON)
syft packages alpine:3.19 -o cyclonedx-json > sbom.cdx.json
# Generate SBOM with Syft (SPDX JSON)
syft packages alpine:3.19 -o spdx-json > sbom.spdx.json
# Generate SBOM with Trivy (CycloneDX)
trivy image --format cyclonedx --output sbom.cdx.json alpine:3.19
# Scan an existing SBOM for vulnerabilities
grype sbom:sbom.cdx.json
# GitHub Actions: Generate SBOM on every build
- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
image: my-app:${{ github.sha }}
format: cyclonedx-json
output-file: sbom.cdx.json
artifact-name: sbom
- name: Scan SBOM for vulnerabilities
run: grype sbom:sbom.cdx.json --fail-on critical
Pro tip: Generate your SBOM from the final container image, not from source code alone. Source-level SBOMs miss OS packages, runtime dependencies, and anything added during the Docker build. Image-level SBOMs capture everything that actually ships.
Sigstore: Signing Without the Key Management Nightmare
Traditional code signing requires you to generate, store, rotate, and protect long-lived signing keys. Lose the key and you need to revoke everything. Have it stolen and attackers sign malware with your identity. Sigstore eliminates this problem with keyless signing backed by transparency logs.
The Sigstore Stack
- Cosign -- Signs and verifies container images and other OCI artifacts. Supports both key-based and keyless (identity-based) signing.
- Fulcio -- A free certificate authority that issues short-lived (10-minute) signing certificates tied to an OIDC identity (GitHub, Google, Microsoft). No long-lived keys to manage.
- Rekor -- An immutable transparency log that records every signing event. Anyone can verify that a signature was created at a specific time by a specific identity. Think Certificate Transparency, but for software artifacts.
Signing Container Images with Cosign
# Keyless signing (uses OIDC identity -- opens browser for auth)
cosign sign --yes ghcr.io/myorg/my-app:v1.2.3
# Verify a signed image
cosign verify ghcr.io/myorg/my-app:v1.2.3 \
--certificate-identity=deploy@myorg.iam.gserviceaccount.com \
--certificate-oidc-issuer=https://accounts.google.com
# Key-based signing (traditional approach)
cosign generate-key-pair
cosign sign --key cosign.key ghcr.io/myorg/my-app:v1.2.3
cosign verify --key cosign.pub ghcr.io/myorg/my-app:v1.2.3
# GitHub Actions: Sign image in CI with keyless Cosign
- name: Sign container image
uses: sigstore/cosign-installer@v3
- run: cosign sign --yes ghcr.io/myorg/my-app:${{ github.sha }}
env:
COSIGN_EXPERIMENTAL: "true"
Enforcing Signatures in Kubernetes
Signing images is half the story. You also need to verify signatures at admission time so unsigned or tampered images never run in your cluster.
# Kyverno policy: only allow signed images
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image-signatures
spec:
validationFailureAction: Enforce
rules:
- name: check-cosign-signature
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "ghcr.io/myorg/*"
attestors:
- entries:
- keyless:
issuer: "https://token.actions.githubusercontent.com"
subject: "https://github.com/myorg/*"
Dependency Scanning: Dependabot vs Renovate vs Snyk
Dependency scanning tools monitor your dependency tree for known vulnerabilities and outdated packages. They differ significantly in configuration flexibility, supported ecosystems, and how they handle updates.
| Feature | Dependabot | Renovate | Snyk |
|---|---|---|---|
| Cost | Free (GitHub-native) | Free (OSS) / Mend.io hosted | Free tier / $25+/dev/mo |
| PR strategy | One PR per dependency | Grouped PRs, customizable | One PR per vuln fix |
| Auto-merge | Via GitHub config | Built-in, highly configurable | Limited |
| Monorepo support | Basic | Excellent | Good |
| Update scheduling | Daily/weekly/monthly | Cron-based, per-package | Continuous monitoring |
| Lockfile handling | Good | Excellent | Good |
| Vulnerability DB | GitHub Advisory DB | Relies on registry advisories | Snyk Vulnerability DB (proprietary) |
| License scanning | No | No | Yes |
| Container scanning | No | Yes (Docker digests) | Yes |
// renovate.json -- practical config for a monorepo
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:recommended"],
"packageRules": [
{
"matchUpdateTypes": ["minor", "patch"],
"matchCurrentVersion": "!/^0/",
"automerge": true
},
{
"matchPackagePatterns": ["eslint", "prettier", "typescript"],
"groupName": "dev tooling",
"schedule": ["before 6am on Monday"]
}
],
"lockFileMaintenance": {
"enabled": true,
"schedule": ["before 4am on Monday"]
}
}
Pro tip: Use Renovate if you want control. It supports grouping related updates into a single PR, auto-merging patches for stable dependencies, and per-package schedules. Dependabot's one-PR-per-dependency approach creates noise in active projects. Snyk is worth the cost if you need license compliance scanning or a proprietary vulnerability database with faster CVE coverage.
SLSA Framework and Reproducible Builds
SLSA (Supply-chain Levels for Software Artifacts, pronounced "salsa") is a framework from Google that defines four levels of supply chain integrity. Each level adds verifiable guarantees about how your software was built.
| SLSA Level | Requirement | What It Proves |
|---|---|---|
| Level 1 | Build process documented | Provenance exists |
| Level 2 | Signed provenance, hosted build service | Tamper-resistant provenance |
| Level 3 | Hardened build platform, isolated builds | Provenance is accurate |
| Level 4 | Two-party review, hermetic/reproducible builds | Dependencies are complete and verified |
# GitHub Actions: SLSA Level 3 provenance generation
- name: Generate SLSA provenance
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
with:
image: ghcr.io/myorg/my-app
digest: ${{ steps.build.outputs.digest }}
Reproducible builds take this further: given the same source code and build environment, you get bit-for-bit identical output. This lets anyone verify that the binary you distribute was built from the source you claim. Languages like Go and Rust have good reproducibility support. Node.js and Python require more effort (pinned dependencies, fixed timestamps, deterministic bundling).
Locking Down Your CI/CD Pipeline
Your CI/CD pipeline has access to source code, secrets, signing keys, and deployment credentials. It is the highest-value target in your supply chain. Hardening it is non-negotiable.
- Pin action versions by SHA -- Use
actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29instead of@v4. Tags can be moved; commit SHAs cannot. - Restrict GITHUB_TOKEN permissions -- Set
permissions: read-allat the workflow level and grant write access only where needed. - Use OpenID Connect (OIDC) -- Authenticate to cloud providers with short-lived tokens instead of storing long-lived credentials as secrets.
- Enable branch protection -- Require PR reviews and status checks before merging to main.
- Audit third-party actions -- Treat GitHub Actions like dependencies. Review their source and pin to specific commits.
- Isolate sensitive workflows -- Separate build, sign, and deploy steps into different jobs with minimal permissions each.
# Hardened GitHub Actions workflow
name: Build and Deploy
on:
push:
branches: [main]
permissions: read-all # Default restrictive permissions
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write # For OIDC and Sigstore
steps:
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.2.2
- uses: docker/build-push-action@14487ce63c7a62a3fd1178128fff2c91331a2a2e # v6.12.0
with:
push: true
tags: ghcr.io/myorg/my-app:${{ github.sha }}
- uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a # v3.8.0
- run: cosign sign --yes ghcr.io/myorg/my-app:${{ github.sha }}
Frequently Asked Questions
What is an SBOM and why do I need one?
An SBOM (Software Bill of Materials) is a machine-readable inventory of every component in your software, including transitive dependencies, versions, and licenses. You need one to answer "are we affected?" when a new CVE drops. Without an SBOM, you are guessing. US Executive Order 14028 requires SBOMs for software sold to the federal government, and many enterprises now require them from vendors.
How does keyless signing work with Sigstore?
When you sign with Cosign in keyless mode, Fulcio verifies your identity through an OIDC provider (GitHub, Google, etc.) and issues a short-lived (10-minute) X.509 certificate. The signing event is recorded in Rekor, an immutable transparency log. Verifiers check the Rekor log to confirm that a valid certificate existed at signing time. No long-lived keys are generated or stored.
Should I use Dependabot or Renovate?
Use Dependabot if you want zero-config dependency updates on GitHub and your project is small. Use Renovate if you need grouped PRs, auto-merge for patches, monorepo support, or fine-grained scheduling. Renovate has a steeper learning curve but produces far less PR noise in active projects. For most teams with more than a handful of dependencies, Renovate is the better choice.
What SLSA level should I target?
Start with SLSA Level 2 -- signed provenance from a hosted build service like GitHub Actions. This is achievable in a day using the SLSA GitHub Generator. Level 3 (hardened builds) is appropriate for production services and container images. Level 4 (reproducible builds) is aspirational for most teams but critical for security-sensitive software like cryptographic libraries or OS packages.
How do I prevent another xz-utils-style attack on my dependencies?
No single tool prevents social engineering attacks. Layer your defenses: pin dependencies to exact versions and review upgrade diffs, generate and monitor SBOMs, scan for known vulnerabilities in CI, verify signatures on container images, use lockfiles and verify their checksums, and limit the number of direct dependencies. Tools like Socket.dev analyze package behavior changes (new network calls, filesystem access) that traditional CVE scanners miss.
Are reproducible builds practical for Node.js or Python projects?
They are harder than with Go or Rust but achievable. For Node.js: use npm ci (not npm install), pin all dependencies with a lockfile, set SOURCE_DATE_EPOCH for deterministic timestamps, and use a fixed Node.js version via a Docker image digest. For Python: use pip install --require-hashes with a pinned requirements file and build inside a pinned base image. The effort scales with project complexity, but even partial reproducibility improves your security posture.
What is the difference between vulnerability scanning and SBOM generation?
SBOM generation produces an inventory -- it tells you what components are in your software. Vulnerability scanning checks that inventory against databases of known CVEs. They are complementary: generate an SBOM once per build, then scan it against updated vulnerability databases continuously. Tools like Grype can scan a pre-generated SBOM without re-analyzing the artifact, making ongoing monitoring efficient.
Start With the Highest-Impact Steps
You do not need to implement everything at once. Start with three actions that give disproportionate returns: generate SBOMs for your container images using Syft or Trivy, enable Dependabot or Renovate for automated dependency updates, and pin your CI action versions by commit SHA. These three steps take less than a day and close the most common supply chain gaps. From there, add Cosign image signing and work toward SLSA Level 2 provenance. The goal is not perfection -- it is making every link in your supply chain verifiable and auditable.
Written by
Abhishek Patel
Infrastructure engineer with 10+ years building production systems on AWS, GCP, and bare metal. Writes practical guides on cloud architecture, containers, networking, and Linux for developers who want to understand how things actually work under the hood.
Related Articles
Best Vulnerability Scanners for Containers (2026): Snyk vs Trivy vs Grype vs Aqua
Benchmarked comparison of Snyk, Trivy, Grype, and Aqua against 100 production images. Real 2026 pricing, false-positive rates, scan times, and a decision matrix for picking the right scanner.
15 min read
AI/ML EngineeringvLLM vs TGI vs Triton: LLM Inference Server Comparison
Production LLM serving with vLLM 0.7, TGI 3.0, and NVIDIA Triton + TensorRT-LLM. Llama 3.1 70B H100 benchmarks, FP8 KV-cache numbers, $/1M token math, and a decision framework for picking the right server per team shape.
18 min read
SecurityBest Auth Providers (2026): Auth0 vs Clerk vs Supertokens vs WorkOS vs Supabase Auth
A practitioner comparison of the five dominant auth providers in 2026 -- Auth0, Clerk, Supertokens, WorkOS, and Supabase Auth -- with real pricing tiers, SSO connection math, SOC 2 / HIPAA / FedRAMP coverage, integration code samples, and a decision matrix that maps each vendor to a specific stack and scale.
15 min read
Enjoyed this article?
Get more like this in your inbox. No spam, unsubscribe anytime.