Skip to content

Maven pom rewrites add a second <repositories> or <dependencyManagement> section when the existing one is self-closed or has a comment before <dependencies>, so Maven refuses the pom #342

Description

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

Summary

The hosted rewriter (scan --mode hosted) and the vendored wiring (vendor) each look for an existing section by an exact text pattern. When that pattern doesn't match, they write a brand-new section before </project>, even though the pom already has one. Maven's pom reader rejects the result with Non-parseable POM … Duplicated tag, so nothing builds. Meanwhile socket-patch exits 0, reports redirected: 1 / applied: 1 with no warning, and vex attests not_affected.

Three ordinary pom shapes trigger it:

shape mode why the anchor is missed result
<repositories/> (self-closed, empty) hosted insert_maven_repository checks pom.contains("<repositories>") a second <repositories> before </project>
<repositories/> vendored build_repo_edit anchors on </repositories> a second <repositories>
<dependencyManagement> with an XML comment before <dependencies> (e.g. <!-- versions shared across the org -->), and the patched GA is transitive hosted the regex (?s)<dependencyManagement>\s*<dependencies> allows only whitespace between the tags a second <dependencyManagement>
<dependencyManagement/>, patched GA transitive hosted same regex a second <dependencyManagement>

This is a different mechanism from #259, where edits land inside comments or profiles. Here the anchor is missed entirely and a duplicate top-level element is created. Masking comments, as #259 suggests, would not fix the self-closed shapes, and would only fix the comment shape if the regex is also changed.

Impact

  • The build is broken on every Maven line (3.6.3 → 4.0.0-rc-7) right after a successful scan / vendor.
  • The CLI's JSON and vex both claim the patch is in effect: vex --no-verify emits not_affected for the hosted case, and vex --offline does the same for the vendored case.

Repro

Setup: socket-patch 4.0.0 built from main f6b7fb9, and real Apache Maven. For hosted, a local stub API serves the same grant, patched jar and served pom as crates/socket-patch-cli/tests/e2e_redirect_maven_build.rs (commons-text 1.10.0 → 1.10.0-socket.4d5e6f70), and a settings.xml mirror maps socket-patch-<uuid> onto the stub. For vendored, a hand-staged .socket/manifest.json + blob is used, as in e2e_vendor_maven_build.rs.

Hosted, comment inside <dependencyManagement> (commons-text arrives transitively via commons-configuration2 2.9.0):

<project xmlns="http://maven.apache.org/POM/4.0.0">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId><artifactId>app</artifactId><version>1.0.0</version>
  <dependencyManagement>
    <!-- versions shared across the org -->
    <dependencies>
      <dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version></dependency>
    </dependencies>
  </dependencyManagement>
  <dependencies>
    <dependency><groupId>org.apache.commons</groupId><artifactId>commons-configuration2</artifactId><version>2.9.0</version></dependency>
  </dependencies>
</project>
$ socket-patch scan --mode hosted --json --yes --cwd . --api-url http://127.0.0.1:8765 --org test-org --api-token fake
exit 0, redirect.redirected = 1, warnings: [redirect_maven_dep_management_added]
# pom.xml now has a SECOND <dependencyManagement> (holding commons-text 1.10.0-socket.4d5e6f70) after </dependencies>

$ socket-patch vex --no-verify --product pkg:maven/com.example/app@1.0.0 -O vex.json ...
exit 0, not_affected for pkg:maven/org.apache.commons/commons-text@1.10.0

$ mvn -B -s settings.xml org.apache.maven.plugins:maven-dependency-plugin:3.1.2:copy-dependencies
[ERROR] Non-parseable POM /…/pom.xml: Duplicated tag: 'dependencyManagement' (position: START_TAG seen ...</dependencies>\n  <dependencyManagement>... @21:25)
exit 1

Self-closed repositories, hosted or vendored (a direct commons-text:1.10.0 dependency):

<project xmlns="http://maven.apache.org/POM/4.0.0">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId><artifactId>app</artifactId><version>1.0.0</version>
  <repositories/>
  <dependencies>
    <dependency><groupId>org.apache.commons</groupId><artifactId>commons-text</artifactId><version>1.10.0</version></dependency>
  </dependencies>
</project>
$ socket-patch scan --mode hosted ...     # or: socket-patch vendor --json --offline
exit 0, redirected = 1 (applied = 1), no warnings
$ mvn ... copy-dependencies
[ERROR] Non-parseable POM /…/pom.xml: Duplicated tag: 'repositories' (position: START_TAG seen ...</dependencies>\n  <repositories>... @12:17)

Control: the same poms without the self-closed tag or the comment are rewritten in place and resolve the patched jar (PATCHED commons-text-1.10.0-socket.4d5e6f70.jar, and for vendored PATCHED commons-text-1.10.0.jar).

Expected vs actual

  • Expected: the rewrite leaves a pom Maven can read. CLI_CONTRACT.md / docs/ecosystems.md describe hosted Maven as "fail-closed" by pinning the suffixed version, and vendored as a committed file:// repository wired into the project pom. If the tool can't find a safe anchor, it should expand the self-closed element, or refuse with a warning instead of reporting redirected / applied.
  • Actual: a duplicate top-level element, exit 0, a positive VEX, and a build Maven won't load.

Matrix (Linux, JDK 21; each cell run twice on fresh copies)

case Maven 3.6.3 3.8.8 3.9.11 4.0.0-rc-7
hosted, <repositories/> Duplicated tag Duplicated tag Duplicated tag Duplicated tag
hosted, comment in <dependencyManagement> Duplicated tag Duplicated tag Duplicated tag Duplicated tag
hosted, <dependencyManagement/> Duplicated tag Duplicated tag Duplicated tag Duplicated tag
vendored, <repositories/> Duplicated tag Duplicated tag Duplicated tag Duplicated tag
controls (hosted direct / transitive, vendored plain) — — PATCHED PATCHED

macOS and Windows weren't probed. The rewrite is pure text, so it doesn't depend on the OS.

Suspect code (main f6b7fb9)

  • crates/socket-patch-core/src/patch/redirect/mod.rs:7007: insert_maven_repository uses pom.contains("<repositories>"), else a new section before </project> (line 7011).
  • crates/socket-patch-core/src/patch/redirect/mod.rs:7028: the insert_maven_dependency_management regex (?s)<dependencyManagement>\s*<dependencies>, else a new section (line 7037).
  • crates/socket-patch-core/src/vendor/maven_repo.rs:1115: build_repo_edit anchors only on </repositories>, then falls back to a new section before </project> (line 1117).

Related, but a different mechanism: #259 (edits landing in comments/profiles/plugins) and #260 (redirect confirmed by substring, which is why vex accepts these poms).

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