Skip to content

fix(diff): detect upgrades by stripping version from purl key#47

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

fix(diff): detect upgrades by stripping version from purl key#47
dmchaledev wants to merge 1 commit into
mainfrom
claude/magical-ptolemy-rad2bc

Conversation

@dmchaledev

Copy link
Copy Markdown
Contributor

Problem

The headline "upgraded dependencies" feature is broken for essentially every real-world SBOM.

buildComponentMap in src/diff.ts keyed each component on its raw purl:

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

But a purl embeds the versionpkg:npm/lodash@4.17.21. Since tools like syft, trivy, cdxgen, etc. emit a purl for virtually every component, an upgrade of lodash 4.17.20 → 4.17.21 gets two different keys and is misreported as one removal + one addition, never as an upgrade. Upgrade detection only worked for the rare components with no purl.

The previous test even enshrined the broken behavior as "correct":

// Different purl = treated as add/remove (purl includes version)
// This is correct behavior — different purls are different packages

Fix

Key components on a version-independent identity by stripping the version segment from the purl. The version follows the first literal @; a namespace scope like @angular is percent-encoded as %40, so the scope is untouched. Qualifiers (?...) and subpath (#...) are preserved so components differing only by those stay distinct. Falls back to the component name when no purl is present.

Verification

Before this change, an SBOM pair with purl-bearing components reported Upgraded: 0 (all changes shown as add/remove). After:

Summary:
  Added:       0
  Removed:     0
  Upgraded:    2
↑ Upgraded Components:
  ~ lodash: 4.17.20 → 4.17.21
  ~ @angular/core: 15.2.0 → 16.0.0 [MAJOR]
  • Rewrote the misleading purl-upgrade test to assert a single upgrade is detected.
  • Added a test covering a scoped (%40-encoded) purl with a major bump, guarding the version-stripping edge case.
  • Full suite (30 tests), typecheck, and lint all pass.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VKLSy7zqzDs5ztAf6jtKqw


Generated by Claude Code

The component map was keyed on the raw purl, which embeds the version
(e.g. "pkg:npm/lodash@4.17.21"). Since virtually every real-world SBOM
component carries a purl, the tool's headline "upgraded dependencies"
detection never fired: an upgrade was misreported as an unrelated
remove + add.

Key components on a version-independent identity by stripping the
version segment from the purl. Namespace scopes ("@angular", encoded as
%40) and qualifiers/subpath are preserved. Falls back to the name when
no purl is present.

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