Files
git.stella-ops.org/docs/flows/03-sbom-generation-flow.md
StellaOps Bot ca578801fd save progress
2026-01-03 00:49:19 +02:00

17 KiB

SBOM Generation Flow

Overview

The SBOM Generation Flow describes how StellaOps creates Software Bills of Materials for container images using its 11 language-specific analyzers. The flow covers layer extraction, dependency detection, transitive resolution, and final SBOM assembly in SPDX 3.0.1 or CycloneDX 1.6 format.

Business Value: Complete, accurate SBOMs enable precise vulnerability matching and regulatory compliance (Executive Order 14028, EU CRA).

Actors

Actor Type Role
Scanner Service Orchestrates SBOM generation
Layer Extractor Component Unpacks OCI layers
OS Detector Component Identifies base operating system
Language Analyzers (11) Components Detect ecosystem-specific dependencies
SBOM Assembler Component Merges results into final SBOM
Attestor Service Signs SBOM with DSSE envelope

Prerequisites

  • Container image accessible (registry or local)
  • Registry credentials if private registry
  • Analyzer plugins enabled for target ecosystems

Language Analyzers

StellaOps includes 11 specialized analyzers:

Analyzer Ecosystems Lock Files Manifests
DotNet NuGet, .NET packages.lock.json, *.deps.json *.csproj, *.fsproj, Directory.Packages.props
Java Maven, Gradle pom.xml, build.gradle.kts gradle.lockfile, maven-dependency-tree.txt
Node npm, yarn, pnpm package-lock.json, yarn.lock, pnpm-lock.yaml package.json
Python pip, Poetry, Pipenv requirements.txt, poetry.lock, Pipfile.lock pyproject.toml, setup.py
Go Go Modules go.sum go.mod
Rust Cargo Cargo.lock Cargo.toml
PHP Composer composer.lock composer.json
Ruby Bundler Gemfile.lock Gemfile
Deno Deno, JSR deno.lock deno.json, import_map.json
Bun Bun bun.lockb package.json
Binary Native ELF/PE/Mach-O N/A Symbol tables, build IDs

Flow Diagram

┌─────────────────────────────────────────────────────────────────────────────────┐
│                           SBOM Generation Flow                                   │
└─────────────────────────────────────────────────────────────────────────────────┘

┌─────────┐   ┌───────────┐   ┌────────────┐   ┌───────────────────────────────┐
│ Scanner │   │  Layer    │   │    OS      │   │      Language Analyzers       │
│         │   │ Extractor │   │  Detector  │   │ (DotNet, Java, Node, etc.)    │
└────┬────┘   └─────┬─────┘   └──────┬─────┘   └───────────────┬───────────────┘
     │              │                │                         │
     │ Fetch image  │                │                         │
     │ manifest     │                │                         │
     │──────┐       │                │                         │
     │      │       │                │                         │
     │<─────┘       │                │                         │
     │              │                │                         │
     │ Extract      │                │                         │
     │ layers       │                │                         │
     │─────────────>│                │                         │
     │              │                │                         │
     │              │ Unpack to      │                         │
     │              │ work dir       │                         │
     │              │───────┐        │                         │
     │              │       │        │                         │
     │              │<──────┘        │                         │
     │              │                │                         │
     │              │ Layers ready   │                         │
     │<─────────────│                │                         │
     │              │                │                         │
     │ Detect OS    │                │                         │
     │──────────────────────────────>│                         │
     │              │                │                         │
     │              │                │ Parse /etc/os-release   │
     │              │                │ Check package manager   │
     │              │                │──────────┐              │
     │              │                │          │              │
     │              │                │<─────────┘              │
     │              │                │                         │
     │ OS: Alpine 3.19               │                         │
     │<──────────────────────────────│                         │
     │              │                │                         │
     │ Fan-out to   │                │                         │
     │ all analyzers│                │                         │
     │────────────────────────────────────────────────────────>│
     │              │                │                         │
     │              │                │                         │ ┌─────────┐
     │              │                │                         │ │ DotNet  │
     │              │                │                         │ └────┬────┘
     │              │                │                         │      │
     │              │                │                   Scan *.csproj
     │              │                │                   Parse deps.json
     │              │                │                         │<─────┘
     │              │                │                         │
     │              │                │                         │ ┌─────────┐
     │              │                │                         │ │  Java   │
     │              │                │                         │ └────┬────┘
     │              │                │                         │      │
     │              │                │                   Parse pom.xml
     │              │                │                   Run gradle deps
     │              │                │                         │<─────┘
     │              │                │                         │
     │              │                │                         │ ┌─────────┐
     │              │                │                         │ │  Node   │
     │              │                │                         │ └────┬────┘
     │              │                │                         │      │
     │              │                │                   Parse lockfiles
     │              │                │                   Build dep tree
     │              │                │                         │<─────┘
     │              │                │                         │
     │              │                │           ... (8 more analyzers)
     │              │                │                         │
     │ Analyzer     │                │                         │
     │ results      │                │                         │
     │<────────────────────────────────────────────────────────│
     │              │                │                         │
     │  ┌────────────────┐           │                         │
     │  │ SBOM Assembler │           │                         │
     │  └───────┬────────┘           │                         │
     │          │                    │                         │
     │ Merge &  │                    │                         │
     │ dedupe   │                    │                         │
     │──────────>                    │                         │
     │          │                    │                         │
     │ SBOM     │                    │                         │
     │ document │                    │                         │
     │<─────────│                    │                         │
     │          │                    │                         │
     │  ┌─────────┐                  │                         │
     │  │Attestor │                  │                         │
     │  └────┬────┘                  │                         │
     │       │                       │                         │
     │ Sign  │                       │                         │
     │───────>                       │                         │
     │       │                       │                         │
     │ DSSE  │                       │                         │
     │<──────│                       │                         │
     │       │                       │                         │

Step-by-Step

1. Image Manifest Fetch

Scanner retrieves OCI image manifest:

GET /v2/library/nginx/manifests/1.25 HTTP/1.1
Host: registry-1.docker.io
Accept: application/vnd.oci.image.manifest.v1+json

Response contains layer digests:

{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.manifest.v1+json",
  "config": {
    "digest": "sha256:config123..."
  },
  "layers": [
    {"digest": "sha256:layer1...", "size": 31456789},
    {"digest": "sha256:layer2...", "size": 1234567}
  ]
}

2. Layer Extraction

Layer Extractor downloads and unpacks each layer:

work/
├── layer-0/          # Base OS layer (Alpine, Debian, etc.)
│   ├── etc/
│   ├── lib/
│   └── usr/
├── layer-1/          # Application layer
│   ├── app/
│   │   ├── node_modules/
│   │   ├── package.json
│   │   └── package-lock.json
│   └── ...
└── merged/           # Union filesystem view

3. OS Detection

OS Detector identifies the base operating system:

Detection Method Files Checked
/etc/os-release ID, VERSION_ID
/etc/alpine-release Alpine version
/etc/debian_version Debian version
Package manager apk, dpkg, rpm

Result:

{
  "os": "alpine",
  "version": "3.19",
  "package_manager": "apk",
  "architecture": "amd64"
}

4. Parallel Analyzer Execution

All 11 analyzers run in parallel on the merged filesystem:

DotNet Analyzer

Scanning for:
- *.csproj, *.fsproj, *.vbproj
- *.deps.json (runtime dependencies)
- packages.lock.json (NuGet lock)
- Directory.Packages.props (central management)

Output:
{
  "packages": [
    {"purl": "pkg:nuget/Newtonsoft.Json@13.0.3", "scope": "runtime"},
    {"purl": "pkg:nuget/Microsoft.Extensions.Logging@8.0.0", "scope": "runtime"}
  ]
}

Java Analyzer

Scanning for:
- pom.xml (Maven)
- build.gradle, build.gradle.kts (Gradle)
- *.jar in lib/ directories
- MANIFEST.MF inside JARs

Output:
{
  "packages": [
    {"purl": "pkg:maven/com.google.guava/guava@32.1.2-jre", "scope": "compile"},
    {"purl": "pkg:maven/org.slf4j/slf4j-api@2.0.9", "scope": "runtime"}
  ]
}

Node Analyzer

Scanning for:
- package.json + package-lock.json (npm)
- yarn.lock (Yarn)
- pnpm-lock.yaml (pnpm)
- node_modules/.package-lock.json

Output:
{
  "packages": [
    {"purl": "pkg:npm/express@4.18.2", "scope": "runtime"},
    {"purl": "pkg:npm/lodash@4.17.21", "scope": "runtime"}
  ]
}

5. Transitive Resolution

Each analyzer resolves transitive dependencies:

express@4.18.2
├── accepts@1.3.8
│   ├── mime-types@2.1.35
│   │   └── mime-db@1.52.0
│   └── negotiator@0.6.3
├── body-parser@1.20.1
│   ├── bytes@3.1.2
│   └── ...
└── ...

6. SBOM Assembly

SBOM Assembler merges all analyzer results:

  1. Deduplication: Remove duplicate PURLs across analyzers
  2. Relationship mapping: Build component dependency graph
  3. Metadata enrichment: Add licenses, hashes, supplier info
  4. Format conversion: Output as SPDX 3.0.1 or CycloneDX 1.6
{
  "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
  "bomFormat": "CycloneDX",
  "specVersion": "1.6",
  "version": 1,
  "metadata": {
    "timestamp": "2024-12-29T10:30:00Z",
    "tools": [{"name": "stellaops-scanner", "version": "2.1.0"}],
    "component": {
      "type": "container",
      "name": "docker.io/library/nginx",
      "version": "1.25"
    }
  },
  "components": [
    {
      "type": "library",
      "bom-ref": "pkg:npm/express@4.18.2",
      "name": "express",
      "version": "4.18.2",
      "purl": "pkg:npm/express@4.18.2",
      "hashes": [{"alg": "SHA-256", "content": "abc123..."}]
    }
  ],
  "dependencies": [
    {
      "ref": "pkg:npm/express@4.18.2",
      "dependsOn": ["pkg:npm/accepts@1.3.8", "pkg:npm/body-parser@1.20.1"]
    }
  ]
}

7. SBOM Attestation

Attestor creates DSSE envelope for the SBOM:

{
  "_type": "https://in-toto.io/Statement/v1",
  "subject": [
    {
      "name": "docker.io/library/nginx",
      "digest": {"sha256": "abc123..."}
    }
  ],
  "predicateType": "https://spdx.dev/Document",
  "predicate": {
    "sbom": "base64-encoded-sbom...",
    "generator": "stellaops-scanner@2.1.0",
    "timestamp": "2024-12-29T10:30:00Z"
  }
}

Signed with DSSE:

{
  "payloadType": "application/vnd.in-toto+json",
  "payload": "base64-encoded-statement...",
  "signatures": [
    {
      "keyid": "sha256:signer-key-fingerprint",
      "sig": "base64-encoded-signature..."
    }
  ]
}

Data Contracts

Analyzer Output Schema

interface AnalyzerOutput {
  analyzer: string;
  ecosystem: string;
  packages: Array<{
    purl: string;
    name: string;
    version: string;
    scope: 'runtime' | 'dev' | 'optional';
    locations: string[];
    hashes?: Record<string, string>;
    licenses?: string[];
  }>;
  relationships: Array<{
    parent: string;
    child: string;
    type: 'depends-on' | 'dev-depends-on';
  }>;
}

SBOM Output Formats

Format Schema Version Use Case
CycloneDX 1.6 Default, rich dependency graph
SPDX 3.0.1 Regulatory compliance, legal
SPDX 2.3 Legacy compatibility

Error Handling

Error Recovery
Layer download failed Retry with exponential backoff
Analyzer timeout Mark analyzer as partial, continue
Lock file parse error Fall back to manifest parsing
Invalid PURL Log warning, skip component
Attestation failed Return SBOM without attestation

Observability

Metrics

Metric Type Labels
sbom_generation_duration_seconds Histogram image_size, layer_count
sbom_analyzer_duration_seconds Histogram analyzer
sbom_components_total Counter analyzer, ecosystem
sbom_size_bytes Histogram format

Key Log Events

Event Level Fields
sbom.generation.start INFO image, digest
sbom.analyzer.complete DEBUG analyzer, package_count
sbom.assembly.complete INFO total_components, format
sbom.attestation.signed INFO digest, keyid