feat(diff): detect version downgrades instead of mislabeling them as upgrades#24
Open
dmchaledev wants to merge 1 commit into
Open
feat(diff): detect version downgrades instead of mislabeling them as upgrades#24dmchaledev wants to merge 1 commit into
dmchaledev wants to merge 1 commit into
Conversation
…upgrades diff() lumped every version change into the `upgraded` array, so a dependency moving backwards (e.g. 1.3.0 -> 1.1.0) was reported as a non-major "upgrade". For a supply-chain tool a rollback is itself a high-signal event: a return to a yanked or CVE-affected release, a pinning regression, or a dependency-confusion substitution. - types: add `VersionChange.isDowngrade` and `summary.totalDowngraded` (additive) - diff: compute direction with a lightweight segment comparator; a downgrade is never also reported as a major bump - reporter: render downgrades in a dedicated section (text + markdown) and split the summary count so upgrades and downgrades are counted separately - tests: cover rollback detection, forward-upgrade non-regression, the major-version rollback edge case, and both report renderers Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KPWdF3VnuxevNSR6iUwbQF
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
diff()lumps every version change into theupgradedarray, so a dependency that moves backwards (e.g.1.3.0 → 1.1.0) is reported as a non-major "upgrade." For a tool keywordedsupply-chain-security/vulnerability-management, a rollback is itself a high-signal event:Today every one of those renders identically to a routine patch bump. This PR makes downgrades a distinct, visible signal.
Evidence (current behavior)
src/diff.tspushes anyaComp.version !== bComp.versionchange intoupgraded; direction is never considered.isMajorVersionBump()returnsfalsefor2.0.0 → 1.0.0(sincetoMajor > fromMajoris false), so a rollback across a major boundary shows up as a plain "upgrade" with no flag at all.What changed
src/types.ts) — additive, backward compatible:VersionChange.isDowngrade: booleansummary.totalDowngraded: numbersrc/diff.ts) — direction is computed with a small dependency-free segment comparator (compareVersions). A downgrade is never also reported as a major bump.src/reporter.ts) — text and Markdown gain a dedicated "Downgraded Components" section; the summary splits upgrades vs. downgrades so the counts are honest.Example
Why this is a clean addition
Component.licenseis currently extracted but never compared) #9 (license) and Detect component hash/integrity changes in diff (Component.hashes is declared but never parsed or compared) #22 (hashes) add new arrays rather than touchingVersionChange.VersionChange/summary; no existing field or default CLI behavior changes.isDowngrade/totalDowngradedexist,--fail-on downgradebecomes a trivial, high-signal gate condition.Verification
npm run lint,npm run typecheck,npm test(34 passing), andnpm run buildall pass locally, plus an end-to-end CLI run against CycloneDX fixtures in both text and Markdown.🤖 Generated with Claude Code
https://claude.ai/code/session_01KPWdF3VnuxevNSR6iUwbQF
Generated by Claude Code