Repository navigation
Withhold a vendored Cargo copy a member's other source bypasses - #2024
Draft
Mikola Lysenko (mikolalysenko) wants to merge 7 commits into
Draft
Mikola Lysenko (mikolalysenko) wants to merge 7 commits into
Mikola Lysenko (mikolalysenko) wants to merge 7 commits into
Conversation
Mikola Lysenko (mikolalysenko)
force-pushed
the
fix/cargo-mixed-member-pins
branch
from
November 5, 2026 19:57
06d306c to
77a4d2d
Compare
Collaborator
Author
|
Burn-down agent: resolved the merge conflicts with
Generated by Claude Code |
`vendor` wires a crate through the root's `[patch.crates-io]`, which cargo applies only to declarations from crates.io. A workspace member (or the root) that declares the crate from another registry, a `registry-index` or git, at a requirement that may select the patched version, resolves its own unpatched copy. The vendored ref stayed attested beside it because the lock copy claim only reads sourceless entries. The ref now stays (rollback, remove and the ledgers still see the wiring) but is marked unattested with the new `UnattestedKind::CargoPatchBypassed`, naming the declaration. A same-version Cargo.lock entry from any registry or git source beside the copy is marked the same way, naming the lock entry. The check reads the declarations through #2019's classification: live declarations only, a registry dependency counts as covered when a `[patch.<registry>]` table pointing at the same copy redirects it (`Decl::patched_by`), a path declaration (the user's own fork) keeps the existing "ignored" rule, and a `socket-patch-*` registry dependency is hosted wiring. The CLI omits the ref as `vex_cargo_patch_bypassed`; documented in CLI_CONTRACT.md. The workspace member walk ran only when the lock held a Socket-sourced package (or, with no lock, a manifest named a `socket-patch-` registry). A vendored copy's lock entry has no source and it has no such pin, so the walk now also runs whenever the root manifest or the project config wires a Socket copy through `[patch]`, whatever the lock's state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mikola Lysenko (mikolalysenko)
force-pushed
the
fix/cargo-vendored-twins-and-split-patches
branch
from
November 6, 2026 05:27
5b08b24 to
64d73a6
Compare
Three Cargo follow-ups to the declaration classification: Lockless pins read declarations like locked ones. A `socket-patch-<uuid>` registry beside `path`, `git` or `registry-index` is no pin (cargo builds that source, and the rewriter's own pinned check agrees), so it no longer adds its requirement to an `UnlockedPin` or makes one on its own. It counts as an unpinned declaration instead, excluding the versions it accepts (every version, without a requirement). A `[workspace.dependencies]` template no manifest inherits is no declaration: a Socket-pinned one makes no lockless pin, and an unpinned one excludes nothing from the root's pin. A non-Socket `[patch]` registry entry no longer counts as an unpinned declaration either (it redirects declarations, it adds none). `DeclSource::Git` keeps its URL, and `Decl::patched_by` matches a `[patch."<repository URL>"]` table against it (a trailing `/` or `.git` ignored). So a git declaration whose repository table points at the same vendored copy no longer withholds that copy, while one from another repository still does. Hosted routing gains the same match for a Socket registry pin in such a table. When no declaration pins the lock's patch, a pin to another patch now makes the conflict pair only when it is live and its requirement may select the locked version (`cargo_req_selects`). One that excludes it is about another version; the stale lock entry is then diagnosed and dropped like any unrouted one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
False attestation: the vendored bypass check counted a declaration as redirected by any root or config `[patch]` entry pointing at the copy, including a manifest entry cargo ignores (the project config redefines its crates.io key, or a crates.io URL table replaces `[patch.crates-io]`). Cargo then builds whatever replaced it for those declarations, while the copy's other, honored entry was attested. Only honored entries count now: the root's `[patch]` entries are collected once with their shadow reason (a `Shadow` built from the root manifest and the config), and the bypass check sees only the unshadowed ones. `vendored` (the member gate) is derived from the same list instead of re-reading every `[patch]` table. One classifier: the bypass check runs each declaration through `route` with the copy's honored `[patch]` tables as the pins, as hosted pins do, passing the lock's other versions of the crate. A path declaration (the user's own fork) is still left alone. `patched_by` now covers a `registry-index` declaration with a `[patch."<index URL>"]` table for the same index (a `registry+` prefix, host case and a trailing `/` ignored; `sparse+` is another source), and treats a `registry-index` naming the crates.io git index as crates.io. The sparse crates.io index stays a separate source. Git URLs compare as cargo's `CanonicalUrl` spells them (one trailing `/` and a `.git` suffix dropped, a GitHub URL lowercased whole, any host lowercased). The bypass detail ends with the remedy for its evidence: a declaration (re-running `vendor` does not help), a crates.io lock entry beside a `[patch.crates-io]` copy (stale: re-lock), or another lock entry (`cargo tree -i` names its declarer). The CLI note no longer claims re-vendoring never helps. Diagnostics: a Socket pin to another patch that the lock does not reflect (its requirement excludes every locked version) used to vanish once any Socket entry of the crate was locked; it is now diagnosed `patched_ref_unattributable`. A well-formed `socket-patch-<uuid>` registry that pins nothing (beside `path` / `git` / `registry-index`, or on a `[workspace.dependencies]` entry no manifest inherits) is diagnosed `patched_ref_invalid`. The committed `workspace-member` redirect golden gains that diagnostic: its `expected/` tree omits the unchanged inheriting member, so the pinned template reads as uninherited there. `Decl::describe` and `same_version_foreign_entry` are shared by the hosted and vendored checks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…o fix/cargo-vendored-twins-and-split-patches # Conflicts: # crates/socket-patch-core/src/vex/discover/cargo.rs
FALSE ATTESTATION (found reviewing #2019; fixed here, where `Shadow` lives): a root `[patch.crates-io]` Socket registry pin cargo ignores (a `[patch."<crates.io URL>"]` table replaces it, or the config redefines the key) still counted as pinning every crates.io declaration of the crate, so a member's plain declaration was taken as routed to the patch and the hosted ref attested while cargo builds crates.io. Such an entry stays a declaration (Socket's wiring is present, the ref stays) but is not live, and `route` only weighs live `[patch]` pins, as the vendored bypass check already weighs only honored entries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator
Author
|
bugbot run Generated by Claude Code |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 3 potential issues.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Pathless config patch still counts as live
- Changed the shadowing detection logic from '*p != own' to '!(p.is_some() && *p == own)' so that path-less config patches properly shadow path-less manifest patches while preserving the same-path exception.
Or push these changes by commenting:
@cursor push 93a51234a1
Preview (93a51234a1)
diff --git a/crates/socket-patch-core/src/vex/discover/cargo.rs b/crates/socket-patch-core/src/vex/discover/cargo.rs
--- a/crates/socket-patch-core/src/vex/discover/cargo.rs
+++ b/crates/socket-patch-core/src/vex/discover/cargo.rs
@@ -1435,7 +1435,7 @@
let own = path.map(same_path_key);
self.config_keys
.iter()
- .find(|(_, k, p)| k == key && *p != own)
+ .find(|(_, k, p)| k == key && !(p.is_some() && *p == own))
.map(|(file, k, _)| {
format!("{file} redefines `[patch]` key `{k}`, which replaces it in cargo")
})You can send follow-ups to the cloud agent here.
Cargo replaces a manifest [patch] item with a project-config item of the same source and key, whatever the source table and whether either wires a path. Shadow only looked at crates.io tables and treated two path-less items (a registry pin and a git fork) as the same wiring, so a config fork left the manifest's Socket pin live and its stale lock row could be attested. Only an identical path is now the same wiring, and named and URL tables are compared too (URLs as an index key). The ineffective-registry diagnostic also called a shadowed [patch] pin an uninherited [workspace.dependencies] template; it now says cargo ignores the [patch] entry. Addresses Bugbot 4501098254, 4501098256 and 4501098258. Co-Authored-By: Claude <noreply@anthropic.com>
Collaborator
Author
|
bugbot run Generated by Claude Code |
Shadow compared a config's [patch."<url>"] table with the manifest's through index_key, which keeps a trailing .git and a GitHub path's case. Cargo canonicalizes both, so a config item under a canonical-equivalent git URL replaced the manifest's Socket copy while discovery still counted it. Non-sparse URL tables now compare through git_key; sparse+ ones stay index keys (another source). Addresses Bugbot 4501313607. Co-Authored-By: Claude <noreply@anthropic.com>
Collaborator
Author
|
bugbot run Generated by Claude Code |
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 3d413af. Configure here.
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.

Stacked on #2019 (
fix/cargo-mixed-member-pins). It closes the vendored-mode Cargo false-attestation gap the #2019 author found.The second gap, a root pinning a crate to one Socket patch while a member pins it to another, is now handled by #2019 itself (
beside_the_pinemits the other patch'sCargo.tomlref, so the CLI gates it aswiring_conflict). This PR no longer touches it. I ran my earlier split-patch tests against #2019's code alone and they pass, so there was nothing left to keep.Vendored copy bypassed by another source
vendorwires a crate through the root's[patch.crates-io], and cargo applies that only to crates.io declarations. A workspace member (or the root) can declare the crate from another registry, aregistry-indexor a repository URL, at a requirement that may select the patched version. That declaration resolves its own unpatched copy. The vendored ref still attested beside it, because the lock copy claim (vendored_copy_claim) only reads sourceless entries.UnattestedKind::CargoPatchBypassed. The detail names the declaration.Cargo.lockentry at exactly the patched version from any registry or repository source, beside the copy, is marked the same way. The detail names that lock entry.DeclSourceclassification:[workspace.dependencies]template no manifest inherits doesn't.[patch.<registry>]table pointing at the same copy redirects it (Decl::patched_by). A git dependency is covered by a[patch."<repository URL>"]table pointing at the copy (see the follow-ups below).a_same_version_fork_beside_the_tagged_copy_is_ignoredalready specifies.socket-patch-*registry dependency is hosted wiring, handled by Stop attesting a Cargo pin a workspace member resolves from crates.io #2019.vex_cargo_patch_bypassed. It's documented in CLI_CONTRACT.md (Unattested references paragraph and the notes table).I added a new variant rather than reusing
CargoUnpinnedDeclaration. That variant's note tells the user to re-runscan, which is the wrong fix for vendored wiring.Member gate. #2019 reads workspace members only when the lock holds a Socket-sourced package (or, with no lock, a manifest names a
socket-patch-registry). A vendored copy's lock entry has no source, so the members are now also read whenever the root manifest or the project config wires a Socket copy through[patch], whatever the lock's state.Cargo follow-ups (second commit)
unresolved_manifest_pins,UnlockedPin,only_this_patch).socket-patch-<uuid>registry besidepath,gitorregistry-indexis no pin, matching the rewriter's pinned check. It no longer feeds anUnlockedPin; it counts as an unpinned declaration that excludes the versions it accepts (all of them, without a requirement).[workspace.dependencies]template no manifest inherits is no declaration.[patch]registry entry no longer counts as an unpinned declaration (it redirects, it declares nothing).DeclSource::Gitkeeps its URL.Decl::patched_bymatches it against a[patch."<repository URL>"]table (a trailing/or.gitignored). A git declaration whose repository table points at the same vendored copy no longer withholds it, while one from another repository still does. Hosted routing gets the same match for a Socket registry pin in such a table.cargo_req_selectssays it may select the locked version. Otherwise the stale lock entry is diagnosed and dropped like any unrouted one.Tests
a_member_from_another_source_bypasses_the_vendored_copy(another registry, a renamed=1.0.4, a repository URL, aregistry-index)a_same_version_lock_entry_from_any_source_bypasses_the_vendored_copya_member_from_git_bypasses_the_config_wired_copymembers_are_read_for_a_vendored_copy_without_a_socket_lock_entry(with and without a lock)declarations_the_patch_redirects_keep_the_vendored_copy_attested. It covers crates.io members in every spelling, includingregistry = "crates-io"and a rename; an excluding requirement;[patch.my-corp]coverage; a path fork; and an un-inherited workspace template.a_vendored_cargo_copy_beside_a_bypassing_source_is_gated.lockless_path_or_git_beside_a_socket_registry_is_no_pin(path / git / registry-index member beside the root's pin, and alone)lockless_uninherited_template_is_not_a_declaration(Socket-pinned template uninherited vs inherited; an uninherited crates.io template beside the root's pin)a_git_patch_table_for_the_copy_covers_that_repository_only(covered with a.git/spelling and a branch; another repository still bypasses)another_patch_pin_excluding_the_locked_version_is_no_conflictcargo test -p socket-patch-core --lib -- vex::discover:: vendor::cargo patch::redirect:: utils::cargo_workspace(includes the golden corpus test): 1564 passed.-p socket-patch-cli --lib vex_sources: 16 passed.--test in_process_redirect cargo,--test in_process_cargo_apply cargo,--test mode_migration_cargo: 6 / 3 / 30 passed.cargo clippy --locked --workspace --all-features -- -D warningsandcargo fmt --all -- --checkare clean.Review findings (
/code-review high, third commit)Each of findings 1–6 was confirmed first by a test that fails on the previous commit and passes now.
False attestation from a shadowed
[patch]entry: confirmed and fixed. The bypass check counted a manifest entry cargo ignores (the config redefines its crates.io key with a git fork) as redirecting crates.io declarations, so the copy's honored[patch.my-corp]entry was attested beside the fork. Only honored entries count now. Test:a_shadowed_manifest_entry_covers_no_declaration.registry-indexcoverage: confirmed and fixed. A[patch."<index URL>"]table now covers a declaration of the same index (aregistry+prefix, host case and a trailing/ignored). Aregistry-indexnaming the crates.io git index is crates.io. Test:a_registry_index_patch_table_covers_that_index.sparse+https://index.crates.io/) is not treated as crates.io.Cargo.lockrecords it as a distinct source string from the git crates.io index, and the files don't show that[patch.crates-io]reaches it. Treating it as covered could attest a copy cargo doesn't build, so it stays withheld. The cost is a missed attestation on a rare hand-written shape.registry = "<name>"declaration is still matched only by a[patch.<name>]table, not by its index URL (the registry's URL may live in$CARGO_HOME).Remedy per evidence: confirmed and fixed. The detail now ends with the remedy for its evidence:
vendordoesn't help;[patch.crates-io]copy: stale, so re-lock (re-runvendororcargo update -p <crate>);cargo tree -i <crate>@<version>names the dependency that declares it.The CLI note's fixed suffix no longer makes that claim. Test:
the_bypass_remedy_follows_its_evidence.Canonical git URLs: confirmed and fixed. URLs are compared as cargo's
CanonicalUrlspells them: one trailing/and.gitdropped, a GitHub URL lowercased whole, any host lowercased. Test:a_git_patch_table_matches_cargos_canonical_url.Unreflected pin to another patch lost its diagnostic: confirmed and fixed. Once the lock held any Socket entry of the crate,
unresolved_manifest_pinsskipped it, so such a pin got no ref and no diagnostic. It now getspatched_ref_unattributableunless the lock resolves the crate from that patch or a ref already carries it. Test:another_patch_pin_excluding_the_locked_version_is_no_conflict(extended).Ineffective Socket registry gets no diagnostic: confirmed and fixed. A well-formed
socket-patch-<uuid>besidepath/git/registry-index, or on a[workspace.dependencies]entry no manifest inherits, is now diagnosedpatched_ref_invalid("pins nothing there"). Test:an_ineffective_socket_registry_is_diagnosed.redirect/cargo/cargo/workspace-member/expectedtree omits the unchanged inheriting membera/, so its pinned template reads as uninherited there and now carries that diagnostic. I regenerated the golden; no other corpus project changed.socket_copiescomputed twice: fixed. It's computed once, before the root manifest moves;vendored = !wired_all.is_empty().Shared helpers: done.
Decl::describeandsame_version_foreign_entryare used by bothbeside_the_pinandpatch_bypassed.One classifier: done without widening
route.routealready takes(table, uuid)pins, so the vendored check calls it with the copy's honored[patch]tables as the pins and the lock's other versions of the crate. That is the same classifier hosted pins use, with no new parameter. The only vendored-specific rule left is that a path declaration (the user's own fork) is ignored, an existing documented rule.Not done
vendor::cargoalready refuses the lock-twin state:locked_multi_source_conflictcovers a same-version entry from two sources, andvendor_source_unsupportedcovers a non-crates.io entry. A member declaration whose other-source copy isn't locked yet is invisible to the planner without re-resolving.Cloud routine: T1 verified, brought current, one more fix
6592bf2) landed before this run. All 9 findings are addressed (see the section above). I re-ran the full T1 test set on6592bf2: discovery,vendor::cargo,patch::redirect,utils::cargo_workspace, the golden corpus, CLIvex_sources,--test in_process_redirect cargo,--test mode_migration_cargo, clippy and fmt. Everything passes exceptvlt_heal::an_unremovable_hidden_lock_keeps_every_store_entry, a chmod test that fails only because the container runs as root.1021784. Three conflicts invex/discover/cargo.rs::read_members/extract. I resolved them by combining Read Cargo members in discovery; match liveness uuids by origin #2012's rule (parse members whenever asocket-patch-name appears anywhere) with this PR'svendoredrule (a[patch]-wired copy always reads members).[patch.crates-io]Socket registry pin that cargo ignores is replaced either by a[patch."<crates.io URL>"]table or by a config that redefines the key. That pin still counted for hostedroute, so a member's crates.io declaration was taken as pinned. Such an entry now stays a declaration, so the ref stays, but it isn't live.routeweighs only live[patch]pins, matching what the vendored bypass check already does. Test:a_shadowed_patch_pin_pins_no_declaration. It fails without the fix.Tests after the merge and fix:
cargo test -p socket-patch-core --lib -- vex::discover:: vendor::cargo patch::redirect:: utils::cargo_workspacegives 1570 passed (the 1 failure is the root-only chmod test). CLIvex_sources,in_process_redirect cargoandmode_migration_cargopass. clippy and fmt are clean.Note
Medium Risk
Changes Cargo VEX attestation logic for vendored workflows; incorrect bypass detection could wrongly omit or emit patch statements, though wiring management paths are preserved.
Overview
Closes a false-attestation gap for vendored Cargo: when
[patch.crates-io](or another honored[patch]table) wires a Socket copy but the workspace can still build the same version from another registry, git, registry-index, or a foreignCargo.lockentry, discovery now marks the ref unattested asCargoPatchBypassed/vex_cargo_patch_bypassedwhile keeping rollback/list/remove behavior.The Cargo discovery pass is expanded to always read workspace members when any
[patch]points at a vendor copy, classify declarations with live/shadowed[patch]entries (Shadow, git/index URL canonicalization), and only count honored patch tables when deciding coverage. Related follow-ups tighten lockless pins (path/git besidesocket-patch-*registries, uninherited workspace templates), conflict detection when another patch pin cannot select the locked version, and diagnostics for ineffective Socket registries.CLI contract, VEX note mapping, tests, and one discover golden fixture are updated to match.
Reviewed by Cursor Bugbot for commit 3d413af. Configure here.
Generated by Claude Code