From 0174254d8398f92b86098d3a47f713125a908de6 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Thu, 18 Jun 2026 17:41:05 +0000 Subject: [PATCH 1/2] [#12302] Add RED IT for TransitiveDependencyManager version downgrade MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds integration test that verifies TransitiveDependencyManager does not silently downgrade dependency versions when an intermediate POM's declares a lower version of a transitive dependency. Dependency chain: root → module-a:1.0 (parent=parent-a) → module-b:1.0 → lib-c:2.0 parent-a manages lib-c to 1.0 Expected: lib-c resolves to 2.0 (declared by module-b) Actual (bug): lib-c downgraded to 1.0 by parent-a's dependencyManagement because TransitiveDependencyManager has deriveUntil=MAX_VALUE. This test is expected to FAIL (RED) until the fix is applied. --- ...TransitiveDepMgmtVersionDowngradeTest.java | 84 ++++++++++++++++++ .../pom.xml | 62 +++++++++++++ .../repo/.gitattributes | 2 + .../maven/its/gh12302/lib-c/1.0/lib-c-1.0.jar | Bin 0 -> 322 bytes .../maven/its/gh12302/lib-c/1.0/lib-c-1.0.pom | 9 ++ .../maven/its/gh12302/lib-c/2.0/lib-c-2.0.jar | Bin 0 -> 322 bytes .../maven/its/gh12302/lib-c/2.0/lib-c-2.0.pom | 9 ++ .../its/gh12302/module-a/1.0/module-a-1.0.jar | Bin 0 -> 322 bytes .../its/gh12302/module-a/1.0/module-a-1.0.pom | 22 +++++ .../its/gh12302/module-b/1.0/module-b-1.0.jar | Bin 0 -> 322 bytes .../its/gh12302/module-b/1.0/module-b-1.0.pom | 17 ++++ .../its/gh12302/parent-a/1.0/parent-a-1.0.pom | 19 ++++ .../settings-template.xml | 41 +++++++++ 13 files changed, 265 insertions(+) create mode 100644 its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh12302TransitiveDepMgmtVersionDowngradeTest.java create mode 100644 its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/pom.xml create mode 100644 its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/.gitattributes create mode 100644 its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/lib-c/1.0/lib-c-1.0.jar create mode 100644 its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/lib-c/1.0/lib-c-1.0.pom create mode 100644 its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/lib-c/2.0/lib-c-2.0.jar create mode 100644 its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/lib-c/2.0/lib-c-2.0.pom create mode 100644 its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/module-a/1.0/module-a-1.0.jar create mode 100644 its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/module-a/1.0/module-a-1.0.pom create mode 100644 its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/module-b/1.0/module-b-1.0.jar create mode 100644 its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/module-b/1.0/module-b-1.0.pom create mode 100644 its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/parent-a/1.0/parent-a-1.0.pom create mode 100644 its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/settings-template.xml diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh12302TransitiveDepMgmtVersionDowngradeTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh12302TransitiveDepMgmtVersionDowngradeTest.java new file mode 100644 index 000000000000..6ba89c4df6df --- /dev/null +++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh12302TransitiveDepMgmtVersionDowngradeTest.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.maven.it; + +import java.io.File; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Integration test for gh-12302. + * + *

Verifies that {@code TransitiveDependencyManager} does not silently downgrade + * dependency versions when an intermediate POM's {@code } + * declares a lower version of a transitive dependency. + * + *

Dependency graph: + *

+ *   root (test project)
+ *     └── module-a:1.0 (parent = parent-a:1.0)
+ *           └── module-b:1.0
+ *                 └── lib-c:2.0
+ *
+ *   parent-a has <dependencyManagement> managing lib-c to 1.0
+ * 
+ * + *

Expected: lib-c resolves to 2.0 (declared by module-b). + *
Actual (bug): lib-c is downgraded to 1.0 by parent-a's dependencyManagement + * because {@code TransitiveDependencyManager} has {@code deriveUntil = Integer.MAX_VALUE}, + * collecting managed versions from every POM in the graph. + */ +public class MavenITgh12302TransitiveDepMgmtVersionDowngradeTest extends AbstractMavenIntegrationTestCase { + + MavenITgh12302TransitiveDepMgmtVersionDowngradeTest() { + super("[4.0.0-rc-3,)"); + } + + @Test + public void testTransitiveDependencyManagerDoesNotDowngradeVersions() throws Exception { + File testDir = extractResources("/gh-12302-transitive-dep-mgmt-version-downgrade"); + + Verifier verifier = newVerifier(testDir.getAbsolutePath()); + verifier.setAutoclean(false); + verifier.deleteDirectory("target"); + verifier.deleteArtifacts("org.apache.maven.its.gh12302"); + verifier.filterFile("settings-template.xml", "settings.xml"); + verifier.addCliArgument("--settings"); + verifier.addCliArgument("settings.xml"); + verifier.addCliArgument("validate"); + verifier.execute(); + verifier.verifyErrorFreeLog(); + + List classpath = verifier.loadLines("target/classpath.txt"); + + // lib-c should resolve to 2.0 (as declared by module-b), not 1.0 + // (managed by parent-a's dependencyManagement). + // With the TransitiveDependencyManager bug, lib-c gets downgraded to 1.0. + assertTrue( + classpath.contains("lib-c-2.0.jar"), + "lib-c should be version 2.0 (declared by module-b), not downgraded: " + classpath); + assertFalse( + classpath.contains("lib-c-1.0.jar"), + "lib-c should NOT be downgraded to 1.0 by parent-a's dependencyManagement: " + classpath); + } +} diff --git a/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/pom.xml b/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/pom.xml new file mode 100644 index 000000000000..eb2f79c4a8e1 --- /dev/null +++ b/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/pom.xml @@ -0,0 +1,62 @@ + + + + 4.0.0 + + org.apache.maven.its.gh12302 + test + 1.0 + jar + + Maven Integration Test :: gh-12302 + Verify that TransitiveDependencyManager does not downgrade dependency versions + when an intermediate POM's dependencyManagement declares a lower version. + + + + org.apache.maven.its.gh12302 + module-a + 1.0 + + + + + + + org.apache.maven.its.plugins + maven-it-plugin-dependency-resolution + 2.1-SNAPSHOT + + target/classpath.txt + 1 + + + + resolve + + compile + + validate + + + + + + diff --git a/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/.gitattributes b/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/.gitattributes new file mode 100644 index 000000000000..6d6420ce595a --- /dev/null +++ b/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/.gitattributes @@ -0,0 +1,2 @@ +*.pom text eol=lf +maven-metadata.xml text eol=lf diff --git a/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/lib-c/1.0/lib-c-1.0.jar b/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/lib-c/1.0/lib-c-1.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..ee3ce6555d83aeb8b64dbe2f1297b5af4c248338 GIT binary patch literal 322 zcmWIWW@h1HVBlb2_|SbRh5-q1GO#fCx`sIFdiuHP|2xIN5CBvv!ob17fuU3cs12^v z*U`_@%{4eg&)4m<@0rs+-nx1hdA)VD&Yd~GImqCO@q?#DdS1Rdp1v1LS8WM0VCs4y zshROZTvhG)5-p48;-XK)Ri`nCW`8RBSi}gllbs{{kwcdg&;}3=@MdJn>lflO@P3GilR1F2#H!df7`9>ie)0ELD{LI3~& literal 0 HcmV?d00001 diff --git a/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/lib-c/1.0/lib-c-1.0.pom b/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/lib-c/1.0/lib-c-1.0.pom new file mode 100644 index 000000000000..c969e383a7f1 --- /dev/null +++ b/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/lib-c/1.0/lib-c-1.0.pom @@ -0,0 +1,9 @@ + + + 4.0.0 + + org.apache.maven.its.gh12302 + lib-c + 1.0 + jar + diff --git a/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/lib-c/2.0/lib-c-2.0.jar b/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/lib-c/2.0/lib-c-2.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..ee3ce6555d83aeb8b64dbe2f1297b5af4c248338 GIT binary patch literal 322 zcmWIWW@h1HVBlb2_|SbRh5-q1GO#fCx`sIFdiuHP|2xIN5CBvv!ob17fuU3cs12^v z*U`_@%{4eg&)4m<@0rs+-nx1hdA)VD&Yd~GImqCO@q?#DdS1Rdp1v1LS8WM0VCs4y zshROZTvhG)5-p48;-XK)Ri`nCW`8RBSi}gllbs{{kwcdg&;}3=@MdJn>lflO@P3GilR1F2#H!df7`9>ie)0ELD{LI3~& literal 0 HcmV?d00001 diff --git a/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/lib-c/2.0/lib-c-2.0.pom b/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/lib-c/2.0/lib-c-2.0.pom new file mode 100644 index 000000000000..5aa08a8cbc19 --- /dev/null +++ b/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/lib-c/2.0/lib-c-2.0.pom @@ -0,0 +1,9 @@ + + + 4.0.0 + + org.apache.maven.its.gh12302 + lib-c + 2.0 + jar + diff --git a/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/module-a/1.0/module-a-1.0.jar b/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/module-a/1.0/module-a-1.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..ee3ce6555d83aeb8b64dbe2f1297b5af4c248338 GIT binary patch literal 322 zcmWIWW@h1HVBlb2_|SbRh5-q1GO#fCx`sIFdiuHP|2xIN5CBvv!ob17fuU3cs12^v z*U`_@%{4eg&)4m<@0rs+-nx1hdA)VD&Yd~GImqCO@q?#DdS1Rdp1v1LS8WM0VCs4y zshROZTvhG)5-p48;-XK)Ri`nCW`8RBSi}gllbs{{kwcdg&;}3=@MdJn>lflO@P3GilR1F2#H!df7`9>ie)0ELD{LI3~& literal 0 HcmV?d00001 diff --git a/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/module-a/1.0/module-a-1.0.pom b/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/module-a/1.0/module-a-1.0.pom new file mode 100644 index 000000000000..5c8d0bf728ce --- /dev/null +++ b/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/module-a/1.0/module-a-1.0.pom @@ -0,0 +1,22 @@ + + + 4.0.0 + + + org.apache.maven.its.gh12302 + parent-a + 1.0 + + + module-a + 1.0 + jar + + + + org.apache.maven.its.gh12302 + module-b + 1.0 + + + diff --git a/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/module-b/1.0/module-b-1.0.jar b/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/module-b/1.0/module-b-1.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..ee3ce6555d83aeb8b64dbe2f1297b5af4c248338 GIT binary patch literal 322 zcmWIWW@h1HVBlb2_|SbRh5-q1GO#fCx`sIFdiuHP|2xIN5CBvv!ob17fuU3cs12^v z*U`_@%{4eg&)4m<@0rs+-nx1hdA)VD&Yd~GImqCO@q?#DdS1Rdp1v1LS8WM0VCs4y zshROZTvhG)5-p48;-XK)Ri`nCW`8RBSi}gllbs{{kwcdg&;}3=@MdJn>lflO@P3GilR1F2#H!df7`9>ie)0ELD{LI3~& literal 0 HcmV?d00001 diff --git a/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/module-b/1.0/module-b-1.0.pom b/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/module-b/1.0/module-b-1.0.pom new file mode 100644 index 000000000000..40e8134d7e78 --- /dev/null +++ b/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/module-b/1.0/module-b-1.0.pom @@ -0,0 +1,17 @@ + + + 4.0.0 + + org.apache.maven.its.gh12302 + module-b + 1.0 + jar + + + + org.apache.maven.its.gh12302 + lib-c + 2.0 + + + diff --git a/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/parent-a/1.0/parent-a-1.0.pom b/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/parent-a/1.0/parent-a-1.0.pom new file mode 100644 index 000000000000..9d0c192914b0 --- /dev/null +++ b/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/repo/org/apache/maven/its/gh12302/parent-a/1.0/parent-a-1.0.pom @@ -0,0 +1,19 @@ + + + 4.0.0 + + org.apache.maven.its.gh12302 + parent-a + 1.0 + pom + + + + + org.apache.maven.its.gh12302 + lib-c + 1.0 + + + + diff --git a/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/settings-template.xml b/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/settings-template.xml new file mode 100644 index 000000000000..c25336565b85 --- /dev/null +++ b/its/core-it-suite/src/test/resources/gh-12302-transitive-dep-mgmt-version-downgrade/settings-template.xml @@ -0,0 +1,41 @@ + + + + + + maven-core-it-repo + + + maven-core-it + @baseurl@/repo + + ignore + + + false + + + + + + + maven-core-it-repo + + From 7396e8715a74317e80690331f06b9f959dc0f6d0 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Fri, 19 Jun 2026 12:12:53 +0200 Subject: [PATCH 2/2] [#12302] Strip parent-inherited dependencyManagement from transitive dependency descriptors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TransitiveDependencyManager collects from every POM in the dependency graph. When a POM inherits depMgmt from its parent, those entries leak into the transitive graph and can override version declarations in deeper dependencies, causing unintended version downgrades. The fix strips parent-inherited depMgmt entries in DefaultArtifactDescriptorReader.loadPom() before passing the model to the resolver. Only entries directly declared in the POM (or from its own BOM imports) are kept. This is safe because ArtifactDescriptorReader is only called for transitive dependencies — the root project's depMgmt goes via CollectRequest.setManagedDependencies(). Co-Authored-By: Claude Opus 4.6 --- .../DefaultArtifactDescriptorReader.java | 54 ++++++++++++++++++- .../DefaultArtifactDescriptorReader.java | 44 ++++++++++++++- 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/compat/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultArtifactDescriptorReader.java b/compat/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultArtifactDescriptorReader.java index b7cc1d90811b..b8ca5288843c 100644 --- a/compat/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultArtifactDescriptorReader.java +++ b/compat/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultArtifactDescriptorReader.java @@ -22,12 +22,16 @@ import javax.inject.Named; import javax.inject.Singleton; +import java.util.ArrayList; +import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Properties; +import org.apache.maven.model.Dependency; +import org.apache.maven.model.DependencyManagement; import org.apache.maven.model.Model; import org.apache.maven.model.building.ArtifactModelSource; import org.apache.maven.model.building.DefaultModelBuildingRequest; @@ -262,7 +266,7 @@ private Model loadPom( RequestTraceHelper.interpretTrace(false, request.getTrace())); } } - model = modelResult.getEffectiveModel(); + model = stripInheritedDependencyManagement(modelResult); } catch (ModelBuildingException e) { for (ModelProblem problem : e.getProblems()) { if (problem.getException() instanceof UnresolvableModelException unresolvableModelException) { @@ -300,6 +304,54 @@ private boolean withinSameGav(Artifact a1, Artifact a2) { && Objects.equals(a1.getVersion(), a2.getVersion()); } + /** + * Strips parent-inherited {@code } entries from the effective model. + * Only entries directly declared in the POM (or from its own BOM imports) are kept. + * This prevents {@code TransitiveDependencyManager} from propagating parent-inherited + * management rules through the transitive dependency graph (gh-12302). + */ + private Model stripInheritedDependencyManagement(ModelBuildingResult modelResult) { + Model model = modelResult.getEffectiveModel(); + DependencyManagement effectiveDm = model.getDependencyManagement(); + if (effectiveDm == null || effectiveDm.getDependencies().isEmpty()) { + return model; + } + + // Collect depMgmt declared by parent POMs in the lineage + Map parentManagedVersions = new HashMap<>(); + List modelIds = modelResult.getModelIds(); + for (int i = 1; i < modelIds.size(); i++) { + Model rawParent = modelResult.getRawModel(modelIds.get(i)); + if (rawParent != null && rawParent.getDependencyManagement() != null) { + for (Dependency d : rawParent.getDependencyManagement().getDependencies()) { + parentManagedVersions.putIfAbsent(d.getManagementKey(), d.getVersion()); + } + } + } + + if (parentManagedVersions.isEmpty()) { + return model; + } + + List ownDeps = new ArrayList<>(); + for (Dependency d : effectiveDm.getDependencies()) { + String parentVersion = parentManagedVersions.get(d.getManagementKey()); + if (parentVersion == null || !parentVersion.equals(d.getVersion())) { + ownDeps.add(d); + } + } + + if (ownDeps.size() == effectiveDm.getDependencies().size()) { + return model; + } + + Model result = model.clone(); + DependencyManagement newDm = new DependencyManagement(); + newDm.setDependencies(ownDeps); + result.setDependencyManagement(newDm); + return result; + } + private Properties toProperties(Map dominant, Map recessive) { Properties props = new Properties(); if (recessive != null) { diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/resolver/DefaultArtifactDescriptorReader.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/resolver/DefaultArtifactDescriptorReader.java index 88d168cccf84..de880843a4a7 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/resolver/DefaultArtifactDescriptorReader.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/resolver/DefaultArtifactDescriptorReader.java @@ -18,6 +18,7 @@ */ package org.apache.maven.impl.resolver; +import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; @@ -28,6 +29,8 @@ import org.apache.maven.api.di.Inject; import org.apache.maven.api.di.Named; import org.apache.maven.api.di.Singleton; +import org.apache.maven.api.model.Dependency; +import org.apache.maven.api.model.DependencyManagement; import org.apache.maven.api.model.Model; import org.apache.maven.api.services.ModelBuilder; import org.apache.maven.api.services.ModelBuilderException; @@ -242,7 +245,7 @@ private Model loadPom( RequestTraceHelper.interpretTrace(false, request.getTrace())); } } - model = modelResult.getEffectiveModel(); + model = stripInheritedDependencyManagement(modelResult); } catch (ModelBuilderException e) { for (ModelProblem problem : e.getResult().getProblemCollector().problems().toList()) { @@ -281,6 +284,45 @@ private boolean withinSameGav(Artifact a1, Artifact a2) { && Objects.equals(a1.getVersion(), a2.getVersion()); } + /** + * Strips parent-inherited {@code } entries from the effective model. + * Only entries directly declared in the POM (or from its own BOM imports) are kept. + * This prevents {@code TransitiveDependencyManager} from propagating parent-inherited + * management rules through the transitive dependency graph (gh-12302). + */ + private Model stripInheritedDependencyManagement(ModelBuilderResult modelResult) { + Model model = modelResult.getEffectiveModel(); + DependencyManagement effectiveDm = model.getDependencyManagement(); + if (effectiveDm == null || effectiveDm.getDependencies().isEmpty()) { + return model; + } + + Model parentModel = modelResult.getParentModel(); + DependencyManagement parentDm = parentModel != null ? parentModel.getDependencyManagement() : null; + if (parentDm == null || parentDm.getDependencies().isEmpty()) { + return model; + } + + Map parentManagedVersions = new HashMap<>(); + for (Dependency d : parentDm.getDependencies()) { + parentManagedVersions.put(d.getManagementKey(), d.getVersion()); + } + + List ownDeps = new ArrayList<>(); + for (Dependency d : effectiveDm.getDependencies()) { + String parentVersion = parentManagedVersions.get(d.getManagementKey()); + if (parentVersion == null || !parentVersion.equals(d.getVersion())) { + ownDeps.add(d); + } + } + + if (ownDeps.size() == effectiveDm.getDependencies().size()) { + return model; + } + + return model.withDependencyManagement(effectiveDm.withDependencies(ownDeps)); + } + private Map toProperties(Map dominant, Map recessive) { Map props = new HashMap<>(); if (recessive != null) {