Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions sbom/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# SBOM Generation Package
#
# This package provides Bazel-native SBOM (Software Bill of Materials) generation
# using module extensions and aspects.
#
# Public API:
# - load("@score_tooling//sbom:defs.bzl", "sbom")
# - use_extension("@score_tooling//sbom:extensions.bzl", "sbom_metadata")

load("@rules_python//python:defs.bzl", "py_library")

package(default_visibility = ["//visibility:public"])

exports_files([
"defs.bzl",
"extensions.bzl",
"repos.bzl",
"repository_rules.bzl",
])

# Filegroup for all SBOM-related bzl files
filegroup(
name = "bzl_files",
srcs = [
"defs.bzl",
"extensions.bzl",
"//sbom/internal:bzl_files",
],
)
161 changes: 161 additions & 0 deletions sbom/SBOM_Implementation_Approach_SCORE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# Detailed SBOM Implementation Approach for Eclipse SCORE

## Executive Summary

This proposal addresses the existing backlog items ([#2144](https://github.com/eclipse-score/score/issues/2144), [#2232](https://github.com/eclipse-score/score/issues/2232), [#2060](https://github.com/eclipse-score/score/issues/2060), [#2103](https://github.com/eclipse-score/score/issues/2103)) and provides a comprehensive implementation roadmap for SBOM generation in Eclipse SCORE.

---

## High-Level Architecture


```
┌─────────────────────────────────────────────────────────────────────────────┐
│ SCORE SBOM ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Rust │ │ C++ │ │ Bazel │ │
│ │ Cargo.toml │ │ http_archive│ │ MODULE.bazel│ │
│ │ (metadata) │ │ git_override│ │ (bazel_dep) │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ SBOM GENERATOR MODULE │ │
│ │ ┌──────────────────────┐ ┌──────────────────────┐ │ │
│ │ │ Bazel Aspect │ │ Metadata Extension │ │ │
│ │ │ (dep graph traversal│ │ (license/supplier │ │ │
│ │ │ via sbom_aspect) │ │ from MODULE.bazel)│ │ │
│ │ └──────────────────────┘ └──────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────┴─────────────────┐ │
│ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ SPDX 2.3 │ │ CycloneDX │ │
│ │ .spdx.json │ │ 1.6 .json │ │
│ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```

### 2.2 Integration with Existing SCORE Tooling

Dash is a **license compliance checker** only (no SBOM output, no VEX).
SBOM generation is a new, separate module that complements Dash.

```
┌─────────────────────────────────────────────────────────────────────────────┐
│ eclipse-score/tooling │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ EXISTING NEW (IMPLEMENTED) │
│ ──────── ────────────────── │
│ ├── dash/ ├── sbom/ │
│ │ └── dash_license_checker │ ├── defs.bzl │
│ │ (license compliance) │ │ └── sbom() macro │
│ ├── cr_checker/ │ ├── extensions.bzl │
│ │ └── copyright_checker │ │ └── sbom.license() │
│ │ (header validation) │ ├── internal/ │
│ │ │ │ ├── aspect.bzl (dep traversal) │
│ │ │ │ ├── rules.bzl (build rule) │
│ │ │ │ └── generator/ │
│ │ │ │ ├── sbom_generator.py │
│ │ │ │ ├── spdx_formatter.py │
│ │ │ │ ├── cyclonedx_formatter.py │
│ │ │ │ └── purl.py │
│ │ │ └── tests/ │
│ │ │ │
│ COMPLEMENTARY WORKFLOW │ │
│ ────────────────────── │ │
│ Dash: checks if dependency │ │
│ licenses are allowed by policy │ │
│ SBOM: generates .spdx.json / │ │
│ .cdx.json listing all deps │ │
│ with name, version, license, │ │
│ supplier, PURL │ │
│ │ │
│ VALIDATION (external, optional) │ │
│ ──────────────────────────── │ │
│ pip install spdx-tools │ │
│ pyspdxtools -i out.spdx.json │ │
│ Or: https://tools.spdx.org │ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```

## 3. SBOM Generation Chain

When `bazel build //:my_sbom` is invoked, the following chain executes:

```
┌──────────────────────────────────────────────────────────────────────┐
│ PHASE 1: Loading (MODULE.bazel) │
│ │
│ sbom_metadata module extension iterates ALL modules in workspace: │
│ - Collects sbom.license() tags (name, license, supplier, version) │
│ - Collects sbom.license(type="cargo") tags (Rust crates) │
│ - Writes metadata.json to @sbom_metadata repository │
└──────────────────────────┬───────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────┐
│ PHASE 2: Analysis (aspect.bzl) │
│ │
│ sbom_aspect is attached to `targets` attr of sbom_rule. │
│ For each target in targets = ["//src:app"]: │
│ - Traverses deps, srcs, proc_macro_deps, hdrs, etc. │
│ - Recursively collects SbomDepsInfo from all transitive deps │
│ - Builds depsets of: │
│ * external_repos (e.g. "score_kyron", "crates__tokio-1.10") │
│ * transitive_deps (all labels in the dep graph) │
└──────────────────────────┬───────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────┐
│ PHASE 3: Execution (rules.bzl → sbom_generator.py) │
│ │
│ _sbom_impl combines aspect output + extension metadata: │
│ 1. Reads external_repos and transitive_deps from SbomDepsInfo │
│ 2. Reads metadata.json from @sbom_metadata extension │
│ 3. Writes _deps.json with all data + config │
│ 4. Runs sbom_generator.py which: │
│ a. Filters repos by exclude_patterns (removes build tools) │
│ b. Resolves each repo to a component (name, version, PURL) │
│ c. Merges extension metadata (license, supplier, version) │
│ d. Calls spdx_formatter.py → {name}.spdx.json │
│ e. Calls cyclonedx_formatter.py → {name}.cdx.json │
└──────────────────────────────────────────────────────────────────────┘
```

### Key files in the chain

| File | Phase | Role |
|------|-------|------|
| `extensions.bzl` | Loading | Collects `sbom.license()` from all modules (all dep types) |
| `internal/aspect.bzl` | Analysis | Traverses target dep graph, returns `SbomDepsInfo` |
| `internal/providers.bzl` | Analysis | Defines `SbomDepsInfo` provider (external_repos, transitive_deps) |
| `internal/rules.bzl` | Execution | Joins aspect + extension data, invokes Python generator |
| `internal/generator/sbom_generator.py` | Execution | Resolves repos to components, calls formatters |
| `internal/generator/spdx_formatter.py` | Execution | Produces SPDX 2.3 JSON |
| `internal/generator/cyclonedx_formatter.py` | Execution | Produces CycloneDX 1.6 JSON |
| `internal/generator/purl.py` | Execution | Generates Package URLs for components |
| `defs.bzl` | Public API | `sbom()` macro |

## 4. Tool Selection

### 4.1 Implemented Tool Stack

| Component | Tool Used | Status | Rationale |
|-----------|-----------|--------|-----------|
| SBOM Framework | Custom Bazel rules (aspects + module extension) | Implemented | Native Bazel integration, hermetic builds |
| Dependency Discovery | Bazel aspect (sbom_aspect) | Implemented | Traverses transitive deps of any target |
| Rust Crate Metadata | `sbom.license(type = "cargo")` in MODULE.bazel | Implemented | Manual license/supplier, auto PURL |
| SPDX Generation | Custom Python formatter (spdx_formatter.py) | Implemented | SPDX 2.3 JSON, validated at tools.spdx.org |
| CycloneDX Generation | Custom Python formatter (cyclonedx_formatter.py) | Implemented | CycloneDX 1.6 JSON |
| License Data | `sbom.license()` in MODULE.bazel | Implemented | Manual declaration per dependency |
| SPDX Validation | [spdx-tools](https://github.com/spdx/tools-python) (external) | Available | For offline validation |
| License Compliance | Existing Dash (separate tool) | Existing | Complements SBOM, not integrated |

---
85 changes: 85 additions & 0 deletions sbom/SBOM_Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# SBOM Setup Guide

## 1. Configure MODULE.bazel

Add the following at the end of your `MODULE.bazel`:

```starlark
# Load the SBOM extension and make the generated metadata repo available
sbom_ext = use_extension("@score_tooling//sbom:extensions.bzl", "sbom_metadata")
use_repo(sbom_ext, "sbom_metadata")

# Declare license/supplier for each dependency:

# For bazel_dep() modules — version is read from the module graph, no need to specify it:
sbom_ext.license(name = "googletest", license = "BSD-3-Clause", supplier = "Google LLC")

# For http_archive deps — version is NOT in the module graph, must be specified:
sbom_ext.license(name = "boost", license = "BSL-1.0", version = "1.87.0", supplier = "Boost.org")

# For git_override deps — specify version (commit) + remote so a PURL can be generated:
sbom_ext.license(name = "iceoryx2", license = "Apache-2.0", supplier = "Eclipse Foundation",
version = "d3d1c9a", remote = "https://github.com/eclipse-iceoryx/iceoryx2.git")

# For Rust crates (type = "cargo" generates pkg:cargo/ PURL):
sbom_ext.license(name = "tokio", license = "MIT", version = "1.10", type = "cargo",
supplier = "Tokio Contributors")
```

## 2. Add SBOM target in BUILD

```starlark
load("@score_tooling//sbom:defs.bzl", "sbom")

sbom(
name = "my_sbom",
targets = ["//my/app:binary"],
component_name = "my_application",
component_version = "1.0.0",
)
```

## 3. Build

```bash
bazel build //:my_sbom
```

## 4. Output

Two files in `bazel-bin/`:

- `my_sbom.spdx.json` -- SPDX 2.3
- `my_sbom.cdx.json` -- CycloneDX 1.6

---

## Auto-extracted vs manual fields

**Always auto-extracted:**

| Field | Source |
|-------|--------|
| Dependency list | Aspect traverses transitive deps of your targets |
| Version (bazel_dep) | From module graph |
| Version (crates) | From crate repo name |
| PURL | Generated from URLs/remotes |

**What is excluded from the SBOM:**

- Dependencies not in the transitive dep graph of your `targets` (e.g. `dev_dependency = True` lint/formatting tools that your binary never links against)
- Build toolchain repos matching `exclude_patterns` (e.g. `rules_rust`, `rules_cc`, `bazel_tools`, `platforms`)

**What you must provide manually:**

| Field | Where | When |
|-------|-------|------|
| license | `sbom_ext.license()` | All dependencies |
| supplier | `sbom_ext.license()` | Recommended for NTIA compliance |
| version | `sbom_ext.license()` | For http_archive/git/crate deps (auto-extracted for bazel_dep) |

---

## Example

See `reference_integration/BUILD:39-66` for working SBOM targets and `reference_integration/MODULE.bazel:69-77` for the metadata extension setup.
Loading