DevSecOps Guides

DevSecOps Guides

SBOM and Bill of Materials Security Labs in 2026

We wrote 35 hands-on labs covering every aspect of Software Bill of Materials and its variants. Each lab walks through a real gap in visibility, shows what happens when that gap exists during a CVE.

Reza's avatar
Reza
Apr 03, 2026
∙ Paid

Before we start, shoutout to a platform we built for YOU!

💎 Your next level in cybersecurity isn’t a dream, it’s a proactive roadmap.

HADESS AI Career Coach turns ambition into expertise:

→ 390+ clear career blueprints from entry-level to leadership

→ 490+ in-demand skill modules + practical labs

→ Intelligent AI(Not AI buzz, applied AI, promise!) tools + real-world expert coaches and scenarios

Master the skills that matter. Land the roles that pay. Build the future you want.

🔥 Start engineering your career →

https://career.hadess.io


The labs span seven areas: SBOM standards and formats (SPDX, CycloneDX, NTIA minimum elements, Package URL), SBOM generation tools (Syft, Trivy, cdxgen, Docker Scout, Microsoft sbom-tool), cloud-specific SBOM solutions (Amazon Inspector for ECR, Lambda, and EC2, Azure Defender, GCP Artifact Analysis), CI/CD SBOM pipelines (GitHub Actions, GitLab CI, Cosign attestation, Kyverno admission), SBOM consumption platforms (Dependency-Track, GUAC, DefectDojo, Grype), vulnerability triage with VEX (OpenVEX, CycloneDX VEX, CSAF advisories), and alternative bill of materials types (AI BOM for ML models, Cryptographic BOM for PQC migration, SaaSBOM, Hardware BOM, Operations BOM).


Table of Contents

SBOM Standards and Formats

  1. SPDX SBOM Format Deep Dive

  2. CycloneDX SBOM Format Deep Dive

  3. NTIA Minimum Elements Compliance

  4. Package URL (purl) for Component Identification

SBOM Generation Tools

  1. Syft for Container Image SBOM Generation

  2. Trivy SBOM Generation and Scanning

  3. cdxgen for Application Level SBOMs

  4. Docker Scout SBOM and Analysis

  5. Microsoft SBOM Tool (sbom-tool)

  6. GitHub Dependency Graph and SBOM Export

Cloud SBOM Solutions

  1. Amazon Inspector SBOM for ECR Container Images

  2. Amazon Inspector SBOM for Lambda Functions

  3. Amazon Inspector SBOM for EC2 Instances

  4. Azure Defender for Containers SBOM

  5. GCP Artifact Analysis and On-Demand Scanning

CI/CD SBOM Pipelines

  1. GitHub Actions SBOM Pipeline

  2. GitLab CI SBOM Pipeline

  3. SBOM Attestation with Cosign

  4. Kyverno SBOM Admission Policy

SBOM Consumption and Analysis

  1. OWASP Dependency-Track for SBOM Management

  2. GUAC (Graph for Understanding Artifact Composition)

  3. Grype SBOM-Based Vulnerability Scanning

Vulnerability Triage with VEX

  1. OpenVEX for Vulnerability Exploitability

  2. CycloneDX VEX (Vulnerability Disclosure Report)

  3. CSAF (Common Security Advisory Framework)

Alternative Bill of Materials Types

  1. AI Bill of Materials (AI BOM / ML BOM)

  2. Cryptographic Bill of Materials (CBOM)

  3. SaaSBOM (Software-as-a-Service Bill of Materials)

  4. Hardware Bill of Materials (HBOM)

  5. Operations Bill of Materials (OBOM)

Advanced SBOM Topics

  1. SBOM Diff and Drift Detection

  2. SBOM for Kubernetes Clusters

  3. SBOM for License Compliance

  4. DefectDojo SBOM Ingestion and Vulnerability Management

  5. Complete SBOM Pipeline End-to-End


Lab 01: SPDX SBOM Format Deep Dive

SPDX (Software Package Data Exchange) is a Linux Foundation project and an ISO standard for communicating software bill of materials information. It was the first SBOM format to gain widespread adoption and remains the format specified in many government procurement requirements.

We focus on SPDX 2.3 in this lab because it is the current stable release and the version most tools generate by default. SPDX 3.0 exists but tooling support is still catching up.

SPDX documents have five main element types:

  • Document: the top-level container with creation info, document namespace, and licensing metadata

  • Package: a unit of software (library, application, container layer, OS package)

  • File: an individual file within a package, with checksums and license info

  • Snippet: a portion of a file (rarely used in practice)

  • Relationship: how elements relate to each other (DEPENDS_ON, CONTAINS, BUILD_TOOL_OF, DESCRIBED_BY, and others)

SPDX supports multiple serialization formats: SPDX-JSON, SPDX-TV (tag-value), SPDX-RDF, SPDX-YAML, and SPDX-XML. JSON is the most common in CI/CD pipelines today.

Required fields at the document level:

  • SPDXVersion (e.g., SPDX-2.3)

  • DataLicense (always CC0-1.0, this licenses the SBOM data itself)

  • SPDXID (SPDXRef-DOCUMENT)

  • DocumentName

  • DocumentNamespace (a unique URI for this specific SBOM)

Package-level fields that matter for vulnerability matching:

  • name, versionInfo, downloadLocation

  • supplier (who distributes the package)

  • externalRefs with type cpe23Type or purl for cross-referencing vulnerability databases

Root Cause Analysis

Organizations fall into two failure modes with SPDX. The first is having no SBOM at all, meaning they ship software with zero visibility into its composition. The second, more subtle failure, is generating malformed SPDX documents that are missing required fields. A malformed SBOM gives a false sense of compliance. Downstream consumers (vulnerability scanners, procurement systems, compliance auditors) silently fail when fields like supplier or externalRefs are absent.

The root cause is typically that teams add SBOM generation as an afterthought. They run a quick syft or trivy command, store the output, and never validate it.

Vulnerable Configuration

A CI pipeline that generates no SBOM, or generates a broken one:

# .github/workflows/build.yml - VULNERABLE
name: Build and Push
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build image
        run: docker build -t myapp:latest .
      - name: Push image
        run: docker push myapp:latest
      # No SBOM generation at all

Or worse, a hand-crafted SPDX file missing required fields:

{
  "spdxVersion": "SPDX-2.3",
  "SPDXID": "SPDXRef-DOCUMENT",
  "name": "myapp",
  "packages": [
    {
      "name": "express",
      "SPDXID": "SPDXRef-Package-express"
    }
  ]
}

This document is missing dataLicense, documentNamespace, creationInfo, package versionInfo, downloadLocation, supplier, and externalRefs. It will fail validation and is useless for vulnerability matching.

Exploitation

Step 1: Show the problem with missing SBOMs

Pull a container image and inspect it without SBOM tooling:

docker pull nginx:1.25
docker inspect nginx:1.25 | jq '.[0].RootFS.Layers | length'

Expected output:

7

We see 7 layers but have zero information about the hundreds of packages inside those layers.

Step 2: Generate a proper SPDX SBOM with Syft

syft nginx:1.25 -o spdx-json=nginx-spdx.json

Expected output:

 ✔ Pulled image
 ✔ Loaded image            nginx:1.25
 ✔ Parsed image            sha256:a8758716bb6a...
 ✔ Cataloged contents      156 packages
   ├── dpkg           90 packages
   ├── java-archive    0 packages
   └── ...

Step 3: Examine the generated SPDX structure

jq '{
  spdxVersion: .spdxVersion,
  dataLicense: .dataLicense,
  SPDXID: .SPDXID,
  name: .name,
  documentNamespace: .documentNamespace,
  packageCount: (.packages | length),
  relationshipCount: (.relationships | length)
}' nginx-spdx.json

Expected output:

{
  "spdxVersion": "SPDX-2.3",
  "dataLicense": "CC0-1.0",
  "SPDXID": "SPDXRef-DOCUMENT",
  "name": "nginx-1.25",
  "documentNamespace": "https://anchore.com/syft/image/nginx-1.25-...",
  "packageCount": 156,
  "relationshipCount": 312
}

Step 4: Check a single package for required fields

jq '.packages[] | select(.name == "openssl") | {
  name, versionInfo, downloadLocation, supplier,
  externalRefs: [.externalRefs[]? | {referenceType, referenceLocator}]
}' nginx-spdx.json

Expected output:

{
  "name": "openssl",
  "versionInfo": "3.0.11-1~deb12u2",
  "downloadLocation": "NOASSERTION",
  "supplier": "Organization: Debian",
  "externalRefs": [
    {
      "referenceType": "cpe23Type",
      "referenceLocator": "cpe:2.3:a:openssl:openssl:3.0.11-1~deb12u2:*:*:*:*:*:*:*"
    },
    {
      "referenceType": "purl",
      "referenceLocator": "pkg:deb/debian/openssl@3.0.11-1~deb12u2?arch=amd64&distro=debian-12"
    }
  ]
}

Step 5: Validate the SPDX document

pip install spdx-tools
python -m spdx_tools.spdx.parser.parse nginx-spdx.json

Expected output for a valid document:

The document is valid.

For an invalid document with missing fields:

Validation errors:
  - document_namespace is required
  - data_license must be CC0-1.0
  - creation_info.created is missing

Step 6: Check NTIA minimum elements compliance

pip install ntia-conformance-checker
ntia-checker --file nginx-spdx.json

Expected output:

NTIA Conformance Results:
  Supplier Name:           PASS
  Component Name:          PASS
  Version:                 PASS
  Unique Identifier:       PASS
  Dependency Relationship: PASS
  Author of SBOM Data:     PASS
  Timestamp:               PASS
Overall: COMPLIANT

Detection

Detect missing or invalid SBOMs in your environment:

# Check if an image has an attached SBOM (cosign)
cosign verify-attestation --type spdx myregistry.io/myapp:v1.2.3

# Validate SPDX documents in a directory
for f in sboms/*.json; do
  echo "Validating $f..."
  python -m spdx_tools.spdx.parser.parse "$f" 2>&1 | tail -1
done

# Count packages missing purl references
jq '[.packages[] | select(.externalRefs == null or
  (.externalRefs | map(.referenceType) | contains(["purl"]) | not))] |
  length' sbom.json

Solution

Fixed CI pipeline with SPDX generation, validation, and attestation:

# .github/workflows/build.yml - FIXED
name: Build and Push with SBOM
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install SBOM tools
        run: |
          curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
          pip install spdx-tools ntia-conformance-checker

      - name: Build image
        run: docker build -t myapp:${{ github.sha }} .

      - name: Generate SPDX SBOM
        run: |
          syft myapp:${{ github.sha }} -o spdx-json=sbom-spdx.json

      - name: Validate SPDX
        run: |
          python -m spdx_tools.spdx.parser.parse sbom-spdx.json

      - name: Check NTIA compliance
        run: |
          ntia-checker --file sbom-spdx.json --output ntia-report.json
          # Fail if not compliant
          ntia-checker --file sbom-spdx.json | grep -q "COMPLIANT"

      - name: Attest SBOM to image
        run: |
          cosign attest --predicate sbom-spdx.json \
            --type spdx \
            myregistry.io/myapp:${{ github.sha }}

      - name: Upload SBOM artifact
        uses: actions/upload-artifact@v4
        with:
          name: sbom-spdx
          path: sbom-spdx.json

Verification

After applying the fix, verify the SBOM is valid and attached:

# 1. Validate the generated SPDX
python -m spdx_tools.spdx.parser.parse sbom-spdx.json
# Expected: "The document is valid."

# 2. Confirm all required SPDX fields exist
jq '{
  hasVersion: (.spdxVersion != null),
  hasLicense: (.dataLicense == "CC0-1.0"),
  hasNamespace: (.documentNamespace != null),
  hasCreationInfo: (.creationInfo != null),
  packageCount: (.packages | length)
}' sbom-spdx.json

# 3. Check NTIA compliance
ntia-checker --file sbom-spdx.json
# Expected: Overall: COMPLIANT

# 4. Verify the attestation on the image
cosign verify-attestation --type spdx \
  --certificate-identity-regexp ".*" \
  --certificate-oidc-issuer-regexp ".*" \
  myregistry.io/myapp:latest

Lab 02: CycloneDX SBOM Format Deep Dive

User's avatar

Continue reading this post for free, courtesy of Reza.

Or purchase a paid subscription.
© 2026 Reza · Privacy ∙ Terms ∙ Collection notice
Start your SubstackGet the app
Substack is the home for great culture