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

430 lines
17 KiB
Markdown

# 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:
```http
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:
```json
{
"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:
```json
{
"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
```json
{
"$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:
```json
{
"_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:
```json
{
"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
```typescript
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` |
## Related Flows
- [Scan Submission Flow](02-scan-submission-flow.md) - Parent flow
- [Binary Delta Attestation Flow](15-binary-delta-attestation-flow.md) - Binary-level analysis
- [Evidence Bundle Export Flow](13-evidence-bundle-export-flow.md) - SBOM packaging