Repository navigation
Key NuGet purls by the normalized version in PurlKey (#1202) - #1230
Merged
Mikola Lysenko (mikolalysenko) merged 2 commits intoOct 9, 2026
Merged
Conversation
Assisted-by: Claude Code:claude-opus-5-5
A NuGet package vendored under a non-normalized purl version, such as the 4-part pkg:nuget/Foo@1.0.0.0 spelling packages.config projects use, was wired against the lock's normalized `resolved` version but judged by PurlKey, which only lowercased NuGet versions. VEX, vendor --check and the scan --prune GC then saw the live entry as unused, so prune silently reverted a working patch. PurlKey now folds a NuGet version through the one normalize_nuget_version the vendored backend and upstream restore already use, the same way it folds composer release spellings. The canonical spelling is unchanged. Tests pin the issue's vectors, sweep PurlKey against the vendored version match, and vendor at @13.0.3.0 against a lock resolving 13.0.3, which must stay in use and live. Refs #1202 Assisted-by: Claude Code:claude-opus-5-5
Mikola Lysenko (mikolalysenko)
marked this pull request as ready for review
October 9, 2026 06:14
Collaborator
Author
|
BugBot review Generated by Claude Code |
Mikola Lysenko (mikolalysenko)
pushed a commit
that referenced
this pull request
Oct 9, 2026
Assisted-by: Claude Code:claude-opus-5-5
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 8c93493. Configure here.
Tanmay Singla (Tanmay182003)
approved these changes
Oct 9, 2026
Collaborator
Author
|
Burn-down agent: labeled Ready for review at head
Generated by Claude Code |
Mikola Lysenko (mikolalysenko)
deleted the
arch-refactor/1202-nuget-purl-identity
branch
October 9, 2026 07:41
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.
LLM Description written by Claude Code:claude-opus-5-5
Refs #1202 (this PR is the
PurlKeyslice. The issue stays open for the crawler directory lookup and for moving the normalizer intoformats::nuget.)Summary
PurlKeynow keys a NuGet purl by its normalized version, using the samenormalize_nuget_versionthat the vendored backend and upstream restore already use. Before this change, a NuGet entry vendored at a non-normalized version, such aspkg:nuget/Newtonsoft.Json@13.0.3.0(the 4-partpackages.configspelling), was wired against the lock'sresolved: "13.0.3". Every liveness reader then compared it throughPurlKey, which only lowercased the version, and judged it unused. As a result,scan --prunesilently reverted a live patch,vendor --checkreported the wiring as lost, andvexdid not attest it.Why (leverage)
normalize_nuget_versionandPurlKey's lowercase-only arm. Now there is one.formats::nugetmove now build on a single identity.register/10-audit-ecosystems.md). Living document:doc/06-discovery-vex.md, the "NuGet version identity is defined twice" bullet.crawlers/nuget_crawler.rsandvendor/nuget_feed.rsare changed by open PRs Classify purls through Ecosystem::from_purl in free files (#747) #1126 and Resolve the org once per run and route every API call through it (#648) #1041, so this slice doesn't touch them.PurlKeyimports the existingpub(crate)normalizer, the same wayupstream/nuget.rsalready does.What changed
utils/purl_key.rs:PurlKey::newgoes throughrelease_identity, which handles composer as before and adds a newnuget_identity(pkg:nuget/<id>@<normalize_nuget_version(v)>). An empty id or version keys as its canonical spelling.canonical_base_purlis unchanged and keeps the as-written version.utils/purl_key_nuget_vendor_tests.rs(a child test module).Nothing was deleted: the lowercase-only NuGet version rule is now the spelling layer only.
Behavior
Two NuGet purls that differ only in version spelling (
1.0.0.0vs1.0.0,1.02.3vs1.2.3,+buildmetadata, pre-release case) now share one key. Any report that prints aPurlKeystring (rollout and policypurl) shows the normalized NuGet version, the same way composer keys already show3.0.2.0. JSON shapes, error codes and exit codes are unchanged.Test evidence
nuget_version_spellings_share_the_normalized_key: the issue's acceptance vectors.nuget_key_agrees_with_the_vendored_version_match: a 30×30 sweep checking thatPurlKey::sameequals "case-insensitive id equal ∧normalize_nuget_versionequal", the rulelocked_atand upstream restore apply.nuget_vendored_at_a_four_part_version_stays_live:test_support::vendor_nuget("…@13.0.3.0")against a lock resolving13.0.3. It passesassert_fresh_vendor_in_use, which is the prune GC'svendor_entry_in_use, and assertsvendor_entry_in_use == Some(true)andvendor_entry_live.main's rule, all 3 tests fail. The vendor test fails attest_support.rs:498: "the prune GC would revert a freshly vendored nuget entry". With the change, all 3 pass.cargo clippy --workspace --all-features -- -D warnings: clean.cargo test -p socket-patch-core --lib: 5890 passed. The 4 failures are the known root-only sandbox tests (copy_tree::relax_loop_must_not_traverse_symlinked_root,vlt_heal::an_unremovable_hidden_lock_keeps_every_store_entry,pypi_poetry::wire_write_failure_maps_error_and_leaves_lock_untouched,pypi_requirements::wire_failure_rolls_back_already_written_files). They fail onmaintoo.in_process_scan27,in_process_vendor130,e2e_vex_redirect31 andin_process_remote_ecosystems_apply12, all passing.scan --pruneregression test the issue asks for. No CLI fixture vendors NuGet through a mocked service yet, andrun_vendor_gc's verdict is exactlyvendor_entry_in_use, which the core test pins. Nodotnetin the sandbox, soe2e_nuget_dotnet_buildwas not run.Risk
Low. The change only widens equality for NuGet version spellings that NuGet itself treats as one package. Non-NuGet keys are byte-identical, which the existing
purl_keyand rollout tests pin.🤖 Generated with Claude Code
https://claude.ai/code/session_01WLYGeuZVwXNvU9MGhqEeQR
Note
Medium Risk
Widens NuGet
PurlKeyequality across the codebase (GC, VEX, policy maps); behavior matches NuGet’s own identity rules but any caller that assumed byte-exact NuGet versions could see different grouping.Overview
PurlKeynow treats NuGet package releases as the same when their versions normalize identically, aligning liveness checks (VEX,vendor --check,scan --prune) with the vendored backend and lockresolvedfields that already usenormalize_nuget_version.PurlKey::newroutes through a sharedrelease_identityhelper (Composer unchanged, new NuGet path). Equivalent spellings such as13.0.3.0vs13.0.3, padded segments, pre-release case, and+buildmetadata now share one key;canonical_base_purlstill keeps the as-written version for display. ReportedPurlKeystrings for NuGet show the normalized version, similar to Composer’s release identity.New unit and integration tests cover version equivalence, agreement with the vendored version matcher, and that an entry vendored at a four-part purl stays “live” against a lock resolving the normalized version (#1202).
Reviewed by Cursor Bugbot for commit 8c93493. Configure here.
Generated by Claude Code