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
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.mdon branchclaude/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.exclusionssurvived the XML parse and died one line later becauseDependencyhad nowhere to put it.Where the data already is
Cargo.lockchecksum— present intests/fixtures/rust/basic/Cargo.locktodaypackage-lock.json/pnpm-lock.yamlintegrity,yarn.lockchecksumuv.lock/poetry.lockfile hashes,Pipfile.lockhashesrequirements.txt--hash=sha256:…, currently stripped before parsing (ecosystems/python.py:48-51)packages.lock.jsoncontentHashgo.sum— not in the file spec, which lists onlygo.mod(ecosystems/golang.py:158).sha1fetches from CentralGemfile.lockrecords noneFive 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 —
MavenResolveralready talks to Central, so it is possible, just not free.Model note
Dependency(core/models.py:268) needs somewhere to hold these, and theNone-versus-empty rule matters more here than usual, so state it in the field docstring:None— this parser does not read hashes at allCollapsing 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'salgvocabulary 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
Dependency, distinguishing unread from absentrequirements.txt--hash, andpackages.lock.jsongo.sumto the Go file spec, keepinggo.modas the graph sourcealgvocabulary; drop anything unrecognised rather than guessinghashes[]on CycloneDX components; JSON report fields documented inreferences/json-report.md