Skip to content

v0.12 item 3: an SBOM of the wheel, measured from the wheel - #210

Merged
rohanrkamath merged 2 commits into
mainfrom
v0.12/3-sbom
Sep 14, 2026
Merged

rohanrkamath merged 2 commits into
mainfrom
v0.12/3-sbom

Conversation

@arpanghoshal

Copy link
Copy Markdown
Member

I filed this behind "when a design partner asks", beside v1.0's EU controls pack. That was wrong, and the two are not the same kind of thing.

A controls pack is prose about clause mappings, written on request. An SBOM is a build artifact generated mechanically from data this repository already holds, produced at release time from the artifact itself. Deferring it means either retrofitting one onto tags already published or shipping releases that never had one.

It also clears the standards rule without argument. ROADMAP.md: integrate first, map second, never claim compliance. A bill of materials asserts conformance with nothing. It is a measurement.

From the built wheel, not from the manifest

scripts/sbom.sh installs the wheel into an empty environment and records what resolves. A manifest-derived SBOM would be this repository's opinion of its own dependencies, and the point of the document is to be checkable against reality, which is the rule release verification already follows everywhere else here.

root: ctrlrun 0.11.0  (library, Apache-2.0)
components: [('PyYAML', '6.0.3'), ('click', '8.5.0')]

Two runtime dependencies, and test_core_declares_only_pyyaml_and_click pins that from the other side.

pip is uninstalled before the scan, and that is not cosmetic

python -m venv seeds pip, and a scanner reading that environment cannot tell "ctrlrun needs this" from "the venv came with this". The first draft produced exactly that:

components: [('PyYAML', '6.0.3'), ('click', '8.5.0'), ('pip', '26.2.1')]

An SBOM listing pip is wrong in the direction that matters: it overstates what a consumer is taking on, and nothing about a padded SBOM looks broken. tests/test_sbom.py asserts the component list is exactly {PyYAML, click} by equality and not containment, so a future Python that seeds a venv with something else fails the suite rather than shipping a wrong document. Four assertions in all: the component set, the root's name/type/licence, that it is CycloneDX with a spec version, and that every component carries a version and a purl (a scanner matching on names alone is guessing).

Where it runs

  • CI, every pull request — generated the same way the release generates it, so a change that would ship a wrong one goes red on the PR rather than at the tag.
  • release.yml — written into dist/ before the attestation step, so it is signed along with the distributions and attached to the GitHub release. An unsigned inventory of a signed artifact is the weakest link in the pair.

cyclonedx-bom is hash-pinned in its own requirements/sbom.txt via scripts/lock.sh, like every other tool CI installs.

One thing it does not buy

Not a Scorecard check. I pulled the live API rather than assuming: SBOM is not among the eighteen scored for this repository, so this does nothing for the 9.1. It is worth doing anyway, for the audience that actually reads one.

Gate

All checks passed!            (ruff, ruff format, mypy --strict)
4591 passed, 6 warnings in 172.14s

No docs pairing: this adds no page and changes no generated reference.

🤖 Generated with Claude Code

I filed this behind "when a design partner asks", beside v1.0's EU controls
pack. That was wrong, and the two are not the same kind of thing. A controls
pack is prose about clause mappings, written on request. An SBOM is a build
artifact generated mechanically from data this repository already holds, and it
is produced at release time from the artifact itself, so deferring it means
either retrofitting one onto tags already published or shipping releases that
never had one.

It also clears the standards rule without argument: `integrate first, map
second, never claim compliance`. A bill of materials asserts conformance with
nothing. It is a measurement.

**From the built wheel, not from `pyproject.toml`.** `scripts/sbom.sh` installs
the wheel into an empty environment and records what resolves. A
manifest-derived SBOM would be this repository's opinion of its own
dependencies, and the point of the document is to be checkable against reality,
which is the same rule release verification already follows everywhere else
here.

**pip is uninstalled before the scan.** `python -m venv` seeds it, and a scanner
reading that environment cannot tell "ctrlrun needs this" from "the venv came
with this". An SBOM listing pip is wrong in the direction that matters: it
overstates what a consumer is taking on, and nothing about a padded SBOM looks
broken. `test_sbom.py` asserts the component list is **exactly** `PyYAML` and
`click`, by equality and not containment, so a future Python that seeds a venv
with something else fails the suite rather than shipping a wrong document.

The result is worth having for what it says: two runtime dependencies, and
`test_core_declares_only_pyyaml_and_click` pins that from the other side.

CI generates and checks it on every pull request, so a change that would ship a
wrong one goes red on the PR rather than at the tag. `release.yml` writes it
into `dist/` **before** the attestation step, so it is signed along with the
distributions: an unsigned inventory of a signed artifact is the weakest link in
the pair.

Not a Scorecard check. I checked the live API rather than assuming: SBOM is not
among the eighteen scored for this repository, so this buys nothing on the 9.1
and is worth doing anyway.

Signed-off-by: arpan <contact@arpanghoshal.com>
@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 24 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: dffe460c-bc55-4840-afbc-a1a982bd4245

📥 Commits

Reviewing files that changed from the base of the PR and between 5c736b4 and 802cce7.

📒 Files selected for processing (7)
  • .github/workflows/ci.yml
  • .github/workflows/release.yml
  • requirements/in/sbom.in
  • requirements/sbom.txt
  • scripts/lock.sh
  • scripts/sbom.sh
  • tests/test_sbom.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

The SBOM step went red on its first real run with
`['PyYAML', 'click', 'setuptools']`. Python 3.12 seeds only pip into a venv and
3.11 seeds setuptools as well, so the version I developed against was the one
that hid it.

That is the guard doing its job rather than a slip getting through. The
assertion compares by equality and not containment precisely so a padded SBOM
fails instead of shipping, and the thing it caught is a document that would have
told a consumer ctrlrun depends on setuptools.

`setuptools` and `wheel` go first, then pip, because pip cannot uninstall
anything once it has removed itself. Verified on both interpreters: 3.11 and
3.12 now produce the same two components.

Signed-off-by: arpan <contact@arpanghoshal.com>
@rohanrkamath
rohanrkamath merged commit 4634fbd into main Sep 14, 2026
16 checks passed
@rohanrkamath
rohanrkamath deleted the v0.12/3-sbom branch September 14, 2026 23:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants