Real-world threat context
SolarWinds (2020), Codecov (2021), Log4Shell (2021), XZ Utils (2024): attacks targeting the software supply chain have become the most devastating attack vector. A single compromised component can affect thousands of organizations simultaneously.
Why the software supply chain is the priority target
In December 2020, attackers compromised the build systems of SolarWinds and inserted a backdoor into legitimate updates of the Orion suite. More than 18,000 organizations downloaded the poisoned update, including US government agencies. The attack went undetected for 9 months.
In December 2021, CVE-2021-44228 (Log4Shell) revealed a critical vulnerability in Log4j, a Java library used in hundreds of thousands of applications. The problem: most teams did not even know they were using Log4j — it was present as a transitive dependency, hidden several levels deep.
These two incidents illustrate the two main vectors of supply chain attacks:
- Build chain compromise: an attacker tampers with the compilation process or the build tools
- Vulnerabilities in dependencies: third-party libraries contain unknown or deliberately introduced flaws
This tutorial covers the four defensive pillars: inventory (SBOM), detection (SCA), pipeline hardening (CI/CD) and artifact verification (signing + provenance).
SBOM: Software Bill of Materials
An SBOM is the software equivalent of a food product's ingredient list. It exhaustively lists all the components of an application: direct libraries, transitive dependencies, exact versions, licenses and relationships between components.
CycloneDX vs SPDX: choosing your format
Two standards dominate the market:
- SPDX (Software Package Data Exchange) — ISO 5962:2021 standard, maintained by the Linux Foundation. License-compliance oriented. XML, JSON or RDF format. Recommended for legal analysis and open source distribution.
- CycloneDX — OWASP standard, security oriented. Supports SBOMs, HBOMs (hardware), MBOMs (ML models) and VEX documents. JSON, XML or Protocol Buffers format. Recommended for DevSecOps use cases.
Recommendation
Use CycloneDX for operational security (CVE tracking, VEX) and SPDX if your legal team needs license compliance analysis. Both formats are interoperable and Syft generates both.
Generating an SBOM with Syft
Syft (Anchore) is the reference tool for generating SBOMs from Docker images, source code directories or archives.
# Installing Syft
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
# Check the installation
syft version
# Generate an SBOM from a Docker image (CycloneDX JSON format)
syft image:my-app:latest -o cyclonedx-json=sbom.cdx.json
# Generate an SBOM from an image with a full tag
syft registry:myregistry.io/my-app:v1.2.3 -o cyclonedx-json=sbom.cdx.json
# Generate from a directory (analyzes the source code)
syft dir:/path/to/project -o cyclonedx-json=sbom.cdx.json
# SPDX JSON format
syft image:my-app:latest -o spdx-json=sbom.spdx.json
# SPDX tag-value format (for legal toolchains)
syft image:my-app:latest -o spdx-tag-value=sbom.spdx
# Table display for quick inspection
syft image:my-app:latest -o table
# Include dev packages in the SBOM
syft dir:/project -o cyclonedx-json --scope all-layers
A CycloneDX JSON SBOM looks like this (simplified excerpt):
{
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79",
"version": 1,
"metadata": {
"timestamp": "2026-02-20T10:00:00Z",
"tools": [{"vendor": "anchore", "name": "syft", "version": "1.4.0"}],
"component": {
"type": "container",
"name": "my-app",
"version": "1.2.3"
}
},
"components": [
{
"type": "library",
"name": "log4j-core",
"version": "2.17.1",
"purl": "pkg:maven/org.apache.logging.log4j/[email protected]",
"licenses": [{"expression": "Apache-2.0"}]
}
]
}
Integrating the SBOM into your CI pipeline
The SBOM must be generated at every build and archived as an artifact. Here is a GitHub Actions example:
# .github/workflows/sbom.yml
name: SBOM Generation
on:
push:
branches: [main]
release:
types: [published]
jobs:
sbom:
runs-on: ubuntu-latest
permissions:
contents: write
packages: read
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Build image
run: docker build -t my-app:${{ github.sha }} .
- name: Install Syft
uses: anchore/sbom-action/download-syft@fd74a6fb98a204a1ad35bbfae0122c1a302ff88d # v0.15.0
- name: Generate SBOM
uses: anchore/sbom-action@fd74a6fb98a204a1ad35bbfae0122c1a302ff88d # v0.15.0
with:
image: my-app:${{ github.sha }}
format: cyclonedx-json
output-file: sbom-${{ github.sha }}.cdx.json
artifact-name: sbom-${{ github.sha }}.cdx.json
- name: Upload SBOM as release asset
if: github.event_name == 'release'
uses: actions/upload-release-asset@e8f9f06c4b078e705bd2ea027f0926603fc9b4d5 # v1.0.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: sbom-${{ github.sha }}.cdx.json
asset_name: sbom.cdx.json
asset_content_type: application/json
Comments