Skip to content
Learni
View all tutorials
Sécurité logicielle

How to Generate, Validate, and Sign an SBOM in 2026

Lire en français

Introduction

A SBOM (Software Bill of Materials) is a comprehensive, structured inventory of an application's software components, including libraries, versions, and transitive dependencies. In 2026, with the US Executive Order 14028 and Europe's Cyber Resilience Act, SBOMs are mandatory for critical software supply chains. Think of your app like a recipe: without a precise list of ingredients (and suppliers), you can't detect poison (zero-day vulnerabilities like Log4Shell). This advanced tutorial guides you from basic generation with Syft to cryptographic SLSA3 signing, including validation and CI/CD integration. By the end, you'll produce CycloneDX/SPDX-compliant SBOMs ready for automated scans and regulatory audits. Perfect for senior DevSecOps pros managing containerized deployments.

Prerequisites

  • Docker installed (version 24+ for buildkit)
  • Go 1.22+ (to compile Syft/Trivy)
  • GitHub account with a private repository
  • Node.js 20+ and npm (for example project)
  • CLI tools: cosign and jq via brew/apt
  • Knowledge of supply chain security (SLSA, in-toto)

Install Syft and SBOM Tools

install-tools.sh
#!/bin/bash

# Install Syft (Anchore) via Go
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin v1.10.0

# Install Grype for vulnerability scanning
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin v0.94.0

# Install CycloneDX CLI for validation
curl -sSfL https://github.com/CycloneDX/cyclonedx-cli/releases/latest/download/cyclonedx-linux-amd64.tar.gz | tar -xz -C /usr/local/bin cyclonedx
chmod +x /usr/local/bin/cyclonedx

# Verify installations
syft version
grype version
cyclonedx version

This script installs Syft to generate SBOMs from images or sources, Grype to scan vulnerabilities, and CycloneDX CLI to validate the format. Use pinned versions for reproducibility in CI/CD; avoid root installs in production by using containers.

Generate an SBOM for a Docker Image

Let's start with a real-world case: an Alpine image with Nginx. Syft scans Docker layers to list OS packages, libraries, and metadata. The SBOM outputs as CycloneDX 1.6 JSON, an open, machine-readable standard.

Build Image and Generate SBOM

generate-sbom-docker.sh
#!/bin/bash

# Simple Dockerfile for testing
docker run --rm -d --name test-nginx nginx:alpine sleep 3600

# Generate SBOM in CycloneDX JSON
syft nginx:alpine -o cyclonedx-json=sbom-docker.json

# Scan vulnerabilities with Grype
grype sbom:./sbom-docker.json -o table

# Clean up
docker rm -f test-nginx

Syft extracts the SBOM from the nginx:alpine image without manual building, producing a complete JSON file with SHA256 hashes and PURLs. Grype cross-references this SBOM against CVE/NVD databases; in CI, pipe to Slack for critical alerts. Pitfall: don't forget multi-arch images.

Generate SBOM for a Node.js Source Project

Now move to an application project. Create a Node.js repo, install dependencies, then generate a source-level SBOM. This captures package-lock.json for transitives.

Set Up Node Project and Source SBOM

node-sbom.sh
#!/bin/bash

mkdir myapp && cd myapp
npm init -y
npm install express lodash

# Generate SBOM from source directory
syft dir:. -o cyclonedx-json=sbom-node.json --exclude ".git"

# Example extraction with jq (for verification)
jq '.components | length' sbom-node.json
jq '.metadata.component.name' sbom-node.json

Syft scans node_modules and the lockfile for a precise SBOM, excluding .git for security. jq verifies component count (~150 for Express+Lodash). Benefit: detects vulns before build; pitfall, use npm ci in CI for consistent lockfiles.

Example Generated SBOM JSON

sbom-example.json
{
  "bomFormat": "CycloneDX",
  "specVersion": "1.6",
  "serialNumber": "urn:uuid:3e671687-3953-4ab3-b6bb-17e9dd61fd62",
  "metadata": {
    "timestamp": "2026-01-01T00:00:00Z",
    "tools": [{"vendor": "anchore", "name": "syft", "version": "v1.10.0"}],
    "component": {
      "type": "application",
      "name": "myapp",
      "version": "1.0.0"
    }
  },
  "components": [
    {
      "type": "library",
      "name": "lodash",
      "version": "4.17.21",
      "purl": "pkg:npm/lodash@4.17.21",
      "hashes": [{"alg": "SHA-256", "value": "fe5854e6f14bece6c494d9753f0f069c5a5d6fb8d910261ee94611be79630ede"}]
    }
  ],
  "dependencies": []
}

This minimal CycloneDX SBOM illustrates the structure: metadata (tools, timestamp), components (PURL, hashes), dependencies (graph). Copy-paste for testing; in production, sign for non-repudiation.

Validate the Generated SBOM

Validation ensures schema compliance and integrity. CycloneDX CLI checks JSON/XML against official specs.

Validate and Convert SBOM

validate-sbom.sh
#!/bin/bash

# Validate CycloneDX JSON
cyclonedx-cli validate --input-file sbom-node.json --schema-file https://raw.githubusercontent.com/CycloneDX/specification/main/json/schema/cyclonedx-1.6-strict.json

# Convert to SPDX JSON
syft dir:. -o spdx-json=sbom-spdx.json
cyclonedx-cli convert --input-file sbom-node.json --output-file sbom-cyclonedx.xml --output-format xml

# Verify SPDX
cat sbom-spdx.json | jq '.packages | length'

Validation against the strict schema rejects errors; conversion enables dual formats (CycloneDX for tools, SPDX for government). Download schemas locally for air-gapped environments; pitfall: spec version mismatches cause false negatives.

Integrate SBOM into GitHub Actions CI/CD

Automate in pipelines: build, SBOM, scan, publish Artifact. Use GitHub Dependency Submission API for auto graphs.

GitHub Actions SBOM Workflow

.github/workflows/sbom.yml
name: Generate and Validate SBOM

on: [push, pull_request]

jobs:
  sbom:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
      - run: npm ci
      - name: Install Syft
        uses: anchore/sbom-action/download-syft@v0.15.4
      - name: Generate SBOM
        run: syft dir:. -o cyclonedx-json=sbom.json
      - name: Validate SBOM
        uses: CycloneDX/cyclonedx-cli-action@v3
        with:
          input: 'sbom.json'
          command: validate
      - name: Upload SBOM
        uses: actions/upload-artifact@v4
        with:
          name: sbom
          path: sbom.json
      - name: Submit to GitHub
        uses: actions/dependency-submission@v1
        with:
          dependency-graph: sbom.json

This workflow generates/validates/uploads SBOM on every push/PR and submits to the Dependency Graph for GitHub Advanced Security scans. npm caching speeds things up; add if: github.ref == 'refs/heads/main' for production-only.

Sign SBOM with Cosign/SLSA

sign-sbom.sh
#!/bin/bash

export COSIGN_EXPERIMENTAL=true

# Generate key pair (once)
cosign generate-key-pair

# Sign SBOM (SLSA provenance + SBOM)
syft packages nginx:alpine -o cyclonedx-json | cosign sign --key cosign.key - |
  cosign verify --key cosign.pub

# For OCI artifacts (OCI registry)
docker create nginx:alpine nginx.tar
cosign sign --yes nginx.tar --key cosign.key

Cosign signs the SBOM with asymmetric keys for SLSA Level 2/3, publicly verifiable. COSIGN_EXPERIMENTAL enables SBOM signing; integrate in GitHub with sigstore/cosign-installer. Pitfall: rotate keys via KMS.

Best Practices

  • Generate SBOM at every build: Integrate early in CI, not post-production.
  • Use normalized PURLs: CycloneDX/SPDX for interoperability (e.g., pkg:npm/lodash@4.17.21).
  • Sign and timestamp: Cosign + TSA for non-repudiable audits.
  • Store in registry: GitHub Packages or Harbor for versioning.
  • Automate scans: Grype/Trivy in loops, alert on CVSS>7 thresholds.

Common Errors to Avoid

  • Incomplete SBOM: Missing transitives? Force npm ci --frozen-lockfile.
  • Incompatible formats: CycloneDX 1.5 vs 1.6; pin tools.
  • No validation: Lax schemas crash in prod; always use --strict.
  • Ignore multi-platform: Test with docker buildx for arm64/x86.

Next Steps

Dive into SLSA Framework v1.0 at slsa.dev. Integrate OPA/Gatekeeper for SBOM policies in K8s. Check our Learni DevSecOps training for advanced certifications. Resources: CycloneDX spec, Anchore docs.