The companion to #20, from the same question about SBOM signing.
Signing belongs in CI, not here — but an attestation binds an SBOM to an artefact externally. If the two are ever separated, or if someone wants to check that an SBOM corresponds to the revision it claims, the document itself has to carry enough to corroborate that. Ours carries almost nothing.
What we emit today
_metadata in src/icebergsca/report/cyclonedx.py:88-92:
"component": {
"type": "application",
"bom-ref": "root",
"name": report.root.name or "project",
},
That is the basename of the scanned directory. Nothing else. No version, no purl, no hashes, no VCS reference. Scan /tmp/checkout and the SBOM claims to describe a project called tmp; scan the same repo from two paths and you get two different names for the same thing.
So the SBOM cannot answer "which commit is this?", and there is nothing to check a claimed binding against. The metadata.component object is also where SBOM consumers look first to identify the subject, so the field is not merely thin, it is the wrong value.
What would fix it
CycloneDX has the slots already; we just do not populate them.
version on metadata.component — the project's own version, where a manifest declares one. The Maven parser already reads pom.xml's <version>, and pyproject.toml/package.json/Cargo.toml all carry one.
externalReferences with type: "vcs" and the commit, when the caller supplies it. A --project-version / --source-ref flag is the honest route: the tool should not shell out to git, and in CI the values are already in the environment as GITHUB_SHA and friends.
hashes on metadata.component, or per-file evidence, derived from the manifests and lockfiles actually parsed. ManifestResult.parsed (core/models.py) already tracks exactly that set, and it is the honest thing to hash: it says "this SBOM was derived from files with these digests", which is a claim the tool can actually back. There is no hashing anywhere in src/ today, so this is new but small — hashlib only, no dependency.
The last one is the most valuable and the most defensible. It is a statement about our own inputs rather than about an artefact we never saw, which keeps it inside what this tool can honestly assert.
Scope
Ordering
Best done after #20. Adding fields to metadata.component while the document is still non-reproducible means the identity fields land in a document whose digest changes every run anyway, which is most of the reason to have them.
The companion to #20, from the same question about SBOM signing.
Signing belongs in CI, not here — but an attestation binds an SBOM to an artefact externally. If the two are ever separated, or if someone wants to check that an SBOM corresponds to the revision it claims, the document itself has to carry enough to corroborate that. Ours carries almost nothing.
What we emit today
_metadatainsrc/icebergsca/report/cyclonedx.py:88-92:That is the basename of the scanned directory. Nothing else. No version, no purl, no hashes, no VCS reference. Scan
/tmp/checkoutand the SBOM claims to describe a project calledtmp; scan the same repo from two paths and you get two different names for the same thing.So the SBOM cannot answer "which commit is this?", and there is nothing to check a claimed binding against. The
metadata.componentobject is also where SBOM consumers look first to identify the subject, so the field is not merely thin, it is the wrong value.What would fix it
CycloneDX has the slots already; we just do not populate them.
versiononmetadata.component— the project's own version, where a manifest declares one. The Maven parser already readspom.xml's<version>, andpyproject.toml/package.json/Cargo.tomlall carry one.externalReferenceswithtype: "vcs"and the commit, when the caller supplies it. A--project-version/--source-refflag is the honest route: the tool should not shell out togit, and in CI the values are already in the environment asGITHUB_SHAand friends.hashesonmetadata.component, or per-file evidence, derived from the manifests and lockfiles actually parsed.ManifestResult.parsed(core/models.py) already tracks exactly that set, and it is the honest thing to hash: it says "this SBOM was derived from files with these digests", which is a claim the tool can actually back. There is no hashing anywhere insrc/today, so this is new but small —hashlibonly, no dependency.The last one is the most valuable and the most defensible. It is a statement about our own inputs rather than about an artefact we never saw, which keeps it inside what this tool can honestly assert.
Scope
metadata.component.versionfrom the project manifest where one declares it--project-versionand a source-reference flag, mapped toversionand avcsexternal reference; never invokedgitManifestResult.parsedreferences/json-report.md(tests/test_skill.pypolices top-level keys)tests/test_report_formats.pypicks this up for free oncesample_report()exercises itOrdering
Best done after #20. Adding fields to
metadata.componentwhile the document is still non-reproducible means the identity fields land in a document whose digest changes every run anyway, which is most of the reason to have them.