Skip to content

fix(diff): detect version upgrades for version-qualified purls#31

Open
dmchaledev wants to merge 1 commit into
mainfrom
claude/magical-ptolemy-5c3oux
Open

fix(diff): detect version upgrades for version-qualified purls#31
dmchaledev wants to merge 1 commit into
mainfrom
claude/magical-ptolemy-5c3oux

Conversation

@dmchaledev

Copy link
Copy Markdown
Contributor

Problem

Upgrade detection — the tool's headline feature ("Highlights added, removed, upgraded dependencies") — never triggered for real-world SBOMs.

buildComponentMap used the raw purl as the component match key:

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

But a purl embeds the version (pkg:npm/lodash@4.17.21). CycloneDX and SPDX tooling almost always emits version-qualified purls, so a simple version bump produced two different keys in the two SBOMs and was reported as a spurious remove + add. The upgraded array only ever populated in the rare case where a component had no purl at all and was matched by bare name.

The existing test even documented the broken behavior as intended:

// Different purl = treated as add/remove (purl includes version)
// This is correct behavior — different purls are different packages
expect(report.added.length + report.removed.length + report.upgraded.length).toBeGreaterThan(0);

Fix

Introduce a version-independent identity key:

  • componentKey() — uses the purl identity when present, else an ecosystem-qualified name.
  • purlIdentity() — strips the @version segment while preserving any trailing ?qualifiers / #subpath.

Scoped npm purls resolve correctly because their leading @ is percent-encoded (%40), so the version's @ is the first literal @ in the string.

Before / after

Same two SBOMs where lodash and react are bumped and momentexpress swapped:

Added Removed Upgraded
Before 3 3 0
After 1 1 2 (react [MAJOR])

Tests

  • Rewrote the misleading detects version upgrades test to assert a single upgrade (0 added, 0 removed).
  • Added coverage for scoped npm purls (%40angular/core) and purls carrying ?arch= qualifiers.
  • Full suite green (31 tests), lint and typecheck clean.

🤖 Generated with Claude Code


Generated by Claude Code

…e detected

buildComponentMap keyed on the raw purl, which embeds the version
(e.g. "pkg:npm/lodash@4.17.21"). Real-world CycloneDX/SPDX SBOMs almost
always emit version-qualified purls, so an upgrade produced two distinct
keys and was reported as a spurious remove + add — the `upgraded` array
only ever populated for the rare purl-less, name-matched case. This
silently broke the tool's headline feature (upgraded-dependency
detection).

Introduce componentKey()/purlIdentity(): strip the "@Version" segment
from the purl while preserving any ?qualifiers/#subpath, and fall back to
an ecosystem-qualified name when no purl is present. Scoped npm purls
(leading @ encoded as %40) resolve correctly since the version "@" is the
first literal "@".

Update the misleading test that asserted the broken behavior and add
coverage for scoped and qualifier-bearing purls.

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