Skip to content

Commit a05ef3f

Browse files
authored
feat(sbom): add SBOM generation, license resolution, and CSV export tooling (#239)
* feat(sbom): add SBOM generation, license resolution, and CSV export tooling Add mise-integrated SBOM pipeline for container images using Syft. Includes license resolution via crates.io/npm/PyPI APIs and CycloneDX JSON to CSV conversion. Adds agent skill for on-demand SBOM operations. Closes #237 * fix(sbom): chain task dependencies to run generate → resolve → csv sequentially * fix(sbom): add concurrent license resolution, progress logging, and exclude dev artifacts * feat(notices): add mise run notices to generate THIRD-PARTY-NOTICES with full license texts Use cargo-about for Rust crate licenses and pip-licenses for Python packages. Produces a single attribution file with per-package copyright notices and full license text for open-source compliance.
1 parent 5803c0b commit a05ef3f

File tree

10 files changed

+16478
-1851
lines changed

10 files changed

+16478
-1851
lines changed

.agents/skills/sbom/SKILL.md

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
---
2+
name: sbom
3+
description: Generate and manage Software Bill of Materials (SBOMs) for the OpenShell project. Covers SBOM generation with Syft, license resolution via public registries, and CSV export for compliance review. Trigger keywords - SBOM, sbom, bill of materials, license audit, license resolution, generate sbom, sbom csv, dependency license, supply chain, license scan.
4+
---
5+
6+
# SBOM Generation and License Resolution
7+
8+
Generate CycloneDX SBOMs, resolve missing licenses, and export to CSV for compliance review.
9+
10+
## Overview
11+
12+
The OpenShell SBOM tooling produces CycloneDX JSON SBOMs using Syft, resolves missing or hash-based licenses by querying public registries (crates.io, npm, PyPI), and exports the results to CSV for stakeholder review.
13+
14+
SBOMs are **release artifacts only** -- they are generated on demand and not committed to the repository. Output lands in `deploy/sbom/output/` (gitignored).
15+
16+
## Prerequisites
17+
18+
- `mise install` has been run (installs Syft and other tools)
19+
- The repository is checked out at the root
20+
21+
## Workflow 1: Full SBOM Generation (One Command)
22+
23+
```bash
24+
mise run sbom
25+
```
26+
27+
This single command chains three stages:
28+
29+
1. **Generate** (`sbom:generate`): Syft scans the workspace source tree and produces a CycloneDX JSON SBOM
30+
2. **Resolve** (`sbom:resolve`): Public registry APIs fill in missing or hash-based licenses in the JSON
31+
3. **CSV** (`sbom:csv`): JSON SBOMs are converted to CSV for review
32+
33+
Output directory: `deploy/sbom/output/`
34+
35+
After running, the user can find:
36+
- `deploy/sbom/output/*.cdx.json` -- full CycloneDX SBOMs
37+
- `deploy/sbom/output/*.csv` -- CSV exports ready for spreadsheet review
38+
39+
## Workflow 2: Individual Stages
40+
41+
Run stages independently when debugging or iterating:
42+
43+
```bash
44+
mise run sbom:generate # Generate JSON SBOMs only (requires Syft)
45+
mise run sbom:resolve # Resolve licenses in existing JSONs (queries APIs)
46+
mise run sbom:csv # Convert existing JSONs to CSV
47+
```
48+
49+
## Workflow 3: License Check (CI Advisory)
50+
51+
```bash
52+
mise run sbom:check
53+
```
54+
55+
Reports unresolved licenses without failing. Intended for PR CI as a non-blocking advisory check. Requires that SBOMs have already been generated (`mise run sbom:generate`).
56+
57+
## Workflow 4: Processing External SBOMs
58+
59+
The Python scripts accept explicit file paths, so they can process SBOMs from any source (e.g., NVIDIA nSpect pipeline output):
60+
61+
```bash
62+
uv run python deploy/sbom/resolve_licenses.py /path/to/external-sbom.json
63+
uv run python deploy/sbom/sbom_to_csv.py /path/to/external-sbom.json
64+
```
65+
66+
## License Resolution Details
67+
68+
The resolver queries these public registries:
69+
70+
| Registry | Package URL prefix | Method |
71+
|----------|-------------------|--------|
72+
| crates.io | `pkg:cargo/*` | REST API |
73+
| npm | `pkg:npm/*` | Registry API |
74+
| PyPI | `pkg:pypi/*` | JSON API |
75+
| Go modules | `pkg:golang/*` | Known license map (no API) |
76+
| Debian/Ubuntu | `pkg:deb/*` | Known license map |
77+
78+
Components from private registries (e.g., `@openclaw/*` npm packages) are not resolved and will appear in the "unresolved" report.
79+
80+
## Output Files
81+
82+
| Pattern | Description |
83+
|---------|-------------|
84+
| `deploy/sbom/output/openshell-source-{version}.cdx.json` | CycloneDX JSON SBOM |
85+
| `deploy/sbom/output/openshell-source-{version}.csv` | CSV export (name, version, type, purl, licenses, bom-ref) |
86+
87+
## Key Files
88+
89+
| File | Purpose |
90+
|------|---------|
91+
| `deploy/sbom/resolve_licenses.py` | License resolution script |
92+
| `deploy/sbom/sbom_to_csv.py` | JSON-to-CSV converter |
93+
| `tasks/sbom.toml` | Mise task definitions |
94+
| `mise.toml` | Syft tool definition (under `[tools]`) |
95+
96+
## Quick Reference
97+
98+
| Task | Command |
99+
|------|---------|
100+
| Full pipeline | `mise run sbom` |
101+
| Generate only | `mise run sbom:generate` |
102+
| Resolve licenses | `mise run sbom:resolve` |
103+
| Export CSV | `mise run sbom:csv` |
104+
| CI license check | `mise run sbom:check` |
105+
| Process external SBOM | `uv run python deploy/sbom/resolve_licenses.py <file>` |

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,9 @@ _build/
184184
# Docker build artifacts (image tarballs, packaged helm charts)
185185
deploy/docker/.build/
186186

187+
# SBOM generated output (JSON, CSV) — release artifacts, not committed
188+
deploy/sbom/output/
189+
187190
# Local mise settings
188191
mise.local.toml
189192

0 commit comments

Comments
 (0)