IMPORTANT NOTICE:
The Software Bill of Materials (SBOM) files generated by this project are automatically generated by the CNCF and are NOT official SBOMs provided by the individual CNCF projects themselves.
-
No Warranty: These SBOMs are provided "AS IS" without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and noninfringement.
-
No Guarantee of Completeness or Accuracy: We make no representations or guarantees regarding the completeness, accuracy, reliability, or currentness of the information contained in these SBOMs. The automated generation process may miss dependencies, include incorrect versions, or contain other errors.
-
Use at Your Own Risk: Any use of these SBOMs is entirely at your own risk. This project, its contributors, and maintainers shall not be liable for any claims, damages, or other liability arising from the use of these SBOMs.
-
Not a Substitute for Official SBOMs: For production use, compliance requirements, or security audits, please refer to the official documentation and releases of each individual CNCF project.
-
Automated Generation: These SBOMs are generated using Waybill (formerly Mikebom) with deps.dev enrichment for license and dependency resolution. The generation process runs weekly and supports all ecosystems that Waybill can detect (Go, Rust, npm, Python, Java/Maven, Ruby, and more).
This tool generates Software Bill of Materials (SBOM) in SPDX JSON format for CNCF projects and uploads them to OCI-compatible S3 storage.
The SBOM generator:
- Automatically syncs the list of CNCF projects from the CNCF Landscape
- Discovers additional subproject repositories within CNCF GitHub organizations
- Fetches stable releases (major, minor, patch) from CNCF project repositories
- Supports all ecosystems with lockfile-aware dependency graph extraction (Go, Rust, npm, Python, Maven, Ruby, etc.)
- Skips alpha, beta, RC, and other pre-release versions
- Generates SPDX 2.3-compliant SBOM files using Waybill (v0.10.0)
- Automatically enriches SBOMs with license information via deps.dev and ClearlyDefined
- Extracts real dependency graph edges (not a flat fan-out) from lockfiles
- Uploads SBOMs directly to OCI S3-compatible buckets (no SBOM files are stored in this repository)
Generated SBOMs are stored in two OCI S3-compatible buckets:
| Bucket | Content |
|---|---|
cncf-project-sboms |
Official CNCF project SBOMs |
cncf-subproject-sboms |
Subproject / discovered repo SBOMs |
A CNCF project has one folder name in both buckets: <project> is the slug of
its CNCF landscape name (lowercase, spaces become -, every other character
outside [a-z0-9-] is dropped), e.g. Argo → argo, Aeraki Mesh → aeraki-mesh,
Open Policy Agent (OPA) → open-policy-agent-opa.
Projects (cncf-project-sboms):
<project>/<version>/<project>_<version>_spdx.json
Example: argo/3.3.14/argo_3_3_14_spdx.json
Subprojects (cncf-subproject-sboms), with the same <project> slug as their parent:
<project>/<subproject>/<version>/<project>_<subproject>_<version>_spdx.json
Example: argo/argo-workflows/4.1.4/argo_argo-workflows_4_1_4_spdx.json
The parent of a subproject is recorded in util/data/discovered-repos.yaml as
parent_project (landscape name) and parent_repo (the project's main repository),
next to discovered_by, which is kept for provenance. When parent_project is
missing, discovered_by's owner/repo is looked up in cncf-projects.yaml. Only if
the parent is no longer in the landscape is the parent's repository name used, as
before. All writers (workflows, ingest-sbom-oci.sh, migrate-subproject-keys.sh)
derive these names from util/sbom-lib.sh, so they cannot drift apart.
Note: Dots in version numbers are replaced with underscores in the filename to avoid potential issues.
Migration: subproject SBOMs uploaded before this convention live under the parent's repository name (e.g.
argo-cd/argo-workflows/…,aeraki/meta-protocol-proxy/…).util/migrate-subproject-keys.shmoves them; see Utility Tools.
This repository contains only tooling and configuration — no SBOM data files.
./
├── README.md # This file
├── .gitignore # Excludes generated SBOM data
├── .github/workflows/
│ ├── sync-cncf-projects.yml # Sync CNCF project list from landscape
│ ├── discover-cncf-repos.yml # Discover subproject repos in CNCF orgs
│ ├── generate-sbom.yml # Generate SBOMs and upload to S3
│ ├── generate-tooling-sbom.yml # Generate SBOMs for this repo's tooling and CI chain
│ ├── watch-sandbox-applications.yml # One-time SBOMs for CNCF sandbox applications
│ └── migrate-sboms-to-oci.yml # One-time migration of legacy repo SBOMs to S3
└── util/
├── data/
│ ├── cncf-projects.yaml # Auto-synced CNCF project list (DO NOT EDIT)
│ ├── repositories.yaml # Manually maintained additional projects
│ └── discovered-repos.yaml # Subproject repos found in CNCF orgs (DO NOT EDIT)
├── extract-projects/ # Go tool to sync projects from CNCF landscape
├── discover-repos/ # Go tool to find subproject repos in CNCF orgs
├── generate-index/ # Go tool to generate an index of SBOMs (with project hierarchy)
├── generate-tooling-sbom.sh # Generates repo-tooling and CI-chain SBOMs for this repo
├── sbom-lib.sh # Shared folder-slug, parent-project, tag and import helpers
├── sbom-plan.sh # Plans a run: (repository, tag) pairs missing from the buckets
├── sbom-worker.sh # Processes one worker's share of the plan
├── postprocess-spdx.sh # Shared SPDX post-processing applied before every upload
├── postprocess-cdx.sh # Same for imported CycloneDX release assets
├── postprocess-spdx3.sh # Same for imported SPDX 3 JSON-LD release assets
├── migrate-subproject-keys.sh # Moves subproject SBOMs to the project slug (dry-run by default)
├── ingest-sbom-oci.sh # Script to upload local SBOMs to OCI buckets
├── generate-sbom-local.sh # Local testing script (Linux/macOS)
└── generate-sbom-local.ps1 # Local testing script (Windows)
Automatically syncs the list of CNCF projects from the official landscape.
- Scheduled: Daily at 03:00 UTC
- Manual trigger: Via workflow_dispatch
- Output:
util/data/cncf-projects.yaml
Scans GitHub organizations of CNCF projects to find additional subproject repositories with releases.
- Scheduled: Weekly on Monday at 04:00 UTC
- Manual trigger: Via workflow_dispatch
- Output:
util/data/discovered-repos.yaml
This workflow finds subproject repositories that:
- Belong to the same GitHub org/user as a CNCF project
- Have at least one release
- Are not forks, archived, or disabled
Generates SBOMs for CNCF and manually added projects and uploads them directly to OCI S3 buckets.
- Scheduled: Daily at 02:00 UTC (releases from the past 60 days that are not yet in the bucket; a failed run is caught up by the next one)
- Manual trigger: Via workflow_dispatch with optional filters
| Input | Description | Default |
|---|---|---|
project_filter |
Filter by owner/repo (e.g., "coredns/coredns") | empty (all projects) |
force_regenerate |
Force regenerate existing SBOMs | false |
releases_mode |
recent (past 60 days) or latest (N latest releases) |
recent |
max_releases |
Max releases per repo (only for latest mode) |
3 |
source |
all, cncf, manual, or discovered |
all |
How it works:
plan:util/sbom-plan.shreadscncf-projects.yaml, the manually maintainedrepositories.yamlanddiscovered-repos.yaml(2300+ repositories), fetches their releases and tags with GraphQL (50 repositories per query), lists both buckets once and writes the(repository, tag)pairs whose*_spdx.jsonobject is missing (all pairs withforce_regenerate) to aplan.jsonlartifact. Tags with a path segment (api/v0.21.3,kyaml/v0.21.3,pkg/app/.../v0.1.0) version a sub-component, not the repository, and would collide on the version-only key, so they are skippedgenerate: one matrix job per ~15 planned SBOMs (util/sbom-worker.sh, round-robin share of the plan, at most 30 in parallel). The matrix scales with the work, not with the repository count, so the 256-entry matrix limit is never hit and a quiet night costs a handful of jobs instead of 2300- SBOMs the project attached to the GitHub release are imported (see Release-attached SBOMs): a project-wide SPDX 2.x asset replaces our own scan, a per-binary one is uploaded next to it as
*_release_spdx.json - When the release has no project-wide SPDX 2.x asset, Waybill generates one with
waybill sbom scan --format spdx-2.3-json(deps.dev and ClearlyDefined enrichment inline) - Every SPDX 2.x SBOM is post-processed with
util/postprocess-spdx.sh, every CycloneDX asset withutil/postprocess-cdx.sh, every SPDX 3 asset withutil/postprocess-spdx3.sh(see SBOM post-processing) - Each SBOM is uploaded right after it is built, so a failing repository costs only its own item; the worker reports it and continues
- Workers write a
sbom-result-<n>artifact;summarize-sbom-runturns them into the run summary (planned, uploaded, failed items) - No files are committed to the repository
Add an entry to util/data/repositories.yaml:
repositories:
- owner: OmniTrustILM
repo: core
name: OmniTrust ILMEntries are included in scheduled and manually triggered source=all runs.
Use source=manual to scan only this list; cncf and discovered retain their
existing scopes. Duplicate owner/repo entries in the CNCF and manual lists are
scanned once (case-insensitive, with CNCF metadata taking precedence).
Manual projects use the project bucket and the existing project-name/version
key layout. The sync and discovery workflows do not overwrite this list.
For the first scan of an older release, run the generation workflow with
source=manual, project_filter=OmniTrustILM/core, and releases_mode=latest.
Scheduled runs keep the existing recent-release selection; adding an entry
does not itself trigger a run or backfill older releases.
The local Bash and PowerShell generators also read repositories.yaml.
One-time / on-demand migration of any legacy SBOM files from the repository into OCI S3 buckets.
- Manual trigger only: Via workflow_dispatch
- Skips objects that already exist in the bucket (unless
FORCEis set) - Uses
util/ingest-sbom-oci.sh, which applies the same folder naming and post-processing asgenerate-sbom.yml
Generates SBOMs for this repository's own tooling on every push, pull request, and manual run.
- Triggers:
push,pull_request,workflow_dispatch - Artifact:
tooling-sbom-<git-sha> - Tracked files on
main: - Files:
tooling-repo.spdx.json— Waybill-generated SBOM for this repository at the checked-out committooling-ci.spdx.json— SPDX inventory of the GitHub Actions and downloaded tools involved in SBOM generation
This separates the repository source inventory from the CI/generation chain, so the SBOM production tooling is captured explicitly as part of the delivered artifact set.
The workflow generates files in tooling-sbom-out/, uploads them as an artifact for each run, and refreshes the tracked tooling-sbom/ snapshots on pushes to main.
Polls cncf/sandbox issues daily at 02:17 UTC and on manual dispatch. Cross-repository issue creation does not directly trigger this repository's workflows, so new applications are picked up on the next poll.
Issues must start with [Sandbox]. GitHub repository URLs are extracted only
from the Project repo URL in scope of application field. Each repository in
that field gets one SBOM per application issue; links elsewhere in the issue
are not scanned. Malformed application fields are reported in the logs.
The first run includes existing open applications. A persistent start timestamp also allows subsequent polls to include newly created applications that have already been closed. Historical closed applications are excluded.
For each unprocessed repository, the workflow selects its latest stable release, falling back to the default branch if no stable release exists. The selected ref is resolved to a commit SHA before scanning; branch snapshots use that SHA as their version. The document name explicitly identifies the repository and scanned version, independent of the temporary checkout directory. No project build scripts are run.
SBOMs and processing state are stored in the dedicated sandbox-reviews
bucket, separate from CNCF project SBOMs. The optional OCI_SANDBOX_BUCKET
repository variable overrides this default. Application snapshots use the prefix:
sandbox-applications/<issue-number>/<owner>/<repo>.spdx.json
For example, application #525 for KubeRay produces
sandbox-applications/525/ray-project/kuberay.spdx.json. The key remains stable
even if new releases appear: a successfully uploaded SBOM marks that
issue/repository as processed. Failed scans or uploads are retried on subsequent
polls. Workflow runs are serialized, with up to five repository scans in parallel;
more than 256 pending repositories are deferred to subsequent runs.
The timestamp is stored in the same sandbox bucket at
sandbox-applications/.started-at (not an SBOM).
Keep this state and the uploaded snapshots to preserve one-time processing.
The S3 key needs list, read, and write access to this prefix; access errors stop
discovery rather than being interpreted as empty state. The scan step receives
no S3 credentials. Generated SBOMs and their resolved revision metadata are also
retained as GitHub Actions artifacts for seven days.
The following must be configured in the repository settings:
Secrets:
| Secret | Description |
|---|---|
OCI_S3_ACCESS_KEY |
S3-compatible access key with PutObject permission for the configured project, subproject, and sandbox buckets |
OCI_S3_SECRET_KEY |
S3-compatible secret key for OCI Object Storage |
Variables:
| Variable | Description | Example |
|---|---|---|
OCI_S3_ENDPOINT |
S3-compatible endpoint URL | https://axtwf1hkrwcy.compat.objectstorage.us-sanjose-1.oraclecloud.com |
OCI_S3_REGION |
S3 region | us-sanjose-1 |
OCI_PROJECT_BUCKET |
Bucket name for project SBOMs | cncf-project-sboms |
OCI_SUBPROJECT_BUCKET |
Bucket name for subproject SBOMs | cncf-subproject-sboms |
OCI_SANDBOX_BUCKET |
Optional sandbox application bucket override (default: sandbox-reviews) |
sandbox-reviews |
Upload failures fail the generation job, including partially successful uploads.
The logs show the target bucket/key, AWS CLI exit code and diagnostic output;
the uploaded output counts only successful uploads.
On S3-compatible storage, an error response with an empty Message can cause AWS
CLI to print argument of type 'NoneType' is not a container or iterable instead
of the service error. This has been reproduced with HTTP 403 AccessDenied.
Check the permissions and bucket policy for the S3 key stored in GitHub's
OCI_S3_ACCESS_KEY / OCI_S3_SECRET_KEY secrets. Successful read access does not
establish PutObject permission. Manually listed projects use OCI_PROJECT_BUCKET,
the same bucket as CNCF projects.
- Go to Actions tab in GitHub
- Select "Generate SBOM for CNCF Projects"
- Click "Run workflow"
- Optionally specify a project filter or change the releases mode
The run summary lists planned and uploaded SBOMs and every failed owner/repo@tag; the sbom-plan artifact holds the full plan of the run.
Go tool that downloads the CNCF landscape and extracts all projects with status graduated, incubating, or sandbox.
cd util/extract-projects
go run . ../data/cncf-projects.yamlGo tool that scans GitHub organizations of CNCF projects to find additional subproject repositories with releases.
cd util/discover-repos
go run . /path/to/cncf-automationThe tool will:
- Read the list of CNCF projects from
cncf-projects.yaml - Scan each unique GitHub organization/user
- Find subproject repos that have releases and contain a supported package manifest
- Record the owning project of every entry as
parent_project/parent_repo(also for entries discovered earlier, resolved fromdiscovered_by) - Output results to
discovered-repos.yaml
To only (re)record the parent fields without scanning GitHub:
cd util/discover-repos
go run . -backfill-only ../..Go tool that writes sbom/index.json for a local sbom/ tree (as produced by
generate-sbom-local.sh). Besides sboms and subproject_sboms, the index contains
a projects list (name, slug, owner, repo, status) from cncf-projects.yaml
and repositories.yaml, and every subproject entry carries parent_project and
parent_slug, so consumers can group SBOMs without heuristics. parent_slug is the
subproject's parent folder in the bucket; it equals the parent's projects[].slug
whenever parent_project is known.
cd util/generate-index
go run . ../..Moves existing subproject SBOMs from the legacy parent folder (the parent's
repository name) to the parent's project slug, e.g.
argo-cd/argo-workflows/… → argo/argo-workflows/… and
aeraki/meta-protocol-proxy/… → aeraki-mesh/meta-protocol-proxy/….
Dry-run by default: it lists the subproject bucket (read-only) and prints the
plan (MOVE, CONFLICT, AMBIGUOUS, UNKNOWN) plus a per-folder summary. Only
--apply changes the bucket: each object is copied server-side, the copy is
verified (size and ETag), and only then is the old key deleted. Keys whose target
already exists, and legacy folders that cannot be attributed to exactly one
project, are reported and left in place.
# Print the plan (default; credentials from the environment or .env.sbom)
./util/migrate-subproject-keys.sh
# Review a saved key listing offline
./util/migrate-subproject-keys.sh --listing keys.txt
# Perform the moves (maintainers only, after reviewing the plan)
./util/migrate-subproject-keys.sh --applyScript to upload local SBOM files to OCI S3 buckets. Supports both OCI CLI and S3-compatible auth modes.
# Using S3 auth (recommended for CI)
./util/ingest-sbom-oci.sh \
--auth-mode s3 \
--s3-endpoint https://axtwf1hkrwcy.compat.objectstorage.us-sanjose-1.oraclecloud.com \
--s3-access-key "$ACCESS_KEY" \
--s3-secret-key "$SECRET_KEY"
# Dry run to preview uploads
./util/ingest-sbom-oci.sh --dry-run
# Force overwrite existing objects
./util/ingest-sbom-oci.sh --forceCredentials can also be provided via environment variables or a .env.sbom / .env file.
Generates the repository's own tooling SBOM plus a second SPDX document for the GitHub Actions and downloaded tools that form the SBOM generation chain.
# Full output (requires waybill in PATH)
./util/generate-tooling-sbom.sh
# Only regenerate the CI/generation-chain SBOM
./util/generate-tooling-sbom.sh --skip-repo-sbom
# Write directly into the tracked repo snapshot directory
./util/generate-tooling-sbom.sh --output-dir tooling-sbom- git
- GitHub CLI (gh)
- jq (for bash script)
- yq
- Waybill (auto-downloaded by the script, or install manually)
Note: Go is not required for SBOM generation (Waybill is a precompiled Rust binary). If Go is installed, the local script writes
sbom/index.jsonwithutil/generate-index.
The script reads the same repository lists as the scheduled workflow
(cncf-projects.yaml, repositories.yaml, discovered-repos.yaml) and shares its
release selection, release-asset import, Waybill flags and post-processing via
util/sbom-lib.sh. Unlike the workflow it skips releases that already exist as a
local file, not in the bucket. A cached Waybill in .local/bin is replaced if it is not
WAYBILL_VERSION.
# Process all repositories
./util/generate-sbom-local.sh
# Process specific repo
./util/generate-sbom-local.sh coredns/coredns
# One specific release, regenerated
./util/generate-sbom-local.sh --force --tag v0.10.0 kagent-dev/kagent
# Only discovered subprojects (written to sbom/subprojects/<owner>/<repo>/...)
./util/generate-sbom-local.sh --source discovered argoproj/argo-workflows
# Set max releases per repo (default: 3)
MAX_RELEASES=5 ./util/generate-sbom-local.shA thin wrapper around the bash script; needs bash from Git for Windows or WSL.
# Process all projects
.\util\generate-sbom-local.ps1
# Process specific repo
.\util\generate-sbom-local.ps1 -ProjectFilter "coredns/coredns"
# Force regenerate
.\util\generate-sbom-local.ps1 -Force -ProjectFilter "coredns/coredns"
# Set max releases per repo
.\util\generate-sbom-local.ps1 -MaxReleases 5| Variable | Description |
|---|---|
GH_TOKEN or GITHUB_TOKEN |
GitHub token for API access (recommended for higher rate limits) |
MAX_RELEASES |
Maximum releases to process per repo (default: 3, bash only) |
WAYBILL_VERSION |
Waybill release version to use (default: v0.10.0) |
bash util/tests/sbom-lib.sh # folder slugs, release import, plan + worker end to end
bash util/tests/postprocess-spdx.sh # SPDX post-processing
bash util/tests/postprocess-cdx.sh # CycloneDX post-processing
bash util/tests/postprocess-spdx3.sh # SPDX 3 post-processing
bash util/tests/migrate-subproject-keys.sh # migration plan (stubbed aws, no bucket access)
bash util/tests/s3-upload.sh
bash util/tests/prepare-project-matrix.sh
bash util/tests/generate-sandbox-sbom.sh
python3 -m unittest discover -s util/tests -p test_sandbox_applications.py
(cd util/discover-repos && go test ./...)
(cd util/generate-index && go test ./...)CNCF projects are automatically synced from the official CNCF Landscape.
Projects with the following status are included:
graduatedincubatingsandbox
Note: You do not need to manually add projects. The sync workflow runs daily and updates the project list automatically. If a project is missing, ensure it is properly listed in the CNCF Landscape with the correct project status.
The generator only processes stable releases:
Included:
- Full releases (e.g., v1.0.0, v2.5.3)
- Releases marked as non-prerelease and non-draft in GitHub
Excluded:
- Alpha releases (e.g., v1.0.0-alpha.1)
- Beta releases (e.g., v1.0.0-beta.2)
- Release candidates (e.g., v1.0.0-rc1)
- Development versions (e.g., v1.0.0-dev)
- Snapshots, nightly, canary builds
- Draft releases
The pre-release check only matches at the end of the tag (v1.0.0-rc.1, v2-preview), so
names like chart-test-tools-1.0.0 stay in. Repositories without GitHub releases fall back
to their newest semver tags. At most the newest 100 releases of a repository are inspected.
Generated SBOMs are in SPDX 2.3 JSON format, containing:
- Package information with real dependency relationships (not flat fan-out)
- SHA-256 content hashes on every component
- License information enriched via deps.dev (
licenseDeclared) and ClearlyDefined (licenseConcluded; opt-in flag--clearly-definedsince Waybill 0.10) - Dependency relationships extracted from lockfiles (go.sum, Cargo.lock, package-lock.json, etc.)
- All lifecycle scopes are kept; build/dev/test-only dependencies are flagged, not dropped: typed
BUILD_DEPENDENCY_OF/DEV_DEPENDENCY_OF/TEST_DEPENDENCY_OFrelationships plus awaybill:lifecycle-scopeannotation on the package. Filter with--exclude-scopein Waybill or by relationship type downstream - Document-level quality annotations (
waybill:graph-completeness,waybill:go-transitive-coverage,waybill:unresolved-declared-dep-count) say what the scanner could not determine - Per-component evidence blocks with confidence scoring
Each SBOM includes:
- SPDX 2.3 document information
- Creator:
Tool: waybill-0.10.0, plus the scanned source (source: git:https://github.com/<owner>/<repo>.git#<tag>) - Scope annotation (manifest vs artifact SBOM)
- Per-component
waybill:sbom-tierannotations (source, deployed, analyzed, etc.)
Every SBOM is passed through util/postprocess-spdx.sh before it is uploaded, on
every path that writes to a bucket (generate-sbom.yml via util/sbom-worker.sh,
ingest-sbom-oci.sh/migrate-sboms-to-oci.yml, generate-sandbox-sbom.sh, and the
local scripts). It uses only the repository, the release tag and the CNCF landscape
project name, and guarantees:
| Field | Value | Example |
|---|---|---|
name |
<owner>/<repo> <tag> (never a temporary checkout name) |
kagent-dev/kagent v0.10.0 |
root package downloadLocation |
git+https://github.com/<owner>/<repo>.git@<tag> |
git+https://github.com/kagent-dev/kagent.git@v0.10.0 |
| root package purl | pkg:github/<owner>/<repo>@<tag> (owner/repo lowercased, tag percent-encoded); replaces Waybill's pkg:generic/<owner>%2F<repo> purl |
pkg:github/kagent-dev/kagent@v0.10.0 |
root package supplier |
Organization: <project>, for subprojects the parent project; NOASSERTION if no project is known, never the SBOM tool |
Organization: Argo |
root package name / versionInfo |
<owner>/<repo> / <tag> (release assets otherwise name a tarball, binary or image here, e.g. flux_2.9.6_source_code.tar.gz / sha256:…) |
kagent-dev/kagent / v0.10.0 |
The root package is the one the document DESCRIBES. A project name containing
parentheses gets an empty contact, e.g. Organization: Open Policy Agent (OPA) (),
because SPDX tools otherwise read (OPA) as an e-mail address. documentNamespace
is not changed and stays unique per document; everything else (packages,
relationships, creators, annotations) is left as Waybill or the project's tool produced it. The script is
idempotent and can also be run on already published documents
(util/postprocess-spdx.sh --project <name> <file>; owner, repo and tag are then
taken from the root package).
Many projects ship SBOMs with their GitHub release. Those are imported instead of or in
addition to our own Waybill scan. Consumers must key on the fields the post-processing
guarantees (document name, root purl pkg:github/<owner>/<repo>@<tag>, root
downloadLocation, root name/versionInfo, supplier) and on the bucket key — never
on creationInfo.creators, documentNamespace or package counts, which differ by tool.
How assets are picked (sbom_import_release_assets in util/sbom-lib.sh):
- Candidates are JSON assets up to 100 MB whose name contains
spdx,cdx,cyclonedxorbom. At most 12 are downloaded, project-wide names first, then by size. The format is detected from the content, not the name: SPDX 2.x, SPDX 3 (@contextwithspdx.org/rdf/3), CycloneDX (bomFormat). - Project-wide asset (name without an OS/arch token such as
linux,amd64,arm64,windows): stored as<slug>_<ver>_spdx.json/_spdx3.json/_cdx.json. For SPDX 2.x this replaces the Waybill scan. Examples:flux_2.9.6_sbom.spdx.json(syft over the source tarball),k6-v1.8.2-spdx.json, trivy'sbom.json(CycloneDX). - Per-binary / per-image asset (e.g.
cosign-linux-amd64_3.1.3_linux_amd64.sbom.json,otelcol-contrib_…_linux_arm64.tar.gz.sbom.json,kubectl-vela-…-linux-amd64.spdx.sbom.json): the largest one is stored as<slug>_<ver>_release_spdx.json(or_release_cdx.json,_release_spdx3.json) next to the Waybill scan, which still runs. Such SBOMs describe one build for one platform: runtime modules only, no build/test scopes, no source-tree view. They are useful as the project's own statement, not as the project SBOM. - Every imported SPDX 2.x document (
_spdx.jsonand_release_spdx.json) goes throughutil/postprocess-spdx.sh, so the root package is<owner>/<repo>at<tag>with apkg:githubpurl, gitdownloadLocationand the CNCF project as supplier. If the asset is not a valid SPDX 2.x document with aDESCRIBESroot, it is dropped and Waybill generates instead. - Every imported CycloneDX document (
_cdx.json,_release_cdx.json) goes throughutil/postprocess-cdx.sh:metadata.component.name/versionbecome<owner>/<repo>/<tag>, avcsexternal reference points at the repository andmetadata.supplieris the CNCF project. The component purl is kept (CycloneDX allows one; the tool'spkg:golang/...carries ecosystem information). BOMHort derives the SBOM name frommetadata.component, so imported CycloneDX shows up as<owner>/<repo> <tag>too. - Every imported SPDX 3 JSON-LD document (
_spdx3.json,_release_spdx3.json) goes throughutil/postprocess-spdx3.sh:SpdxDocument.namebecomes<owner>/<repo> <tag>, the root package(s) (rootElement, elsedescribestargets) getname,software_packageVersion, gitsoftware_downloadLocationand asuppliedByOrganization agent. BOMHort reads SPDX 3 through protobom, which takes the display name fromSpdxDocument.name.
Known differences and limits of imported SBOMs:
| Topic | Waybill scan | Imported release SBOM |
|---|---|---|
| Licenses | licenseDeclared from deps.dev, licenseConcluded from ClearlyDefined |
depends on the tool; syft sets licenseConcluded, usually no licenseDeclared |
| Scopes | all scopes, flagged via *_DEPENDENCY_OF + waybill:lifecycle-scope |
usually runtime only, no scope flags |
| Quality annotations | waybill:graph-completeness etc. |
none |
| CycloneDX assets | – | root component normalised (name, version, supplier, vcs reference); purl stays the tool's, e.g. trivy pkg:golang/github.com/aquasecurity/trivy@v0.76.0?goos=linux&goarch=amd64; no pkg:github purl |
| SPDX 3 assets | – | document name and root package(s) normalised (name, version, software_downloadLocation, suppliedBy); no purl added |
Several components per release (kubevela: sbom-vela-cli + sbom-vela-core) |
one SBOM for the repo | only the largest is kept |
Tag-value SPDX (istio istio-release.spdx), tarballs (argo-cd sbom.tar.gz), XML CycloneDX |
– | ignored, Waybill generates |
If your project attaches an SBOM to its GitHub release, this pipeline prefers it over its own scan. To make sure it is picked up and useful:
- Publish one project-wide SPDX 2.3 JSON document that describes the release as a
whole (source tree or source tarball), e.g.
<project>_<version>_sbom.spdx.json. Per-binary SBOMs (…_linux_amd64.sbom.json) are welcome in addition, but they are stored as*_release_spdx.jsonand do not replace the generated project SBOM. - Name the asset so it is recognised: the name must contain
spdx,cdx,cyclonedxorbomand end in.json.sbom.tar.gz, tag-value.spdxand XML are ignored. Keep the project-wide asset free of OS/arch tokens (linux,darwin,windows,amd64,arm64,x86_64, …) — that is how it is told apart from per-binary files. - Keep it under 100 MB and make it valid JSON with a
DESCRIBESroot (documentDescribesor aSPDXRef-DOCUMENT DESCRIBES …relationship). Documents without a described root are dropped and the pipeline scans instead. - SPDX 2.x or CycloneDX JSON, both are normalised and consumed. SPDX 2.x gets the
fuller treatment (
pkg:githubpurl,downloadLocation) and is the format our own scans use, so prefer it when you have the choice. SPDX 3 JSON-LD is accepted and normalised too. - Include what the scanner cannot see:
licenseDeclaredas well aslicenseConcluded, checksums per package, and — if your tool supports it — build/test dependencies typed asBUILD_DEPENDENCY_OF/TEST_DEPENDENCY_OFrather than dropped. A source-tree scan (syft dir:.,waybill sbom scan --path .) gives a fuller picture than a scan of the shipped binary. - One SBOM per release, not per component, or at least one that covers the whole repository. If a release contains several components (CLI, controller, images), only the largest project-wide document is imported.
- Attach it to the GitHub release of the tag, not only to a container registry or a
separate
-sbomrelease; the importer looks atreleases/tags/<tag>only.
Good examples today: fluxcd/flux2 (flux_<version>_sbom.spdx.json, syft over the source
tarball) and grafana/k6 (k6-<version>-spdx.json).
If you encounter GitHub API rate limits:
- Set the
GH_TOKENenvironment variable with a valid GitHub token - Run
gh auth loginto authenticate the GitHub CLI
Waybill is a precompiled binary — no build step required:
# The local script auto-downloads Waybill if not found in PATH
./util/generate-sbom-local.sh
# Or install manually
curl -sL https://github.com/kusari-oss/waybill/releases/download/v0.10.0/waybill-v0.10.0-x86_64-unknown-linux-gnu.tar.gz | tar xz
sudo cp waybill-v0.10.0-x86_64-unknown-linux-gnu/waybill /usr/local/bin/Some repositories may have protected tags or require authentication. The script will skip these and continue with other releases.
deps.dev and ClearlyDefined enrichment runs inline. If network is slow, scans may take longer (2-5 minutes per repo is normal). Use --offline flag with Waybill to skip enrichment for faster but less complete SBOMs.
If uploads fail with MissingContentLength, ensure the AWS CLI version is up to date and the AWS_REQUEST_CHECKSUM_CALCULATION=when_required environment variable is set.
- Fork the repository
- Make your changes
- Test locally with the provided scripts
- Submit a pull request
For issues with specific CNCF projects, please contact the respective project maintainers directly.