4444//! whatever raw text survives. Only for a uuid no read file mentions
4545//! (formats no extractor reads, patch servers outside the host allowlist)
4646//! does the ledger's own recorded wiring decide — only files that PIN the
47- //! resolution, never a leftover registry definition; a vendor entry
47+ //! resolution, never a leftover registry definition — and a hosted
48+ //! record whose lock entry or recorded wiring routes the uuid through a
49+ //! hosted-shaped url on an origin this run was not given is never
50+ //! attested (core `RecordLiveness::Undecided`, fail closed); a vendor entry
4851//! recording no wiring (repair's reconstruction) probes its ecosystem's
4952//! root locks; a lock inventory entry resolving the purl elsewhere is
5053//! dead. A reverted lockfile plus a leftover ledger or artifact must not
@@ -82,7 +85,7 @@ use socket_patch_core::utils::purl::strip_purl_qualifiers;
8285use socket_patch_core:: utils:: purl_key:: { canonical_base_purl, PurlKey } ;
8386use socket_patch_core:: vendor:: state:: { VendorArtifact , VendorEntry , VendorState } ;
8487use socket_patch_core:: vex:: discover:: {
85- vendor_ref, Discovery , LedgerLiveness , PatchedRef , WiringMode ,
88+ vendor_ref, Discovery , LedgerLiveness , PatchedRef , RecordLiveness , WiringMode ,
8689} ;
8790use socket_patch_core:: vex:: { FailedPatch , Unattested , UnattestedKind } ;
8891
@@ -237,6 +240,14 @@ const NOTE_MAVEN_PROFILE_UNPATCHED: &str = "vex_maven_profile_unpatched";
237240/// Omission tag and note for [`UnattestedKind::MavenConfigOverride`]
238241/// (`vex::Unattested`).
239242const NOTE_MAVEN_CONFIG_OVERRIDE : & str = "vex_maven_config_override" ;
243+ /// Omission tag and note: a hosted Cargo pin beside another declaration of
244+ /// the crate that names no Socket registry, which cargo resolves as a
245+ /// separate, unpatched package (`vex::Unattested`).
246+ const NOTE_CARGO_UNPINNED_DECLARATION : & str = "vex_cargo_unpinned_declaration" ;
247+ /// Omission tag and note: a vendored Cargo copy beside a copy of the crate
248+ /// from a source its `[patch]` entry does not redirect (another registry,
249+ /// git), which cargo builds unpatched (`vex::Unattested`).
250+ const NOTE_CARGO_PATCH_BYPASSED : & str = "vex_cargo_patch_bypassed" ;
240251/// Omission tag and note: a pnpm lock pins the patch, but a
241252/// `pnpm-lock.<branch>.yaml` exists, which pnpm installs from instead when
242253/// `gitBranchLockfile` is on; its presence withholds (`vex::Unattested`).
@@ -321,6 +332,15 @@ fn unattested_note(kind: UnattestedKind) -> (&'static str, &'static str) {
321332 through; the wiring itself is intact, so re-running `scan` does not change this \
322333 (drop that -D from .mvn/maven.config, or write the pinned version literally)",
323334 ) ,
335+ UnattestedKind :: CargoUnpinnedDeclaration => (
336+ NOTE_CARGO_UNPINNED_DECLARATION ,
337+ "not attested while that declaration stands" ,
338+ ) ,
339+ UnattestedKind :: CargoPatchBypassed => (
340+ NOTE_CARGO_PATCH_BYPASSED ,
341+ "not attested while that copy can build beside the vendored one; the vendored \
342+ wiring itself is intact (the warning above says how to clear it)",
343+ ) ,
324344 UnattestedKind :: PnpmGitBranchLock => (
325345 NOTE_PNPM_GIT_BRANCH_LOCKFILE ,
326346 "not attested while any branch lock exists (or the members that could hold one \
@@ -681,9 +701,14 @@ pub(crate) async fn plan(
681701 continue ;
682702 }
683703 } else if cand. redirected {
684- let live = assumed. contains ( strip_purl_qualifiers ( & cand. key ) )
685- || liveness. redirect_record ( & cand. key , & cand. uuid ) . await ;
686- if live {
704+ let verdict = if assumed. contains ( strip_purl_qualifiers ( & cand. key ) ) {
705+ RecordLiveness :: Live
706+ } else {
707+ liveness
708+ . redirect_record_verdict ( & cand. key , & cand. uuid )
709+ . await
710+ } ;
711+ if verdict. attests ( ) {
687712 Basis :: Hosted {
688713 lockfile_basis : false ,
689714 }
@@ -692,6 +717,9 @@ pub(crate) async fn plan(
692717 // purl: verify it as the agent-mode patch it now is.
693718 Basis :: Installed
694719 } else {
720+ if verdict == RecordLiveness :: Undecided {
721+ notes. push ( foreign_origin_note ( & cand. key , & cand. uuid ) ) ;
722+ }
695723 notes. extend ( dead_claim_notes (
696724 & discovery,
697725 & cand. key ,
@@ -1117,6 +1145,20 @@ fn dead_claim_notes(
11171145 notes
11181146}
11191147
1148+ /// Why a hosted-ledger record whose wiring resolves from a patch server
1149+ /// this run was not given ([`RecordLiveness::Undecided`]) is not attested:
1150+ /// fail closed, and say how a staging server is admitted.
1151+ fn foreign_origin_note ( key : & str , uuid : & str ) -> PlanNote {
1152+ PlanNote {
1153+ code : NOTE_CLAIM_UNWIRED ,
1154+ detail : format ! (
1155+ "{key}: the hosted ledger records patch {uuid}, but its wiring resolves it from a \
1156+ patch server this run was not given; pass --patch-server-url for a non-production \
1157+ patch server; not attested"
1158+ ) ,
1159+ }
1160+ }
1161+
11201162/// A local record for `uuid` (manifest, then redirect ledger, then vendor
11211163/// entries) with the key it is filed under.
11221164fn local_record_by_uuid (
@@ -2111,6 +2153,106 @@ mod tests {
21112153 ) ;
21122154 }
21132155
2156+ /// A hosted Cargo pin beside an unpinned declaration of the crate is
2157+ /// omitted as `vex_cargo_unpinned_declaration`, the note naming the
2158+ /// declaration and the remedy.
2159+ #[ tokio:: test]
2160+ async fn a_cargo_pin_beside_an_unpinned_declaration_is_gated ( ) {
2161+ const PURL : & str = "pkg:cargo/serde@1.0.1" ;
2162+ let tmp = tempfile:: tempdir ( ) . unwrap ( ) ;
2163+ let mut redirect = RedirectState :: new ( ) ;
2164+ redirect. records . insert ( PURL . into ( ) , record ( U1 ) ) ;
2165+ let mut found = discovery ( vec ! [ hosted_ref( PURL , U1 , true ) ] ) ;
2166+ found. unattested . push ( socket_patch_core:: vex:: Unattested {
2167+ purl : PURL . into ( ) ,
2168+ uuid : U1 . into ( ) ,
2169+ file : "crates/a/Cargo.toml" . into ( ) ,
2170+ detail : "crates/a/Cargo.toml declares serde \" 1\" from crates.io, not registry \
2171+ \" socket-patch-…\" ; re-run `socket-patch scan` to pin it too"
2172+ . into ( ) ,
2173+ kind : UnattestedKind :: CargoUnpinnedDeclaration ,
2174+ } ) ;
2175+ let gated = plan (
2176+ & common ( tmp. path ( ) ) ,
2177+ Sources {
2178+ manifest : PatchManifest :: new ( ) ,
2179+ vendor : VendorState :: new ( ) ,
2180+ redirect : Some ( redirect) ,
2181+ discovery : found,
2182+ } ,
2183+ & [ ] ,
2184+ & RunApiClient :: new ( ) ,
2185+ )
2186+ . await ;
2187+ assert ! ( gated. view. patches. is_empty( ) && gated. hosted. is_empty( ) ) ;
2188+ assert_eq ! (
2189+ gated. gated,
2190+ vec![ failed( PURL , NOTE_CARGO_UNPINNED_DECLARATION ) ]
2191+ ) ;
2192+ assert ! (
2193+ gated
2194+ . notes
2195+ . iter( )
2196+ . any( |n| n. code == NOTE_CARGO_UNPINNED_DECLARATION
2197+ && n. detail. contains( "crates/a/Cargo.toml declares serde" )
2198+ && n. detail. ends_with(
2199+ "re-run `socket-patch scan` to pin it too; not attested while that \
2200+ declaration stands"
2201+ ) ) ,
2202+ "{:?}" ,
2203+ gated. notes
2204+ ) ;
2205+ }
2206+
2207+ /// A vendored Cargo copy beside a copy of the crate its `[patch]`
2208+ /// entry does not redirect is omitted as `vex_cargo_patch_bypassed`,
2209+ /// the note naming that copy and the remedy.
2210+ #[ tokio:: test]
2211+ async fn a_vendored_cargo_copy_beside_a_bypassing_source_is_gated ( ) {
2212+ const PURL : & str = "pkg:cargo/serde@1.0.1" ;
2213+ let tmp = tempfile:: tempdir ( ) . unwrap ( ) ;
2214+ let mut manifest = PatchManifest :: new ( ) ;
2215+ manifest. patches . insert ( PURL . into ( ) , record ( U1 ) ) ;
2216+ let vref = vendor_ref ( & format ! ( ".socket/vendor/cargo/{U1}/serde-1.0.1" ) ) . unwrap ( ) ;
2217+ let mut found = discovery ( vec ! [ PatchedRef :: vendored(
2218+ PURL . into( ) ,
2219+ & vref,
2220+ "Cargo.toml" ,
2221+ None ,
2222+ ) ] ) ;
2223+ found. unattested . push ( socket_patch_core:: vex:: Unattested {
2224+ purl : PURL . into ( ) ,
2225+ uuid : U1 . into ( ) ,
2226+ file : "crates/a/Cargo.toml" . into ( ) ,
2227+ detail : "crates/a/Cargo.toml declares serde \" 1\" from registry `my-corp`" . into ( ) ,
2228+ kind : UnattestedKind :: CargoPatchBypassed ,
2229+ } ) ;
2230+ let gated = plan (
2231+ & common ( tmp. path ( ) ) ,
2232+ Sources {
2233+ manifest,
2234+ vendor : VendorState :: new ( ) ,
2235+ redirect : None ,
2236+ discovery : found,
2237+ } ,
2238+ & [ ] ,
2239+ & RunApiClient :: new ( ) ,
2240+ )
2241+ . await ;
2242+ assert ! ( gated. view. patches. is_empty( ) && gated. hosted. is_empty( ) ) ;
2243+ assert_eq ! ( gated. gated, vec![ failed( PURL , NOTE_CARGO_PATCH_BYPASSED ) ] ) ;
2244+ assert ! (
2245+ gated
2246+ . notes
2247+ . iter( )
2248+ . any( |n| n. code == NOTE_CARGO_PATCH_BYPASSED
2249+ && n. detail. contains( "from registry `my-corp`" )
2250+ && n. detail. contains( "says how to clear it" ) ) ,
2251+ "{:?}" ,
2252+ gated. notes
2253+ ) ;
2254+ }
2255+
21142256 /// Ledger-only records with no wiring anywhere are gated, except the
21152257 /// manifest-owned one, which falls back to agent-mode verification; an
21162258 /// in-run confirmed purl is live by construction.
0 commit comments