Skip to content

feat: detect component hash changes — supply-chain tampering signal - #69

Merged
dmchaledev merged 2 commits into
mainfrom
feat/hash-integrity
Aug 7, 2026
Merged

feat: detect component hash changes — supply-chain tampering signal#69
dmchaledev merged 2 commits into
mainfrom
feat/hash-integrity

Conversation

@dmchaledev

Copy link
Copy Markdown
Contributor

Closes #22

Problem

Component.hashes was declared in the type but never parsed, compared, or rendered — a dependency re-published under the same name@version (the event-stream / xz class of attack) produced zero output.

Fix

  • Parser: extract CycloneDX hashes[] (alg/content) and SPDX checksums[] (algorithm/checksumValue) into a normalized {algorithm: digest} map (lowercased for cross-generator comparison)
  • diff(): report a HashChange when a component's version is unchanged but a digest differs; version bumps are already reported as upgrades so their hash changes aren't double-counted
  • Reporter: Hash changes section + summary metric in text and markdown

Verification

  • 4 new tests: CycloneDX hash parsing, SPDX checksum parsing, diff detection, upgrade-exclusion
  • 110 tests pass, tsc clean

Hermes Agent added 2 commits August 7, 2026 13:23
…report

Closes #21

parse() previously accepted any JSON — a package.json passed by mistake,
a truncated export, or garbage — and silently returned an empty CycloneDX
SBOM. In a CI gate that read 'nothing changed' (a false negative).

- parse() now throws ParseError when input is not a recognized CycloneDX
  or SPDX document (missing bomFormat/spdxVersion), is invalid JSON, or
  is not an object (array/null/primitives)
- The CLI's loadSbom already wraps this in a clear 'Failed to parse'
  message; main() exits 1 (verified)
- 6 new tests cover the rejection paths + valid-document acceptance

106 tests pass, tsc clean.
Closes #22

Component.hashes was declared but never parsed, compared, or rendered —
a dependency re-published under the same name@version (event-stream / xz
class attack) produced zero output.

- Parser: extract CycloneDX hashes[] (alg/content) and SPDX checksums[]
  (algorithm/checksumValue) into a normalized {algorithm: digest} map
- diff(): report HashChange when a component's version is unchanged but a
  digest differs; a version bump is already reported as an upgrade so its
  hash change is not double-counted
- Reporter: Hash changes section + summary metric in text and markdown
- 4 new tests (parser both formats, diff detection, upgrade-exclusion)

110 tests pass, tsc clean.
@dmchaledev
dmchaledev merged commit 928f710 into main Aug 7, 2026
2 checks passed
@dmchaledev
dmchaledev deleted the feat/hash-integrity branch August 7, 2026 19:32
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.

Detect component hash/integrity changes in diff (Component.hashes is declared but never parsed or compared)

1 participant