Skip to content

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

Description

[agent] Found by the scheduled Maven bug-hunt routine (ledger #318).

Summary

The v5 Maven reactor backend (vendor, scan/get --mode vendored) resolves ${property} dependency versions from top-level <properties> only (maven_reactor.rs:711: "Top-level <properties> (profiles excluded)"). Suppose a module declares <version>${ct.version}</version>, the parent sets ct.version=1.10.0, and an active profile (activeByDefault, <jdk>[11,)</jdk>, a property activation…) overrides it to 1.11.0. Maven builds 1.11.0. The planner instead sees 1.10.0, matches the patch base, and replaces ${ct.version} with the literal 1.10.0-socket.<hex8>. It also adds a dependency-management pin in the local root.

After vendoring, the build silently moves from commons-text 1.11.0 down to 1.10.0-socket.1d3c1fd2. vendor exits 0 with applied: 1 and gives no warning about the profile, and VEX then attests not_affected for 1.10.0.

Impact

  • vendoring silently changes the version a profile-driven build links, here a downgrade. Profile-switched library versions (by JDK or by an activation property) are a common pattern.
  • The profile's override goes dead because the literal replaces the ${ct.version} reference.
  • The equivalent non-profile case is already handled. When a module overrides the inherited property in its own top-level <properties>, the planner emits conflicting_literal_version and leaves the root unpinned (inherited_property_overridden_by_a_module_is_left_alone). The profile variant takes the silent path instead.

Expected vs actual

  • Expected (docs/design/maven-vendoring.md, "Maven"): "Rewrites of conflicting base-version literals and resolved local properties, including profiles … conflicting explicit versions … produce specific warnings; the backend does not silently claim those unsupported declarations are patched." A property that an active, or possibly active, profile resolves to something other than the patch base should be treated like the module-override case: warn conflicting_literal_version (or a profile-specific code) and leave the declaration alone. The planner shouldn't rewrite it to the suffixed base.
  • Actual: ${ct.version} is rewritten to 1.10.0-socket.1d3c1fd2 and the local root gets a pin. Exit 0, no warning, and the build resolves the suffixed 1.10.0 instead of 1.11.0.

Repro

Reactor (aggregator → corp-parent, a):

<!-- corp-parent/pom.xml -->
<properties><ct.version>1.10.0</ct.version></properties>

<!-- a/pom.xml (parent = corp-parent) -->
<profiles>
  <profile>
    <id>modern-jdk</id>
    <activation><activeByDefault>true</activeByDefault></activation>   <!-- or <jdk>[11,)</jdk> -->
    <properties><ct.version>1.11.0</ct.version></properties>
  </profile>
</profiles>
<dependencies>
  <dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-text</artifactId>
    <version>${ct.version}</version>
  </dependency>
</dependencies>

Steps: the repo's e2e_vendor_jvm_build harness (prebuilt patch-service mock, staged manifest for pkg:maven/org.apache.commons/commons-text@1.10.0), with this reactor in place of write_reactor:

mvn package dependency:3.5.0:build-classpath -Dmdep.outputFile=target/cp.txt
#  a → ~/.m2/.../commons-text/1.11.0/commons-text-1.11.0.jar
socket-patch vendor --json --offline
#  exit 0, status success, applied 1; only maven_f_outside_root / maven_mirror_of_all degraded notes
#  a/pom.xml:          <version>${ct.version}</version>  →  <version>1.10.0-socket.1d3c1fd2</version>
#  corp-parent/pom.xml: + dependencyManagement pin 1.10.0-socket.1d3c1fd2 + socket-patch-vendor repo
mvn package dependency:3.5.0:build-classpath -Dmdep.outputFile=target/cp.txt
#  a → .socket/vendor/maven2/.../1.10.0-socket.1d3c1fd2/commons-text-1.10.0-socket.1d3c1fd2.jar
socket-patch vex --offline -O vex.json
#  exit 0, commons-text@1.10.0 not_affected

OS × version

OS Maven JDK Activation Result
Linux 3.9.11 21 <jdk>[11,)</jdk> reproduces (silent 1.11.0 → 1.10.0-socket)
Linux 3.9.11 21 activeByDefault reproduces
Linux 4.0.0-rc-7 21 activeByDefault reproduces
Linux 3.6.3 / 3.8.8 21 – blocked (Maven Central 429 during fixture warm-up)
macOS / Windows – – – untested. The planner is pure text logic, so no OS dependence is expected

Tested on main 2463257 (v5 consolidation #277). This isn't a regression: the reactor backend is new in v5.

Suspect code

  • crates/socket-patch-core/src/vendor/jvm/maven_reactor.rs:711-742: Pom::parse collects only project/properties.
  • crates/socket-patch-core/src/vendor/jvm/maven_reactor.rs:930-948: Reactor::lookup never consults profile properties.
  • crates/socket-patch-core/src/vendor/jvm/maven_reactor.rs:1020-1036: the overridden check only looks at inheritors' top-level properties, so a profile override in the declaring pom, or in any pom on its chain, doesn't trip it.

A possible fix: when any profile on the chain (including the declaring pom) defines the property with a value that isn't base-like, take the conflicting_literal_version path, because activation can't be evaluated statically.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions