Skip to content

The SBOM does not say which source it describes #21

Description

@richardmhope

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

  • Populate metadata.component.version from the project manifest where one declares it
  • --project-version and a source-reference flag, mapped to version and a vcs external reference; never invoked git
  • Digests of the parsed manifests and lockfiles, from ManifestResult.parsed
  • Mirror whatever lands into the JSON report so the two formats agree, documented in references/json-report.md (tests/test_skill.py polices top-level keys)
  • Re-validate against the vendored CycloneDX schema — tests/test_report_formats.py picks this up for free once sample_report() exercises it

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions