You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Vendored Maven reactor reports applied and VEX attests not_affected when a ${property} version is left unresolved, so the build keeps Central's unpatched jar (also triggered by Maven 4 <parent/> inference) #513
[agent] Found by the scheduled Maven bug-hunt routine (ledger #318).
Summary
When a reactor module declares the patched GA as <version>${prop}</version> and the v5 reactor planner (vendor/jvm/maven_reactor.rs) can't resolve ${prop}, it emits vendor_jvm_degraded / property_unresolved ("left as is, probably unpatched"). It leaves the declaration alone but still writes the module's <dependencyManagement> pin and the socket-patch-vendor repository. Maven applies the module's explicit ${prop} version over its own management, so the build keeps Central's unpatched 1.10.0 jar. All the same:
vendor exits 0 with status: success and summary.applied: 1.
vex exits 0 and emits not_affected for pkg:maven/org.apache.commons/commons-text@1.10.0. Its only warning is vendored_tree_out_of_sync ("the attestation is based on the committed .socket/vendor artifact (the lockfile consumes it)"), but the build doesn't consume that artifact.
There are two common ways to hit this:
Maven 3 and 4: the property comes from a parent outside the checkout. For example, a corp parent or spring-boot-starter-parent (<relativePath/>) defines <ct.version>1.10.0</ct.version> and a module uses ${ct.version}. The warning is accurate here, but applied: 1 and the VEX attestation are not.
Maven 4 (model 4.1.0): parent inference. A module with <parent/> (no coordinates; Maven 4 infers the parent from ../pom.xml) uses ${ct.version}, which is defined in the root pom in the checkout. local_parent_of compares the parent's groupId/artifactId (both None) with the root's, finds no match, and treats the parent as remote. The module then becomes its own local root, and the planner reports the property as "not defined in the checkout", which is false. The build is unpatched in the same way, and VEX still attests.
Impact
VEX says not_affected for a vulnerability the shipped artifact still has, and vendor counts the patch as applied. The degraded warning is easy to miss among the always-present maven_f_outside_root / maven_mirror_of_all degraded events (every run in this report emitted all three). Property-managed versions inherited from an external parent are a very common Maven layout. docs/design/maven-vendoring.md says unresolved properties "produce specific warnings; the backend does not silently claim those unsupported declarations are patched", but the VEX attestation is exactly such a claim.
Repro
I used the repo's own harness: a local, uncommitted copy of e2e_vendor_jvm_build::maven_reactor (its warm_fixture, stage_manifest and prebuilt_common::prepare_command fixture server, plus the commons-text 1.10.0 NOTICE marker patch, uuid 1d3c1fd2-…), with only the fixture POMs swapped. The classpath oracle is mvn package dependency:build-classpath from a fresh checkout, with 1.10.0 and the suffixed version purged from the local repository.
Case 1, external parent property (Maven 3.6.3 → 4.0.0-rc-7):
<!-- installed in the local repo: com.corp:corp-parent:3 -->
<project><modelVersion>4.0.0</modelVersion><groupId>com.corp</groupId><artifactId>corp-parent</artifactId><version>3</version><packaging>pom</packaging>
<properties><ct.version>1.10.0</ct.version></properties></project>
<!-- pom.xml (root) -->
<project><modelVersion>4.0.0</modelVersion>
<parent><groupId>com.corp</groupId><artifactId>corp-parent</artifactId><version>3</version><relativePath/></parent>
<groupId>com.example</groupId><artifactId>root</artifactId><version>1.0.0</version><packaging>pom</packaging>
<modules><module>a</module></modules></project>
<!-- a/pom.xml -->
<project><modelVersion>4.0.0</modelVersion>
<parent><groupId>com.example</groupId><artifactId>root</artifactId><version>1.0.0</version></parent>
<artifactId>a</artifactId>
<dependencies><dependency><groupId>org.apache.commons</groupId><artifactId>commons-text</artifactId><version>${ct.version}</version></dependency></dependencies></project>
Case 2, Maven 4 parent inference (4.0.0-rc-7; nothing outside the checkout):
Then, with the commons-text@1.10.0 patch staged in .socket/manifest.json:
socket-patch vendor --json --offline
# exit 0, status success, summary.applied 1# events: … vendor_jvm_degraded "reason: property_unresolved: a/pom.xml:6: org.apache.commons:commons-text# version ${ct.version} is not defined in the checkout; left as is, probably unpatched"# a/pom.xml gains <dependencyManagement> commons-text 1.10.0-socket.1d3c1fd2 + the socket-patch-vendor <repository># (case 2: the module is wired as its own root; the root pom is untouched)
socket-patch vex --json --offline --output vex.json
# exit 0, events[0] {action: verified, status: not_affected}, warning vendored_tree_out_of_sync# fresh checkout, commons-text purged from the local repo:
mvn package dependency:build-classpath -Dmdep.outputFile=target/cp.txt
# a/target/cp.txt -> …/m2/org/apache/commons/commons-text/1.10.0/commons-text-1.10.0.jar (Central's, unpatched)
Expected vs actual
Expected: a declaration the planner leaves "probably unpatched" must not be counted or attested as patched (docs/design/maven-vendoring.md: "the backend does not silently claim those unsupported declarations are patched"). That means vendor reports the patch as not applied (or fails it), and vex omits it, as it already does for the unpinned-root case (vendor_unwired, see the Vendored Maven reactor ignores profile <properties>, so a version an active profile raises is silently downgraded to the patched base (1.11.0 → 1.10.0-socket.*) with exit 0 and no warning #459 reverse-case comment). In case 2, the planner should also follow Maven 4.1.0 parent inference (<parent/> / a parent with only <relativePath> → ../pom.xml), so the root's property resolves and the declaration is rewritten.
Actual:applied: 1, exit 0, VEX not_affected exit 0, and the build ships Central's unpatched jar. In case 2 the warning also wrongly says the property isn't in the checkout.
Matrix (Linux, JDK 21, main 61cfb9b)
Maven
Case 1: external-parent ${prop}
Case 2: 4.1.0 <parent/> + root ${prop}
Controls: 4.1.0 <parent/> with a literal 1.10.0, or versionless + root dM
3.6.3
unpatched, VEX attests
n/a (no 4.1.0 model)
n/a
3.8.8
unpatched, VEX attests
n/a
n/a
3.9.11
unpatched, VEX attests (2/2)
n/a
n/a
4.0.0-rc-7
unpatched, VEX attests
unpatched, VEX attests (2/2)
patched (pass)
This is planner/VEX logic with no OS dependence, so I ran no macOS/Windows probe. Not bisected: the reactor backend is new in v5 (#277).
Suspect code
crates/socket-patch-core/src/vendor/jvm/maven_reactor.rs:1001-1013: property_unresolved pushes a degraded warning and continues. The root is not added to unpinned (compare the conflicting_literal_version branches, which do unpinned.insert(root)), so the pin, the repository and the "applied" outcome all stand.
crates/socket-patch-core/src/vendor/jvm/maven_reactor.rs:1142-1196 (local_parent_of): a <parent/> with no groupId/artifactId never matches candidate.effective_group() / candidate.artifact, so Maven 4 inferred parents count as remote.
crates/socket-patch-cli/src/commands/vex_sources.rs (liveness, vendor_unwired): liveness is satisfied by the module's own pin, although the module's explicit version overrides it.
Related, but a different root cause: #488 (an external BOM/parent version is overridden by the pin, a downgrade), #459 (profile properties).
[agent] Found by the scheduled Maven bug-hunt routine (ledger #318).
Summary
When a reactor module declares the patched GA as
<version>${prop}</version>and the v5 reactor planner (vendor/jvm/maven_reactor.rs) can't resolve${prop}, it emitsvendor_jvm_degraded/property_unresolved("left as is, probably unpatched"). It leaves the declaration alone but still writes the module's<dependencyManagement>pin and thesocket-patch-vendorrepository. Maven applies the module's explicit${prop}version over its own management, so the build keeps Central's unpatched 1.10.0 jar. All the same:vendorexits 0 withstatus: successandsummary.applied: 1.vexexits 0 and emitsnot_affectedforpkg:maven/org.apache.commons/commons-text@1.10.0. Its only warning isvendored_tree_out_of_sync("the attestation is based on the committed .socket/vendor artifact (the lockfile consumes it)"), but the build doesn't consume that artifact.There are two common ways to hit this:
spring-boot-starter-parent(<relativePath/>) defines<ct.version>1.10.0</ct.version>and a module uses${ct.version}. The warning is accurate here, butapplied: 1and the VEX attestation are not.<parent/>(no coordinates; Maven 4 infers the parent from../pom.xml) uses${ct.version}, which is defined in the root pom in the checkout.local_parent_ofcompares the parent'sgroupId/artifactId(bothNone) with the root's, finds no match, and treats the parent as remote. The module then becomes its own local root, and the planner reports the property as "not defined in the checkout", which is false. The build is unpatched in the same way, and VEX still attests.Impact
VEX says
not_affectedfor a vulnerability the shipped artifact still has, andvendorcounts the patch as applied. The degraded warning is easy to miss among the always-presentmaven_f_outside_root/maven_mirror_of_alldegraded events (every run in this report emitted all three). Property-managed versions inherited from an external parent are a very common Maven layout. docs/design/maven-vendoring.md says unresolved properties "produce specific warnings; the backend does not silently claim those unsupported declarations are patched", but the VEX attestation is exactly such a claim.Repro
I used the repo's own harness: a local, uncommitted copy of
e2e_vendor_jvm_build::maven_reactor(itswarm_fixture,stage_manifestandprebuilt_common::prepare_commandfixture server, plus the commons-text 1.10.0 NOTICE marker patch, uuid1d3c1fd2-…), with only the fixture POMs swapped. The classpath oracle ismvn package dependency:build-classpathfrom a fresh checkout, with 1.10.0 and the suffixed version purged from the local repository.Case 1, external parent property (Maven 3.6.3 → 4.0.0-rc-7):
Case 2, Maven 4 parent inference (4.0.0-rc-7; nothing outside the checkout):
Then, with the commons-text@1.10.0 patch staged in
.socket/manifest.json:Expected vs actual
vendorreports the patch as not applied (or fails it), andvexomits it, as it already does for the unpinned-root case (vendor_unwired, see the Vendored Maven reactor ignores profile <properties>, so a version an active profile raises is silently downgraded to the patched base (1.11.0 → 1.10.0-socket.*) with exit 0 and no warning #459 reverse-case comment). In case 2, the planner should also follow Maven 4.1.0 parent inference (<parent/>/ a parent with only<relativePath>→../pom.xml), so the root's property resolves and the declaration is rewritten.applied: 1, exit 0, VEXnot_affectedexit 0, and the build ships Central's unpatched jar. In case 2 the warning also wrongly says the property isn't in the checkout.Matrix (Linux, JDK 21, main
61cfb9b)${prop}<parent/>+ root${prop}<parent/>with a literal 1.10.0, or versionless + root dMThis is planner/VEX logic with no OS dependence, so I ran no macOS/Windows probe. Not bisected: the reactor backend is new in v5 (#277).
Suspect code
crates/socket-patch-core/src/vendor/jvm/maven_reactor.rs:1001-1013:property_unresolvedpushes a degraded warning andcontinues. The root is not added tounpinned(compare theconflicting_literal_versionbranches, which dounpinned.insert(root)), so the pin, the repository and the "applied" outcome all stand.crates/socket-patch-core/src/vendor/jvm/maven_reactor.rs:1142-1196(local_parent_of): a<parent/>with nogroupId/artifactIdnever matchescandidate.effective_group()/candidate.artifact, so Maven 4 inferred parents count as remote.crates/socket-patch-cli/src/commands/vex_sources.rs(liveness,vendor_unwired): liveness is satisfied by the module's own pin, although the module's explicit version overrides it.Related, but a different root cause: #488 (an external BOM/parent version is overridden by the pin, a downgrade), #459 (profile properties).