From 1ece09711fcb3ceeaa940ea878b1c41291b3f00c Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 29 Oct 2026 00:22:47 +0000 Subject: [PATCH 1/3] Start fix for #584 Assisted-by: Claude Code:claude-opus-5-5 From 453d4ca933bb783a46423c15f791c1a269e7f90b Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 29 Oct 2026 00:32:51 +0000 Subject: [PATCH 2/3] Withhold Maven VEX for a module left at base After a reactor was vendored, adding a module (or a dependency) that declares the patched artifact at its plain base version made that module build Central's unpatched jar. `vendor --check` reported the drift, but `vex` still attested not_affected, because its liveness check only looked for ${property} versions it could not resolve. The check now also withholds attestation when the planner would still rewrite a declaration, so `vex` skips the package as vendor_unwired until `vendor` is re-run. Fixes #584. Assisted-by: Claude Code:claude-opus-5-5 --- .../socket-patch-cli/tests/vendor_jvm_cli.rs | 56 +++++++++++++++++++ .../src/vendor/jvm/maven_reactor.rs | 47 ++++++++++++++-- 2 files changed, 98 insertions(+), 5 deletions(-) diff --git a/crates/socket-patch-cli/tests/vendor_jvm_cli.rs b/crates/socket-patch-cli/tests/vendor_jvm_cli.rs index 18b749a51..205f129aa 100644 --- a/crates/socket-patch-cli/tests/vendor_jvm_cli.rs +++ b/crates/socket-patch-cli/tests/vendor_jvm_cli.rs @@ -1237,6 +1237,62 @@ fn maven_reactor_uncertain_property_version_withholds_vex() { } } +/// #584: a module added after vendoring that declares the base literally +/// builds the unpatched jar. `vendor --check` flags the drift and `vex` +/// withholds attestation until a re-vendor rewrites the module. +#[test] +fn maven_reactor_module_added_at_base_after_vendoring_withholds_vex() { + let tmp = tempfile::tempdir().unwrap(); + let root = tmp.path(); + fixture(root, Shape::Reactor, &[("foo", FOO_UUID)]); + let pristine = std::fs::read_to_string(root.join("proj/a/pom.xml")).unwrap(); + ok(root, &["vendor"]); + let vex = root.join("vex.json"); + let vex_args = [ + "vex", + "-O", + vex.to_str().unwrap(), + "--product", + "pkg:generic/x@1", + ]; + ok(root, &vex_args); + std::fs::remove_file(&vex).unwrap(); + + let agg = std::fs::read_to_string(root.join("proj/pom.xml")).unwrap(); + let agg = agg.replace( + "a", + "a\n b", + ); + write(root, "proj/pom.xml", agg.as_bytes()); + std::fs::create_dir_all(root.join("proj/b")).unwrap(); + let b = pristine.replace("a", "b"); + assert!(b.contains("1.0"), "{b}"); + write(root, "proj/b/pom.xml", b.as_bytes()); + + let (code, env) = socket(root, &["vendor", "--check"]); + assert_ne!(code, Some(0), "{env}"); + let (_, env) = socket(root, &vex_args); + assert_eq!( + events(&env), + [( + purl("foo"), + "skipped".to_string(), + "vendor_unwired".to_string() + )], + "{env}" + ); + assert!(!vex.exists(), "{env}"); + + // Re-vendoring wires `b` too, and the reactor attests again. + ok(root, &["vendor"]); + assert!(!std::fs::read_to_string(root.join("proj/b/pom.xml")) + .unwrap() + .contains("1.0")); + ok(root, &vex_args); + let doc: serde_json::Value = serde_json::from_slice(&std::fs::read(&vex).unwrap()).unwrap(); + assert_eq!(doc["statements"][0]["status"], "not_affected", "{doc}"); +} + /// #429 repair: a deleted owned `.gitattributes` (and nothing else) is /// rewritten by `repair` (no download), so the next autocrlf clone keeps the /// script and index LF. diff --git a/crates/socket-patch-core/src/vendor/jvm/maven_reactor.rs b/crates/socket-patch-core/src/vendor/jvm/maven_reactor.rs index f4a8df996..582e3c1ef 100644 --- a/crates/socket-patch-core/src/vendor/jvm/maven_reactor.rs +++ b/crates/socket-patch-core/src/vendor/jvm/maven_reactor.rs @@ -462,9 +462,11 @@ pub fn wired_checked(read: ReadFn<'_>, c: &Coords<'_>) -> Result, c: &Coords<'_>) -> Result { let sv = c.suffixed_version(); let reactor = Reactor::discover(read)?; @@ -486,20 +488,21 @@ pub fn attestable_checked(read: ReadFn<'_>, c: &Coords<'_>) -> Result:` — the start of `c`'s pin comment. @@ -5520,6 +5523,40 @@ mod tests { assert_eq!(attestable(&after), (true, true)); } + /// #584: a module added after vendoring, or a dependency added to an + /// existing one, that declares the base literally beats the root's + /// pin and builds the unpatched jar: not attestable until re-vendored. + #[test] + fn a_base_declaration_added_after_vendoring_is_not_attestable() { + let files = fs(&[ + ("pom.xml", ROOT), + ("a/pom.xml", &module("a", &dep("1.10.0"))), + ("b/pom.xml", &module("b", "")), + ]); + let (_, after) = vendor(&files); + assert_eq!(attestable(&after), (true, true)); + + let mut added_module = after.clone(); + added_module.insert( + "pom.xml".into(), + text(&after, "pom.xml") + .replace( + "b", + "b\n c", + ) + .into_bytes(), + ); + added_module.insert("c/pom.xml".into(), module("c", &dep("1.10.0")).into_bytes()); + let mut added_dep = after.clone(); + added_dep.insert("b/pom.xml".into(), module("b", &dep("1.10.0")).into_bytes()); + for drifted in [added_module, added_dep] { + assert_eq!(attestable(&drifted), (true, false)); + // Re-vendoring rewrites it and the reactor attests again. + let (_, again) = vendor(&drifted); + assert_eq!(attestable(&again), (true, true)); + } + } + /// #513: a possibly-matching declaration named through an undefined /// `${...}` groupId with its own version beats any pin. #[test] From 5e825379767861f55e84183bc7a449f0b31b22c0 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 29 Oct 2026 01:10:24 +0000 Subject: [PATCH 3/3] Test Maven VEX over an earlier patch's suffix A module still declaring an earlier patch's suffixed version builds that patch's jar, so the reactor must not attest the current one. Pin that behavior with a test. Assisted-by: Claude Code:claude-opus-5-5 --- crates/socket-patch-core/src/vendor/jvm/maven_reactor.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/socket-patch-core/src/vendor/jvm/maven_reactor.rs b/crates/socket-patch-core/src/vendor/jvm/maven_reactor.rs index 582e3c1ef..4aad7d899 100644 --- a/crates/socket-patch-core/src/vendor/jvm/maven_reactor.rs +++ b/crates/socket-patch-core/src/vendor/jvm/maven_reactor.rs @@ -5549,7 +5549,14 @@ mod tests { added_module.insert("c/pom.xml".into(), module("c", &dep("1.10.0")).into_bytes()); let mut added_dep = after.clone(); added_dep.insert("b/pom.xml".into(), module("b", &dep("1.10.0")).into_bytes()); - for drifted in [added_module, added_dep] { + // A module still on an earlier patch's suffix builds that patch's + // jar, not this one. + let mut earlier_patch = after.clone(); + earlier_patch.insert( + "b/pom.xml".into(), + module("b", &dep("1.10.0-socket.deadbeef")).into_bytes(), + ); + for drifted in [added_module, added_dep, earlier_patch] { assert_eq!(attestable(&drifted), (true, false)); // Re-vendoring rewrites it and the reactor attests again. let (_, again) = vendor(&drifted);