Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
43fd5aa
Match ledger liveness uuids by origin, not substring
mikolalysenko Nov 4, 2026
1a043ec
Read Cargo workspace members in VEX discovery
mikolalysenko Nov 4, 2026
650143e
Address review: view-mediated member walk, lockless pins, undecided l…
mikolalysenko Nov 5, 2026
b2e171b
Name the view walk's read-manifest pair (clippy type_complexity)
mikolalysenko Nov 5, 2026
77a4d2d
Stop attesting a Cargo pin a workspace member resolves from crates.io
mikolalysenko Nov 4, 2026
7837fb4
Classify each Cargo declaration before judging a hosted pin
mikolalysenko Nov 6, 2026
64d73a6
Withhold a vendored Cargo copy a member's other source bypasses
mikolalysenko Nov 6, 2026
346a313
Classify Cargo lockless pins, git patch tables and conflict pins
mikolalysenko Nov 6, 2026
94af3e4
Normalize foreign hosted urls before reading their origin
claude Nov 6, 2026
6592bf2
Fix review findings on Cargo vendored bypass and pin diagnostics
mikolalysenko Nov 6, 2026
d92dab4
Read Cargo members whenever a Socket pin could need them
claude Nov 6, 2026
64fce24
Merge remote-tracking branch 'origin/fix/cargo-members-liveness-origi…
claude Nov 6, 2026
ebe84e1
Count registry = "crates-io" as re-pinnable in the Cargo remedy
claude Nov 6, 2026
1021784
Merge remote-tracking branch 'origin/fix/cargo-mixed-member-pins' int…
claude Nov 6, 2026
52230be
Let a shadowed [patch] Socket pin pin no hosted declaration
claude Nov 6, 2026
a7611ef
Shadow a manifest [patch] item by any config item for its source and key
claude Nov 7, 2026
3d413af
Compare [patch] URL tables the way cargo canonicalizes them
claude Nov 7, 2026
ca08f29
Merge main into fix/cargo-members-liveness-origin
claude Nov 9, 2026
691fb4b
Merge fix/cargo-members-liveness-origin into fix/cargo-mixed-member-pins
claude Nov 9, 2026
71e39f9
Merge fix/cargo-mixed-member-pins into fix/cargo-vendored-twins-and-s…
claude Nov 9, 2026
b126508
Treat registry = "crates-io" as crates.io for URL-form [patch] tables
claude Nov 9, 2026
319504f
Merge fix/cargo-mixed-member-pins into fix/cargo-vendored-twins-and-s…
claude Nov 9, 2026
3695ddc
Withhold a vendored Cargo copy a stale pin to another patch bypasses
claude Nov 9, 2026
bc39a27
Pair the stale-pin bypass with an emitted ref, not any lock line
claude Nov 9, 2026
cb98b9b
Fail closed on hosted wiring from an unconfigured patch server
mikolalysenko Nov 9, 2026
6b9a1ac
Merge remote-tracking branch 'origin/fix/cargo-members-liveness-origi…
mikolalysenko Nov 9, 2026
0c83105
Ask the hosted rewriter whether a re-scan can pin a Cargo declaration
mikolalysenko Nov 9, 2026
5d99eb0
Merge fix/cargo-mixed-member-pins into fix/cargo-vendored-twins-and-s…
mikolalysenko Nov 9, 2026
f87fc9f
Fix review findings on Cargo [patch] shadowing and bypass details
mikolalysenko Nov 9, 2026
5ebe8ea
Merge remote-tracking branch 'origin/main' into fix/cargo-members-liv…
mikolalysenko Nov 9, 2026
f1eb764
Simplify the folded Cargo VEX stack
mikolalysenko Nov 9, 2026
678f42e
Merge origin/main into fix/cargo-members-liveness-origin
mikolalysenko Nov 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions crates/socket-patch-cli/CLI_CONTRACT.md

Large diffs are not rendered by default.

15 changes: 9 additions & 6 deletions crates/socket-patch-cli/src/commands/scan/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -981,8 +981,10 @@ fn overlap_from_states(
/// them to right now — the truth source for takeover direction.
///
/// Both directions are proved by lockfile discovery with the same liveness
/// rules `vex` gates attestations on (core `Discovery::redirect_record_live`
/// / `Discovery::vendor_entry_live`). `redirect` holds the overlap PURLs the
/// rules `vex` gates attestations on (core `Discovery::redirect_record_verdict`
/// / `Discovery::vendor_entry_live`); a hosted record on a patch server this
/// run was not given (`RecordLiveness::Undecided`) counts as wired, since the
/// wiring is still there. `redirect` holds the overlap PURLs the
/// lock routes to the hosted patch server (the vendored ledger entry is
/// stale); `vendored` holds those it routes to a committed
/// `.socket/vendor/<eco>/<uuid>` artifact.
Expand Down Expand Up @@ -1067,7 +1069,7 @@ pub(super) async fn classify_overlap_takeover_with(
let mut liveness = LedgerLiveness::new(cwd, discovery, None);
for purl in overlap {
let hosted_live = match redirect_uuid_by_purl.get(&PurlKey::new(&purl)) {
Some(uuid) => liveness.redirect_record(&purl, uuid).await,
Some(uuid) => liveness.redirect_record_retained(&purl, uuid).await,
None => discovery.wires_package(&purl, WiringMode::Hosted),
};
// `None`: undecided (a JVM pom that cannot be read, or a tree still
Expand Down Expand Up @@ -1394,8 +1396,9 @@ async fn gradle_scan(
/// pin names the purl (`redirect_state`, the lockfiles' hosted state — see
/// [`crate::commands::hosted_state_from_pins`]) AND lockfile discovery
/// proves the current lockfile still routes it to that hosted patch — core
/// `Discovery::redirect_record_live`, the same liveness rule `vex` gates
/// hosted attestations on.
/// `Discovery::redirect_record_verdict`, the same liveness rule `vex` gates
/// hosted attestations on, keeping a record on a patch server this run was
/// not given (`RecordLiveness::Undecided`): its wiring is still retained.
///
/// Deliberately NOT routed through [`classify_overlap_takeover`]: that
/// classifier keys on purls present in BOTH ledgers, so hosted-only wiring
Expand Down Expand Up @@ -1438,7 +1441,7 @@ pub(super) async fn hosted_wiring_retained_purls(
let mut liveness = LedgerLiveness::new(cwd, &discovery, None);
let mut out = Vec::new();
for (purl, uuid) in candidates {
if liveness.redirect_record(&purl, uuid).await {
if liveness.redirect_record_retained(&purl, uuid).await {
out.push(purl);
}
}
Expand Down
152 changes: 147 additions & 5 deletions crates/socket-patch-cli/src/commands/vex_sources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@
//! whatever raw text survives. Only for a uuid no read file mentions
//! (formats no extractor reads, patch servers outside the host allowlist)
//! does the ledger's own recorded wiring decide — only files that PIN the
//! resolution, never a leftover registry definition; a vendor entry
//! resolution, never a leftover registry definition — and a hosted
//! record whose lock entry or recorded wiring routes the uuid through a
//! hosted-shaped url on an origin this run was not given is never
//! attested (core `RecordLiveness::Undecided`, fail closed); a vendor entry
//! recording no wiring (repair's reconstruction) probes its ecosystem's
//! root locks; a lock inventory entry resolving the purl elsewhere is
//! dead. A reverted lockfile plus a leftover ledger or artifact must not
Expand Down Expand Up @@ -82,7 +85,7 @@ use socket_patch_core::utils::purl::strip_purl_qualifiers;
use socket_patch_core::utils::purl_key::{canonical_base_purl, PurlKey};
use socket_patch_core::vendor::state::{VendorArtifact, VendorEntry, VendorState};
use socket_patch_core::vex::discover::{
vendor_ref, Discovery, LedgerLiveness, PatchedRef, WiringMode,
vendor_ref, Discovery, LedgerLiveness, PatchedRef, RecordLiveness, WiringMode,
};
use socket_patch_core::vex::{FailedPatch, Unattested, UnattestedKind};

Expand Down Expand Up @@ -232,6 +235,14 @@ const NOTE_MAVEN_REACTOR_ROOT: &str = "vex_maven_reactor_root";
/// classifier copy at another version, which the pin does not reach
/// (`vex::Unattested`).
const NOTE_MAVEN_CLASSIFIER_UNPATCHED: &str = "vex_maven_classifier_unpatched";
/// Omission tag and note: a hosted Cargo pin beside another declaration of
/// the crate that names no Socket registry, which cargo resolves as a
/// separate, unpatched package (`vex::Unattested`).
const NOTE_CARGO_UNPINNED_DECLARATION: &str = "vex_cargo_unpinned_declaration";
/// Omission tag and note: a vendored Cargo copy beside a copy of the crate
/// from a source its `[patch]` entry does not redirect (another registry,
/// git), which cargo builds unpatched (`vex::Unattested`).
const NOTE_CARGO_PATCH_BYPASSED: &str = "vex_cargo_patch_bypassed";
/// Omission tag and note: a pnpm lock pins the patch, but a
/// `pnpm-lock.<branch>.yaml` exists, which pnpm installs from instead when
/// `gitBranchLockfile` is on; its presence withholds (`vex::Unattested`).
Expand Down Expand Up @@ -300,6 +311,15 @@ fn unattested_note(kind: UnattestedKind) -> (&'static str, &'static str) {
"not attested while that classifier copy resolves an unpatched version; the \
wiring itself is intact, so re-running `scan` does not change this",
),
UnattestedKind::CargoUnpinnedDeclaration => (
NOTE_CARGO_UNPINNED_DECLARATION,
"not attested while that declaration stands",
),
UnattestedKind::CargoPatchBypassed => (
NOTE_CARGO_PATCH_BYPASSED,
"not attested while that copy can build beside the vendored one; the vendored \
wiring itself is intact (the warning above says how to clear it)",
),
UnattestedKind::PnpmGitBranchLock => (
NOTE_PNPM_GIT_BRANCH_LOCKFILE,
"not attested while any branch lock exists (or the members that could hold one \
Expand Down Expand Up @@ -660,9 +680,14 @@ pub(crate) async fn plan(
continue;
}
} else if cand.redirected {
let live = assumed.contains(strip_purl_qualifiers(&cand.key))
|| liveness.redirect_record(&cand.key, &cand.uuid).await;
if live {
let verdict = if assumed.contains(strip_purl_qualifiers(&cand.key)) {
RecordLiveness::Live
} else {
liveness
.redirect_record_verdict(&cand.key, &cand.uuid)
.await
};
if verdict.attests() {
Basis::Hosted {
lockfile_basis: false,
}
Expand All @@ -671,6 +696,9 @@ pub(crate) async fn plan(
// purl: verify it as the agent-mode patch it now is.
Basis::Installed
} else {
if verdict == RecordLiveness::Undecided {
notes.push(foreign_origin_note(&cand.key, &cand.uuid));
}
notes.extend(dead_claim_notes(
&discovery,
&cand.key,
Expand Down Expand Up @@ -1096,6 +1124,20 @@ fn dead_claim_notes(
notes
}

/// Why a hosted-ledger record whose wiring resolves from a patch server
/// this run was not given ([`RecordLiveness::Undecided`]) is not attested:
/// fail closed, and say how a staging server is admitted.
fn foreign_origin_note(key: &str, uuid: &str) -> PlanNote {
PlanNote {
code: NOTE_CLAIM_UNWIRED,
detail: format!(
"{key}: the hosted ledger records patch {uuid}, but its wiring resolves it from a \
patch server this run was not given; pass --patch-server-url for a non-production \
patch server; not attested"
),
}
}

/// A local record for `uuid` (manifest, then redirect ledger, then vendor
/// entries) with the key it is filed under.
fn local_record_by_uuid(
Expand Down Expand Up @@ -2090,6 +2132,106 @@ mod tests {
);
}

/// A hosted Cargo pin beside an unpinned declaration of the crate is
/// omitted as `vex_cargo_unpinned_declaration`, the note naming the
/// declaration and the remedy.
#[tokio::test]
async fn a_cargo_pin_beside_an_unpinned_declaration_is_gated() {
const PURL: &str = "pkg:cargo/serde@1.0.1";
let tmp = tempfile::tempdir().unwrap();
let mut redirect = RedirectState::new();
redirect.records.insert(PURL.into(), record(U1));
let mut found = discovery(vec![hosted_ref(PURL, U1, true)]);
found.unattested.push(socket_patch_core::vex::Unattested {
purl: PURL.into(),
uuid: U1.into(),
file: "crates/a/Cargo.toml".into(),
detail: "crates/a/Cargo.toml declares serde \"1\" from crates.io, not registry \
\"socket-patch-…\"; re-run `socket-patch scan` to pin it too"
.into(),
kind: UnattestedKind::CargoUnpinnedDeclaration,
});
let gated = plan(
&common(tmp.path()),
Sources {
manifest: PatchManifest::new(),
vendor: VendorState::new(),
redirect: Some(redirect),
discovery: found,
},
&[],
&RunApiClient::new(),
)
.await;
assert!(gated.view.patches.is_empty() && gated.hosted.is_empty());
assert_eq!(
gated.gated,
vec![failed(PURL, NOTE_CARGO_UNPINNED_DECLARATION)]
);
assert!(
gated
.notes
.iter()
.any(|n| n.code == NOTE_CARGO_UNPINNED_DECLARATION
&& n.detail.contains("crates/a/Cargo.toml declares serde")
&& n.detail.ends_with(
"re-run `socket-patch scan` to pin it too; not attested while that \
declaration stands"
)),
"{:?}",
gated.notes
);
}

/// A vendored Cargo copy beside a copy of the crate its `[patch]`
/// entry does not redirect is omitted as `vex_cargo_patch_bypassed`,
/// the note naming that copy and the remedy.
#[tokio::test]
async fn a_vendored_cargo_copy_beside_a_bypassing_source_is_gated() {
const PURL: &str = "pkg:cargo/serde@1.0.1";
let tmp = tempfile::tempdir().unwrap();
let mut manifest = PatchManifest::new();
manifest.patches.insert(PURL.into(), record(U1));
let vref = vendor_ref(&format!(".socket/vendor/cargo/{U1}/serde-1.0.1")).unwrap();
let mut found = discovery(vec![PatchedRef::vendored(
PURL.into(),
&vref,
"Cargo.toml",
None,
)]);
found.unattested.push(socket_patch_core::vex::Unattested {
purl: PURL.into(),
uuid: U1.into(),
file: "crates/a/Cargo.toml".into(),
detail: "crates/a/Cargo.toml declares serde \"1\" from registry `my-corp`".into(),
kind: UnattestedKind::CargoPatchBypassed,
});
let gated = plan(
&common(tmp.path()),
Sources {
manifest,
vendor: VendorState::new(),
redirect: None,
discovery: found,
},
&[],
&RunApiClient::new(),
)
.await;
assert!(gated.view.patches.is_empty() && gated.hosted.is_empty());
assert_eq!(gated.gated, vec![failed(PURL, NOTE_CARGO_PATCH_BYPASSED)]);
assert!(
gated
.notes
.iter()
.any(|n| n.code == NOTE_CARGO_PATCH_BYPASSED
&& n.detail.contains("from registry `my-corp`")
&& n.detail.contains("says how to clear it")),
"{:?}",
gated.notes
);
}

/// Ledger-only records with no wiring anywhere are gated, except the
/// manifest-owned one, which falls back to agent-mode verification; an
/// in-run confirmed purl is live by construction.
Expand Down
65 changes: 24 additions & 41 deletions crates/socket-patch-cli/tests/e2e_vex_lockfile/cargo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -913,48 +913,31 @@ fn cargo_hosted_f_spoofed_references_never_attest() {
let run = fx.vex(&["--proxy-url", &api.uri()]);
assert_nothing_discovered(&run, "foreign host");
assert_eq!(api.hits(), 0, "a foreign-host uuid is never even fetched");
// DELIBERATE (the staging fallback): a uuid on a host outside the
// allowlist is not a lockfile reference, but it is not "recognized"
// either, so a redirect-ledger record for it falls back to the ledger's
// own recorded wiring files — which still name it — so that a staging
// `scan --mode hosted` run without `--patch-server-url` keeps
// attesting. Hash verification still judges the copy the build
// consumes (the foreign registry's): nothing is installed here, so the
// default run omits it; only `--no-verify`, which trusts the ledger's
// record by definition, attests.
// Fail closed: a ledger record wired through a hosted-shaped url on an
// unconfigured origin is `redirect_unwired` whatever is installed, even
// under `--no-verify` (CLI_CONTRACT.md, "Patch hosts (manifest-less VEX)").
write_redirect_ledger(&fx, CARGO_PURL, cargo_record(U), CARGO_HOSTED_EDITS);
let run = fx.vex(&["--offline"]);
assert_omitted(
&run,
CARGO_PURL,
"package_not_found",
"foreign host + ledger",
);
fx.install_crate(SOCKET_CARGO_SRC, PATCHED_RS);
let run = fx.vex(&["--offline"]);
assert_omitted(
&run,
CARGO_PURL,
"package_not_found",
"foreign host + ledger: Socket's copy is not what this lock builds",
);
fx.install_crate("evil.example-0123456789abcdef", PRISTINE_RS);
let run = fx.vex(&["--offline"]);
assert_eq!(
run.code,
Some(1),
"foreign registry's pristine copy: {}",
run.env
);
let run = fx.vex(&["--offline", "--no-verify"]);
assert_attested(
&fx,
&run,
CARGO_PURL,
U,
"redirected",
"staging fallback, --no-verify",
);
for (state, install) in [
("not installed", None),
("Socket's patched copy", Some(SOCKET_CARGO_SRC)),
(
"the foreign registry's patched copy",
Some("evil.example-0123456789abcdef"),
),
] {
if let Some(src) = install {
fx.install_crate(src, PATCHED_RS);
}
for extra in [&["--offline"][..], &["--offline", "--no-verify"][..]] {
let run = fx.vex(extra);
assert_omitted(
&run,
CARGO_PURL,
"redirect_unwired",
&format!("foreign host + ledger, {state} {extra:?}"),
);
}
}

// The same foreign origin configured as the operator's patch server.
let fx = Fx::new();
Expand Down
Loading
Loading