You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by gn...@apache.org on 2026/09/16 12:29:17 UTC

(maven) branch maven-3.10.x updated: Fix #13100: honor repositories from legitimately-active external model profiles

This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch maven-3.10.x
in repository https://gitbox.apache.org/repos/asf/maven.git


The following commit(s) were added to refs/heads/maven-3.10.x by this push:
     new dda1c3f20a Fix #13100: honor repositories from legitimately-active external model profiles
dda1c3f20a is described below

commit dda1c3f20aaf55f88a8f15ecf5e5fc943e54ae21
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Wed Sep 16 14:29:11 2026 +0200

    Fix #13100: honor repositories from legitimately-active external model profiles
    
    The `withoutRepositories()` stripping was applied to all active profiles unconditionally — regardless of why they activated. The `withoutFileAndPropertyActivation` filter already suppresses file- and property-activated profiles before they reach injection, so the blanket strip was over-broad: it discarded repositories from legitimately-active profiles (JDK/OS, `activeByDefault`) with no security benefit.
    
    Removes the stripping from the profile injection loop and the dead `withoutRepositories()` helper. `BUILD_CONSUMER` stripping is intentional and unchanged. Adds TODO(#13146) for the deferred central-shadowing policy.
    
    Fixes #13100.
---
 .../maven/model/building/DefaultModelBuilder.java  | 18 +-----
 .../ResolvedDependencyProfileActivationTest.java   | 66 ++++++++++++++++++++--
 2 files changed, 63 insertions(+), 21 deletions(-)

diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java
index 642caef728..ae3f9b55f2 100644
--- a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java
+++ b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java
@@ -324,12 +324,10 @@ protected ModelBuildingResult build(ModelBuildingRequest request, Collection<Str
             currentData.setActiveProfiles(rawProfiles);
 
             // profile injection
+            // TODO(#13146): repositories contributed by external-model profiles can shadow
+            // central; a WARN/FAIL policy for URL mismatches should be added separately.
             for (Profile activeProfile : activePomProfiles) {
-                profileInjector.injectProfile(
-                        tmpModel,
-                        externalModel ? withoutRepositories(activeProfile) : activeProfile,
-                        request,
-                        problems);
+                profileInjector.injectProfile(tmpModel, activeProfile, request, problems);
             }
 
             if (currentData == resultData) {
@@ -532,16 +530,6 @@ private static List<Profile> withoutFileAndPropertyActivation(List<Profile> prof
         return eligible;
     }
 
-    /**
-     * Returns a copy of the given profile with its repositories and plugin repositories cleared.
-     */
-    private static Profile withoutRepositories(Profile profile) {
-        Profile stripped = profile.clone();
-        stripped.setRepositories(Collections.emptyList());
-        stripped.setPluginRepositories(Collections.emptyList());
-        return stripped;
-    }
-
     @Override
     public ModelBuildingResult build(ModelBuildingRequest request, ModelBuildingResult result)
             throws ModelBuildingException {
diff --git a/maven-model-builder/src/test/java/org/apache/maven/model/building/ResolvedDependencyProfileActivationTest.java b/maven-model-builder/src/test/java/org/apache/maven/model/building/ResolvedDependencyProfileActivationTest.java
index 7ab4747d3f..1fdb785f2f 100644
--- a/maven-model-builder/src/test/java/org/apache/maven/model/building/ResolvedDependencyProfileActivationTest.java
+++ b/maven-model-builder/src/test/java/org/apache/maven/model/building/ResolvedDependencyProfileActivationTest.java
@@ -31,10 +31,10 @@
  * Models built at {@link ModelBuildingRequest#VALIDATION_LEVEL_MINIMAL} come from POMs resolved
  * from a repository during dependency resolution (a dependency POM, one of its parents, or an
  * imported BOM), see for instance {@code DefaultArtifactDescriptorReader#loadPom}. Their file and
- * property activators are not evaluated, and their profiles contribute no repositories. A project
- * build, at {@link ModelBuildingRequest#VALIDATION_LEVEL_STRICT}, still evaluates every activator.
- * Platform-derived activation (JDK version, operating system, activeByDefault) is unaffected at
- * either level.
+ * property activators are not evaluated. Platform-activated profiles (JDK version, OS,
+ * {@code activeByDefault}) still fire and may contribute repositories; only file- and
+ * property-activated profiles are suppressed. A project build, at
+ * {@link ModelBuildingRequest#VALIDATION_LEVEL_STRICT}, still evaluates every activator.
  */
 class ResolvedDependencyProfileActivationTest {
 
@@ -112,12 +112,66 @@ void testProjectBuildEvaluatesAllActivators() throws Exception {
     }
 
     @Test
-    void testDependencyPomActivatesOnlyEnvironmentIndependentProfiles() throws Exception {
+    void testDependencyPomHonorsJdkActivatedProfileRepositories() throws Exception {
         Model model = build(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL);
 
         assertNull(model.getProperties().get("profile.file"));
         assertNull(model.getProperties().get("profile.property"));
         assertEquals("activated", model.getProperties().get("profile.jdk"));
-        assertTrue(model.getRepositories().stream().noneMatch(r -> "profile-repo".equals(r.getId())));
+        // The JDK-activated profile fires legitimately under the sandbox and its
+        // repository must be honored (dep1 → dep2 pattern where dep2 is not on Central).
+        assertTrue(model.getRepositories().stream().anyMatch(r -> "profile-repo".equals(r.getId())));
+    }
+
+    /**
+     * An {@code activeByDefault=true} profile in an external dependency model must contribute
+     * its repositories — the same guarantee as for JDK/OS-activated profiles (see #13100).
+     * Uses an isolated POM with only an activeByDefault profile so the standard Maven rule
+     * ("activeByDefault is suppressed when any other profile activates") does not interfere.
+     */
+    @Test
+    void testActiveByDefaultProfileRepositoryHonored() throws Exception {
+        // Isolated POM: only one profile (activeByDefault=true), no other profiles that could
+        // suppress it.  Without isolation the JDK-activated sibling in POM would cause Maven
+        // to skip the activeByDefault profile entirely, making the test vacuous.
+        String activeByDefaultPom = "<project>\n"
+                + "  <modelVersion>4.0.0</modelVersion>\n"
+                + "  <groupId>thegroup</groupId>\n"
+                + "  <artifactId>active-by-default-profile</artifactId>\n"
+                + "  <version>1</version>\n"
+                + "  <packaging>pom</packaging>\n"
+                + "  <profiles>\n"
+                + "    <profile>\n"
+                + "      <id>always-active</id>\n"
+                + "      <activation>\n"
+                + "        <activeByDefault>true</activeByDefault>\n"
+                + "      </activation>\n"
+                + "      <repositories>\n"
+                + "        <repository>\n"
+                + "          <id>always-active-repo</id>\n"
+                + "          <url>https://repo.example.test/always-active</url>\n"
+                + "        </repository>\n"
+                + "      </repositories>\n"
+                + "    </profile>\n"
+                + "  </profiles>\n"
+                + "</project>\n";
+
+        ModelBuilder builder = new DefaultModelBuilderFactory().newInstance();
+
+        Properties systemProperties = new Properties();
+        systemProperties.putAll(System.getProperties());
+
+        DefaultModelBuildingRequest request = new DefaultModelBuildingRequest();
+        request.setModelSource(new StringModelSource(activeByDefaultPom));
+        request.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL);
+        request.setSystemProperties(systemProperties);
+
+        Model model = builder.build(request).getEffectiveModel();
+
+        // The always-active profile activates by default: its repository must survive external
+        // model resolution unchanged. See #13100, #13116.
+        assertTrue(
+                model.getRepositories().stream().anyMatch(r -> "always-active-repo".equals(r.getId())),
+                "Repository from activeByDefault profile must be retained in external dependency model");
     }
 }