Skip to content

Component Hash: retain the digests five parsers already read and discard #26

Description

@richardmhope

Component Hash is one of four data fields added by CISA's 2026 Minimum Elements, tied to that revision's integrity theme: a consumer should be able to confirm they hold the exact artefact the SBOM describes. We emit none. Assessment in docs/cisa-sbom-minimum-elements-2026.md on branch claude/tool-sbom-requirements-assessment-jkudvt.

Unlike licences (#12), this needs no network access and no new I/O for most ecosystems. The digests are already in the files the parsers open, and are thrown away at the parser boundary — the same shape of loss as #17, where RawDependency.exclusions survived the XML parse and died one line later because Dependency had nowhere to put it.

Where the data already is

Ecosystem Source Cost
Rust Cargo.lock checksum — present in tests/fixtures/rust/basic/Cargo.lock today free
npm package-lock.json / pnpm-lock.yaml integrity, yarn.lock checksum free
Python uv.lock / poetry.lock file hashes, Pipfile.lock hashes free
Python requirements.txt --hash=sha256:…, currently stripped before parsing (ecosystems/python.py:48-51) free
.NET packages.lock.json contentHash free
Go go.sum — not in the file spec, which lists only go.mod (ecosystems/golang.py:158) small
Maven / Gradle Not in a POM at all; needs .sha1 fetches from Central network
Ruby Gemfile.lock records none unavailable

Five of seven ecosystems are reachable by retaining data we already parse. Go needs one new file in the spec. Maven needs network and should be deferred or made opt-in — MavenResolver already talks to Central, so it is possible, just not free.

Model note

Dependency (core/models.py:268) needs somewhere to hold these, and the None-versus-empty rule matters more here than usual, so state it in the field docstring:

  • None — this parser does not read hashes at all
  • empty — the file was read and records no hash for this package

Collapsing them lets a consumer read "we cannot verify this" as "verified". Note also that some lockfiles record several hashes per package (one per wheel or per platform), and that a hash of a source distribution and a hash of a wheel are not interchangeable — CycloneDX hashes[] takes a list of {alg, content}, so the shape is available, but which artefact each digest refers to should not be guessed at.

Multiple algorithms appear across formats (sha1, sha256, sha512, npm's base64 SRI form), so a small normalisation step to CycloneDX's alg vocabulary is part of the work.

Relationship to #21

Different hashes, both worth having. #21 proposes digesting the manifest and lockfile files we parsed, from ManifestResult.parsed — a claim about our own inputs. This issue is about the digests of the dependency artefacts, as recorded by the ecosystem's own tooling. Neither substitutes for the other, and they can land independently.

Scope

  • A hashes field on Dependency, distinguishing unread from absent
  • Populate from Cargo, npm/pnpm/yarn, uv/poetry/pipenv, requirements.txt --hash, and packages.lock.json
  • Add go.sum to the Go file spec, keeping go.mod as the graph source
  • Normalise algorithm names to the CycloneDX alg vocabulary; drop anything unrecognised rather than guessing
  • hashes[] on CycloneDX components; JSON report fields documented in references/json-report.md
  • Fixtures keep real filenames per the house rule, and must include a lockfile that records no hashes, so the empty case is covered
  • Maven left explicitly out of the first cut, with the reason recorded

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions