Share an SBOM
The request arrives in a security questionnaire or a procurement thread and it is always the same sentence: "Please provide an SBOM for the product."
Generating one is the easy half:
# Syft — from an image, a directory, or an archive
syft myapp:1.4.2 -o cyclonedx-json=sbom.cdx.json
syft myapp:1.4.2 -o spdx-json=sbom.spdx.json
# Or from the build system
cyclonedx-npm --output-file sbom.cdx.json
mvn cyclonedx:makeAggregateBom
Delivering it is where it goes wrong. CycloneDX and SPDX are interchange formats — they are meant to be consumed by tools. What you have is a several- megabyte JSON file, and what the person on the other end has is Outlook.
So it gets emailed, stripped by an attachment filter, re-sent as a zip, and finally opened in Notepad by somebody who wanted to check one dependency.
Render it, then publish both
# CycloneDX has a first-party converter with an HTML target
cyclonedx-cli convert --input-file sbom.cdx.json \
--output-file sbom.html --output-format html
# or, format-agnostic
pip install sbom2doc
sbom2doc --input-file sbom.cdx.json --format html --output sbom.html
Publish the rendered page as the report body and attach the raw SBOM to the same report as an asset. Drag both into the app, or:
curl -fsS -X POST "https://commareports.com/api/v1/reports" \
-H "Authorization: Bearer $COMMA_API_TOKEN" \
-H "Content-Type: application/json" \
-d "$(jq -n --rawfile html sbom.html \
--arg t "SBOM — myapp 1.4.2 (CycloneDX 1.5)" '{title: $t, html: $html}')"
Then upload sbom.cdx.json and sbom.spdx.json as assets, so the reader can
read the table and their tooling can ingest the original from the same place.
That combination — legible for the human, machine-readable for the pipeline —
is what the request actually wanted.
Set access before you send it
An SBOM is a complete, versioned inventory of everything you ship. It is exactly what a customer's security team needs, and exactly what somebody scanning for unpatched versions would like to have.
Reports are private by default. For a customer, domain-gated access
(anyone at customer.com) is usually the right shape: no account
administration for you, no shared password, and it stops working for people who
leave. For an auditor, named reviewers. See the
sharing model.
Keep it current from the release pipeline
syft "$IMAGE" -o cyclonedx-json=sbom.cdx.json
cyclonedx-cli convert --input-file sbom.cdx.json \
--output-file sbom.html --output-format html
curl -fsS -X PATCH "https://commareports.com/api/v1/reports/$REPORT_ID" \
-H "Authorization: Bearer $COMMA_API_TOKEN" \
-H "Content-Type: application/json" \
-d "$(jq -n --rawfile html sbom.html \
--arg t "SBOM — myapp $VERSION" '{title: $t, html: $html}')"
This is the part that changes the economics. An emailed SBOM is stale the day after you send it, so the request comes back every quarter. A PATCHed report id means the URL you gave the customer in January still resolves to the current build in October, and the revision list is the version history — which is itself a control that several compliance frameworks want evidence of.
What review adds
- Anchored threads on a specific component, so "yes, that log4j version is shaded and unreachable" is answered once, on the record.
- Revisions, so "which version were we shipping in March?" has an answer.
- Domain-gated or named access, so the inventory does not become public.
Limits
- Entry HTML: 5 MB. Assets: 25 MB per file, 250 MB and 500 files total. A rendered SBOM for a very large monorepo can exceed the entry limit — publish per-artifact SBOMs rather than one aggregate, which is better practice anyway.
- 60 requests/minute per token.
Try it
Comma is free — unlimited reports, unlimited commenters, unlimited revision history.