[agent] Found by the scheduled Maven bug-hunt routine (ledger #318).
Summary
vendor patches a Maven reactor correctly, and the build resolves the vendored 1.10.0-socket.* jar. But when any module's <parent><relativePath> names a directory rather than a pom.xml file, socket-patch vex and socket-patch vendor --check then refuse it:
<relativePath>../corp-parent</relativePath> → cannot establish JVM wiring: corp-parent: <proj>/corp-parent is not a regular file
<relativePath>..</relativePath> → cannot establish JVM wiring: unsafe path ""
Maven accepts both forms; it appends pom.xml to a directory. The repo's own reactor capstone uses the first one (B_POM in crates/socket-patch-cli/tests/e2e_vendor_jvm_build.rs), but the capstone never runs vex or vendor --check.
Impact
vex exits 1 with no_applicable_patches and omits a patch that really is applied (vendor_jvm_shape_unsupported). Users can't produce a VEX document for a correctly patched reactor.
vendor --check exits 1 with vendor_check_failed, so a CI gate on vendor --check goes red on every correctly vendored reactor that uses a directory relativePath.
- It fails closed (no false attestation), but it hits a common, valid layout.
<relativePath>..</relativePath> and <relativePath>../parent</relativePath> are widespread.
Root cause (suspect)
local_parent_of probes the bare relativePath before <path>/pom.xml:
|
let candidates = [ |
|
path.clone(), |
|
if path.is_empty() { |
|
"pom.xml".to_string() |
|
} else { |
|
format!("{path}/pom.xml") |
|
}, |
|
]; |
|
let Some(parent_rel) = candidates |
|
.into_iter() |
|
.find(|c| poms.contains_key(c) || read(c).is_some()) |
During vendor, a directory read just counts as "missing", and the pom.xml candidate wins. During vex / vendor --check, the read goes through ProjectReader::read, which records any error other than NotFound (InvalidInput for a directory, unsafe path "" for the root) in read_error:
|
pub fn read(&self, rel: &str) -> Option<Vec<u8>> { |
|
let path = match self.resolve(rel) { |
|
Ok(path) => path, |
|
Err(e) => { |
|
self.read_error.borrow_mut().get_or_insert(e); |
|
return None; |
|
} |
|
}; |
|
match group_commit::read(&path).unwrap_or_else(|| read_regular_to_bytes_sync(&path)) { |
|
Ok(bytes) => Some(bytes), |
|
Err(e) if e.kind() == std::io::ErrorKind::NotFound => None, |
|
Err(e) => { |
|
self.read_error |
|
.borrow_mut() |
|
.get_or_insert(format!("{rel}: {e}")); |
|
None |
|
} |
|
} |
entry_wired_checked then turns any recorded read error into a hard failure, even though the planner went on to find the parent through the next candidate:
|
if let Some(e) = reader.read_error.borrow().as_ref() { |
|
return Err(e.clone()); |
|
} |
(used by
vex at
crates/socket-patch-cli/src/commands/vex_sources.rs:347 and by
check_entry at
apply.rs:770).
Repro
The stock reactor fixture from e2e_vendor_jvm_build::maven_reactor (aggregator root, corp-parent/, module a with commons-text:1.10.0 at <relativePath>../corp-parent/pom.xml</relativePath>, module b with <relativePath>../corp-parent</relativePath>), plus a staged patch manifest, the same as the capstone:
socket-patch vendor --json --offline # applied: 1
mvn -B package org.apache.maven.plugins:maven-dependency-plugin:3.5.0:build-classpath -Dmdep.outputFile=target/cp.txt
# a/target/cp.txt and b/target/cp.txt → .socket/vendor/maven2/.../commons-text-1.10.0-socket.1d3c1fd2.jar (patched)
socket-patch vex --json --offline --output vex.json
# exit 1, error.code no_applicable_patches, warning vendor_jvm_shape_unsupported:
# "cannot establish JVM wiring: corp-parent: <proj>/corp-parent is not a regular file"
socket-patch vendor --check --json --offline
# exit 1, event failed / vendor_check_failed: "corp-parent: <proj>/corp-parent is not a regular file"
Control: change only b's <relativePath> to ../corp-parent/pom.xml. Then vex exits 0 with not_affected, and vendor --check exits 0.
.. variant: module a inherits the root aggregator with <relativePath>..</relativePath> (b uses the pom.xml form). Then both vex and vendor --check fail with unsafe path "".
Expected vs actual
- Expected:
docs/design/maven-vendoring.md says vendor --check "checks artifact hashes, recorded tree files, wiring…" and fails only on a real mismatch. vex should attest a patch whose wiring is in place (the build demonstrably resolves the patched jar). Maven resolves a directory relativePath by appending pom.xml, as the planner itself does when vendoring.
- Actual: both commands fail with a filesystem error on the probe of the bare directory, which the planner then resolves anyway.
Matrix (Linux, JDK 21, main 61cfb9b)
| Maven |
../corp-parent (dir) |
.. |
../corp-parent/pom.xml (control) |
| 3.6.3 |
fail (vex 1, check 1) |
untested |
— |
| 3.8.8 |
fail (vex 1, check 1) |
untested |
— |
| 3.9.11 |
fail (reproduced 2×) |
fail (unsafe path "") |
pass (vex not_affected, check 0) |
| 4.0.0-rc-7 |
fail (vex 1, check 1) |
untested |
— |
The defect is in path probing, not in Maven itself: the build resolves the patched jar on every line, so the Maven version only matters for the build oracle. macOS and Windows were not probed. The code path is OS-independent apart from the error kind for a directory read, which on Windows is PermissionDenied, not NotFound, so it's expected to fail the same way.
First bad commit
2463257 (#277, the v5 consolidation, which introduced ProjectReader::read_error and the JVM entry_wired_checked). It isn't in any release yet (latest release 4.0.0).
[agent] Found by the scheduled Maven bug-hunt routine (ledger #318).
Summary
vendorpatches a Maven reactor correctly, and the build resolves the vendored1.10.0-socket.*jar. But when any module's<parent><relativePath>names a directory rather than apom.xmlfile,socket-patch vexandsocket-patch vendor --checkthen refuse it:<relativePath>../corp-parent</relativePath>→cannot establish JVM wiring: corp-parent: <proj>/corp-parent is not a regular file<relativePath>..</relativePath>→cannot establish JVM wiring: unsafe path ""Maven accepts both forms; it appends
pom.xmlto a directory. The repo's own reactor capstone uses the first one (B_POMincrates/socket-patch-cli/tests/e2e_vendor_jvm_build.rs), but the capstone never runsvexorvendor --check.Impact
vexexits 1 withno_applicable_patchesand omits a patch that really is applied (vendor_jvm_shape_unsupported). Users can't produce a VEX document for a correctly patched reactor.vendor --checkexits 1 withvendor_check_failed, so a CI gate onvendor --checkgoes red on every correctly vendored reactor that uses a directoryrelativePath.<relativePath>..</relativePath>and<relativePath>../parent</relativePath>are widespread.Root cause (suspect)
local_parent_ofprobes the barerelativePathbefore<path>/pom.xml:socket-patch/crates/socket-patch-core/src/vendor/jvm/maven_reactor.rs
Lines 1164 to 1174 in 61cfb9b
During
vendor, a directory read just counts as "missing", and thepom.xmlcandidate wins. Duringvex/vendor --check, the read goes throughProjectReader::read, which records any error other than NotFound (InvalidInputfor a directory,unsafe path ""for the root) inread_error:socket-patch/crates/socket-patch-core/src/vendor/jvm/apply.rs
Lines 164 to 181 in 61cfb9b
entry_wired_checkedthen turns any recorded read error into a hard failure, even though the planner went on to find the parent through the next candidate:socket-patch/crates/socket-patch-core/src/vendor/jvm/apply.rs
Lines 700 to 702 in 61cfb9b
vexatcrates/socket-patch-cli/src/commands/vex_sources.rs:347and bycheck_entryatapply.rs:770).Repro
The stock reactor fixture from
e2e_vendor_jvm_build::maven_reactor(aggregator root,corp-parent/, moduleawithcommons-text:1.10.0at<relativePath>../corp-parent/pom.xml</relativePath>, modulebwith<relativePath>../corp-parent</relativePath>), plus a staged patch manifest, the same as the capstone:Control: change only
b's<relativePath>to../corp-parent/pom.xml. Thenvexexits 0 withnot_affected, andvendor --checkexits 0...variant: moduleainherits the root aggregator with<relativePath>..</relativePath>(b uses thepom.xmlform). Then bothvexandvendor --checkfail withunsafe path "".Expected vs actual
docs/design/maven-vendoring.mdsaysvendor --check"checks artifact hashes, recorded tree files, wiring…" and fails only on a real mismatch.vexshould attest a patch whose wiring is in place (the build demonstrably resolves the patched jar). Maven resolves a directoryrelativePathby appendingpom.xml, as the planner itself does when vendoring.Matrix (Linux, JDK 21, main
61cfb9b)../corp-parent(dir)..../corp-parent/pom.xml(control)unsafe path "")not_affected, check 0)The defect is in path probing, not in Maven itself: the build resolves the patched jar on every line, so the Maven version only matters for the build oracle. macOS and Windows were not probed. The code path is OS-independent apart from the error kind for a directory read, which on Windows is
PermissionDenied, not NotFound, so it's expected to fail the same way.First bad commit
2463257(#277, the v5 consolidation, which introducedProjectReader::read_errorand the JVMentry_wired_checked). It isn't in any release yet (latest release 4.0.0).