Skip to content

fix(diff): detect upgrades for versioned purls (headline feature was broken for real SBOMs) - #57

Open
dmchaledev wants to merge 1 commit into
mainfrom
claude/magical-ptolemy-17j9ne
Open

fix(diff): detect upgrades for versioned purls (headline feature was broken for real SBOMs)#57
dmchaledev wants to merge 1 commit into
mainfrom
claude/magical-ptolemy-17j9ne

Conversation

@dmchaledev

Copy link
Copy Markdown
Contributor

Summary

Upgrade detection — a headline feature of this tool ("Highlights added, removed, upgraded dependencies") — is broken for essentially every real-world SBOM. A simple patch bump is reported as a removal plus an addition, and the upgraded list stays empty.

Root cause

buildComponentMap in src/diff.ts keyed each component by its full purl:

const key = comp.purl ?? comp.name;

But a purl embeds the version (pkg:npm/lodash@4.17.21). Real SBOM generators (syft, cdxgen, Trivy, …) always emit versioned purls, so lodash@4.17.20 and lodash@4.17.21 get different keys — the old one looks removed, the new one looks added, and no upgrade is ever detected. Upgrades were only found in the rare case where components had no purl and matched by name (see the existing name-fallback test).

Reproduction (before)

$ sbom-diff old.json new.json   # lodash 4.17.20 -> 4.17.21, both with purls
Summary:
  Added:       1
  Removed:     1
  Upgraded:    0      # ← should be 1

Fix

Derive component identity from the version-independent purl coordinates — strip the @version suffix (plus any ?qualifiers / #subpath), falling back to the component name when no purl is present. Per the purl spec any literal @ in a name is percent-encoded, so scoped npm packages like pkg:npm/%40angular/core@16.0.0 stay correct.

After the fix the same input reports Upgraded: 1 with 4.17.20 → 4.17.21, and add/remove/major-bump detection is unchanged.

Changes

  • src/diff.ts — new componentKey / purlWithoutVersion helpers; buildComponentMap keys on version-independent identity.
  • src/__tests__/diff.test.ts — replaced the test that documented the old broken behavior with tests asserting upgrade detection for versioned purls, purls with qualifiers, and scoped npm purls (major bump).
  • CHANGELOG.mdFixed entry under [Unreleased].

Testing

  • npm test — 40 passing (was 38)
  • npm run lint — clean
  • npm run build — clean

Notes / scope

Kept intentionally focused. When a single SBOM legitimately lists two versions of the same package, version-independent keys make them collapse (last-write-wins) — that is the separate concern already tracked in #50 and is out of scope here.


Generated by Claude Code

Component identity was keyed on the full purl, which embeds the version
(e.g. pkg:npm/lodash@4.17.21). Real SBOM generators (syft, cdxgen, Trivy)
always emit versioned purls, so a simple version bump was reported as a
removal + addition and the `upgraded` list stayed empty — defeating the
headline "version upgrades" feature for essentially every real input.

Derive component identity from the version-independent purl coordinates
(stripping @Version plus any ?qualifiers/#subpath), falling back to name
when no purl exists. Scoped npm purls stay correct because the spec
percent-encodes any literal @ in the name.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DZRdkf7mAryZDiNcsAG1n4
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.

2 participants