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 13:44:14 UTC

(maven) branch fix/13084-sandbox-external-profile-activation created (now 02973106c9)

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

gnodet pushed a change to branch fix/13084-sandbox-external-profile-activation
in repository https://gitbox.apache.org/repos/asf/maven.git


      at 02973106c9 Merge origin/master: adopt #13100 repo-honoring into sandbox approach

This branch includes the following new commits:

     new 02973106c9 Merge origin/master: adopt #13100 repo-honoring into sandbox approach

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(maven) 01/01: Merge origin/master: adopt #13100 repo-honoring into sandbox approach

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch fix/13084-sandbox-external-profile-activation
in repository https://gitbox.apache.org/repos/asf/maven.git

commit 02973106c96fa171ff6a284b1336467547a0d9d1
Merge: f2bf110b0a 28962de8ed
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Wed Sep 16 13:44:00 2026 +0000

    Merge origin/master: adopt #13100 repo-honoring into sandbox approach
    
    Conflicts resolved:
    - impl/maven-impl/.../DefaultModelBuilder.java: keep sandbox activation
      (withoutUserPropertiesAndFilesystem) but drop repo stripping per #13100.
      Repositories from legitimately-active external profiles (JDK/OS/activeByDefault
      and POM-property-gated) are now honored, fixing the dep1→dep2 repo pattern.
    - compat/.../ExternalModelProfileActivationTest.java: keep comprehensive sandbox
      tests (file, user-prop, sys-prop, POM-declared, negated, JDK), update repo
      assertion from noneMatch→anyMatch, add testActiveByDefaultProfileRepositoryHonored
      from #13100.

 .github/workflows/maven.yml                        |  16 +-
 THREAT_MODEL.md                                    |   3 +
 apache-maven/pom.xml                               |  57 +-
 apache-maven/src/assembly/component.xml            |   8 +
 apache-maven/src/assembly/maven/bin/mvn            | 206 ++++++-
 apache-maven/src/assembly/maven/bin/mvn.cmd        | 214 ++++++-
 .../src/main/appended-resources/META-INF/NOTICE.vm |   6 +
 .../apache/maven/messages/maven.version.properties |  21 +
 .../src/test/scripts/test-mvn-fast-version.sh      | 231 ++++++++
 .../src/main/java/org/apache/maven/api/Event.java  |  66 ++-
 .../api/{Listener.java => ExecutionEvent.java}     |  34 +-
 .../api/{Listener.java => ExecutionEventType.java} |  29 +-
 .../org/apache/maven/api/ExecutionListener.java    | 192 +++++++
 .../main/java/org/apache/maven/api/Listener.java   |  21 +-
 .../java/org/apache/maven/api/RepositoryEvent.java |  70 +++
 .../{Listener.java => RepositoryEventType.java}    |  31 +-
 .../org/apache/maven/api/RepositoryListener.java   | 209 +++++++
 .../org/apache/maven/api/RepositoryMetadata.java   |  93 ++++
 .../main/java/org/apache/maven/api/Session.java    |  16 +-
 .../main/java/org/apache/maven/api/SourceRoot.java |   8 +
 .../apache/maven/api/services/ProjectManager.java  |  19 +
 .../apache/maven/api/services/RequestTrace.java    |   7 +
 .../java/org/apache/maven/api/SourceRootTest.java  |   2 +
 .../maven/api/services/RequestTraceTest.java       |  11 +
 api/maven-api-model/src/main/mdo/maven.mdo         |  15 +
 .../artifact/versioning/ComparableVersion.java     |  31 +-
 .../artifact/versioning/ComparableVersionTest.java | 171 ++++++
 .../versioning/DefaultArtifactVersionTest.java     |  35 ++
 .../artifact/versioning/VersionRangeTest.java      |  44 ++
 .../internal/BootstrapCoreExtensionManager.java    |  17 +-
 .../maven/model/building/DefaultModelBuilder.java  |  16 +-
 .../ExternalModelProfileActivationTest.java        |  66 ++-
 .../maven/model/io/xpp3/MavenXpp3Reader.java       |  10 +
 .../maven/model/io/xpp3/MavenXpp3ReaderTest.java   |  49 ++
 .../metadata/io/xpp3/MetadataXpp3Reader.java       |  25 +
 .../maven/settings/io/xpp3/SettingsXpp3Reader.java |  25 +
 .../model/io/xpp3/MavenToolchainsXpp3Reader.java   |   5 +
 .../extensions/BootstrapCoreExtensionManager.java  |  18 +-
 .../apache/maven/cling/invoker/LookupInvoker.java  |   2 +-
 .../mvnup/goals/AbstractUpgradeStrategy.java       |  91 ++-
 .../invoker/mvnup/goals/MvnupSessionHolder.java    |  93 ++++
 .../invoker/mvnup/goals/PluginUpgradeStrategy.java |  12 +-
 .../mvnup/goals/ToolchainPluginStrategy.java       | 171 +++++-
 .../mvnup/goals/ToolchainPluginStrategyTest.java   | 194 +++++++
 impl/maven-core/pom.xml                            |  88 +++
 .../org/apache/maven/execution/MavenSession.java   |   8 +-
 .../DefaultRepositorySystemSessionFactory.java     |   4 +-
 .../apache/maven/internal/impl/DefaultEvent.java   |  32 +-
 .../internal/impl/DefaultLifecycleRegistry.java    |   5 +-
 .../maven/internal/impl/DefaultProjectManager.java |  27 +-
 .../apache/maven/internal/impl/DefaultSession.java |  26 +-
 .../apache/maven/internal/impl/EventSpyImpl.java   |  22 +-
 .../org/apache/maven/lifecycle/PluginVersions.java |  81 +++
 .../internal/concurrent/BuildPlanExecutor.java     |  77 ++-
 .../lifecycle/internal/concurrent/BuildStep.java   |   2 +-
 .../AbstractLifecycleMappingProvider.java          |  25 -
 .../packaging/BomLifecycleMappingProvider.java     |   6 +-
 .../packaging/EarLifecycleMappingProvider.java     |  12 +-
 .../packaging/EjbLifecycleMappingProvider.java     |  18 +-
 .../packaging/JarLifecycleMappingProvider.java     |  18 +-
 .../MavenPluginLifecycleMappingProvider.java       |  20 +-
 .../packaging/PomLifecycleMappingProvider.java     |   6 +-
 .../packaging/RarLifecycleMappingProvider.java     |  18 +-
 .../packaging/WarLifecycleMappingProvider.java     |  18 +-
 .../internal/PluginDependenciesResolver.java       |  26 +-
 .../apache/maven/project/ConnectedResource.java    |   3 +-
 .../maven/project/DefaultProjectBuilder.java       | 215 +++++---
 .../maven/lifecycle/plugin-versions.properties     |  35 ++
 .../src/site/markdown/default-bindings.md.vm       |   2 +-
 .../maven/MavenLifecycleParticipantTest.java       |  17 +
 .../apache/maven/execution/MavenSessionTest.java   | 118 ++++
 .../DefaultRepositorySystemSessionFactoryTest.java |  41 ++
 .../internal/impl/DefaultProjectManagerTest.java   |  28 +
 .../maven/internal/impl/DefaultSessionTest.java    | 106 ++++
 .../maven/internal/impl/EventSpyImplTest.java      | 337 +++++++++++
 .../impl/ConsumerPomBuilderTest.java               |   2 +
 .../apache/maven/lifecycle/PluginVersionsTest.java |  54 ++
 .../internal/concurrent/BuildPlanCreatorTest.java  |  40 +-
 .../internal/concurrent/BuildPlanExecutorTest.java | 289 ++++++++++
 ...uginDependenciesResolverDefaultMethodsTest.java | 158 ++++++
 .../org/apache/maven/project/MavenProjectTest.java |  13 +
 .../apache/maven/project/ProjectBuilderTest.java   | 153 +++++
 .../apache/maven/project/ResourceIncludeTest.java  |  20 +
 .../current-project-selection/dependency/pom.xml   |   6 +
 .../current-project-selection/pom.xml              |  19 +
 .../test/resources/consumer/multi-module/pom.xml   |   3 +-
 .../org/apache/maven/impl/AbstractSession.java     |  45 +-
 .../apache/maven/impl/DefaultArtifactDeployer.java |   6 +-
 .../maven/impl/DefaultArtifactInstaller.java       |   8 +-
 .../impl/DefaultDependencyResolverResult.java      |   6 +-
 .../apache/maven/impl/DefaultModelXmlFactory.java  |  16 +-
 .../apache/maven/impl/DefaultRepositoryEvent.java  | 161 ++++++
 .../org/apache/maven/impl/DefaultSourceRoot.java   |  62 ++-
 .../apache/maven/impl/MavenRepositoryListener.java | 160 ++++++
 .../org/apache/maven/impl/RequestTraceHelper.java  |  16 +-
 .../maven/impl/model/DefaultModelBuilder.java      | 614 +++++++++++++--------
 .../maven/impl/model/DefaultModelInterpolator.java |   6 +-
 .../maven/impl/model/DefaultModelObjectPool.java   |  38 +-
 .../maven/impl/model/profile/ConditionParser.java  |  72 ++-
 .../profile/OperatingSystemProfileActivator.java   |   2 +-
 .../apache/maven/impl/standalone/ApiRunner.java    |  21 +-
 .../impl/DefaultDependencyResolverResultTest.java  | 147 +++++
 .../maven/impl/DefaultModelXmlFactoryTest.java     |  69 +++
 .../apache/maven/impl/DefaultSourceRootTest.java   |  17 +
 .../maven/impl/MavenRepositoryListenerTest.java    | 347 ++++++++++++
 .../maven/impl/RepositoryRequestTraceTest.java     |  93 ++++
 .../apache/maven/impl/RequestTraceHelperTest.java  |  32 +-
 .../maven/impl/model/DefaultModelBuilderTest.java  |  36 +-
 .../impl/model/DefaultModelInterpolatorTest.java   |  20 +
 .../maven/impl/model/ParentTraversalTest.java      | 448 +++++++++++++++
 .../impl/model/profile/ConditionParserTest.java    |  35 ++
 .../OperatingSystemProfileActivatorTest.java       |  17 +
 .../maven/impl/standalone/RequestTraceTest.java    |  32 +-
 .../poms/factory/active-by-default-profile.xml     |  37 ++
 .../maven/testing/plugin/stubs/SessionStub.java    |   8 +-
 its/core-it-suite/pom.xml                          |   2 +-
 .../MavenITMvnupToolchainPluginStrategyTest.java   |   3 +
 ...h13068LegacyPluginDependenciesResolverTest.java |  63 +++
 .../client/.mvn/extensions.xml                     |  26 +
 .../client/pom.xml                                 |  33 ++
 .../extension/pom.xml                              |  79 +++
 .../gh13068/LegacyPluginDependenciesResolver.java  |  90 +++
 .../maven-it-plugin-class-loader/pom.xml           |   2 +-
 .../org/apache/maven/plugin/coreit/InfoReport.java |   8 +
 its/core-it-support/pom.xml                        |   2 +-
 its/pom.xml                                        |   6 +-
 pom.xml                                            |  24 +-
 src/mdo/model.vm                                   |  19 +-
 128 files changed, 7064 insertions(+), 722 deletions(-)

diff --cc compat/maven-model-builder/src/test/java/org/apache/maven/model/building/ExternalModelProfileActivationTest.java
index 40a9b520b3,43c51162e4..5ae15fda7e
--- a/compat/maven-model-builder/src/test/java/org/apache/maven/model/building/ExternalModelProfileActivationTest.java
+++ b/compat/maven-model-builder/src/test/java/org/apache/maven/model/building/ExternalModelProfileActivationTest.java
@@@ -28,24 -28,16 +28,26 @@@ import static org.junit.jupiter.api.Ass
  import static org.junit.jupiter.api.Assertions.assertTrue;
  
  /**
 - * 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. 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. Repositories declared in legitimately-active profiles (JDK/OS/activeByDefault) are
 - * honored in external model builds — stripping them would break the established
 - * {@code project → dep1 → dep2} pattern where dep1 declares dep2's non-Central repository inside
 - * a JDK- or activeByDefault-activated profile.
 + * Tests for profile activation in external (repository-resolved) model builds, i.e. those at
 + * {@link ModelBuildingRequest#VALIDATION_LEVEL_MINIMAL} (dependency POMs, parent POMs, imported BOMs).
 + *
 + * <h2>Activation rules for external models</h2>
 + * <ul>
 + *   <li><b>JDK / OS / activeByDefault</b> — always re-activated: they are platform facts.</li>
 + *   <li><b>File</b> — suppressed: publisher-local paths don't exist on the consumer's machine.</li>
 + *   <li><b>Property (user / -D)</b> — suppressed: consumer flags were not set for that dependency
 + *       and must not accidentally activate its profiles.</li>
 + *   <li><b>Property (system)</b> — re-activated: system properties are platform facts
 + *       ({@code java.version}, {@code os.name}, …).</li>
 + *   <li><b>Property (POM-declared, from &lt;properties&gt;)</b> — re-activated: the POM's own
 + *       properties are part of the artifact's published identity, not the consumer's environment.
 + *       A profile conditioned on a POM-declared property is publisher-intent, not consumer-injected.</li>
 + *   <li><b>Negated property (-D absent)</b> — re-activated (default-on): the property is absent from
 + *       the sandboxed context, so {@code !foo} evaluates to true, preserving the publisher's intent.</li>
-  *   <li><b>Profile repositories</b> — always stripped from external models regardless of activation.</li>
++ *   <li><b>Profile repositories</b> — honored from legitimately-active profiles (JDK/OS/activeByDefault
++ *       and POM-property-gated); stripping them would break the established
++ *       {@code project → dep1 → dep2} pattern. See #13100.</li>
 + * </ul>
   */
  class ExternalModelProfileActivationTest {
  
@@@ -276,23 -115,66 +278,75 @@@
      }
  
      @Test
 -    void testDependencyPomHonorsJdkActivatedProfileRepositories() throws Exception {
 -        Model model = build(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL);
 +    void testExternalModelJdkActivationIsPreserved() throws Exception {
 +        // JDK activation is a platform fact and must fire in external builds.
 +        Model model = build(
 +                ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL, systemPropertiesWithTestValues(), new Properties());
  
 -        assertNull(model.getProperties().get("profile.file"));
 -        assertNull(model.getProperties().get("profile.property"));
 -        assertEquals("activated", model.getProperties().get("profile.jdk"));
 +        assertEquals("activated", model.getProperties().get("profile.jdk"), "JDK profile must fire in external build");
 +    }
 +
 +    @Test
-     void testExternalModelProfileRepositoriesAreAlwaysStripped() throws Exception {
-         // Profile repositories must never be contributed by external models, regardless of
-         // which profiles are active, to prevent dependency POMs injecting unwanted repositories.
++    void testExternalModelJdkProfileRepositoriesAreHonored() throws Exception {
+         // Repositories from legitimately-active profiles (JDK-activated) must be honored:
+         // stripping them would break the project → dep1 → dep2 pattern. See #13100, #13116.
 -        assertTrue(model.getRepositories().stream().anyMatch(r -> "profile-repo".equals(r.getId())));
 +        Model model = build(
 +                ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL, systemPropertiesWithTestValues(), new Properties());
 +
 +        assertTrue(
-                 model.getRepositories().stream().noneMatch(r -> "profile-repo".equals(r.getId())),
-                 "profile repository must NOT be present in external model build");
++                model.getRepositories().stream().anyMatch(r -> "profile-repo".equals(r.getId())),
++                "profile repository must be present in external model build (JDK-activated profile)");
+     }
+ 
+     /**
+      * 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, #13141.
+         assertTrue(
+                 model.getRepositories().stream().anyMatch(r -> "always-active-repo".equals(r.getId())),
+                 "Repository from activeByDefault profile must be retained in external dependency model");
      }
  }
diff --cc impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilder.java
index 366917463c,f19dd77a58..1466509ef4
--- a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilder.java
+++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilder.java
@@@ -1718,36 -1625,25 +1626,37 @@@ public class DefaultModelBuilder implem
          private List<Profile> getActiveProfiles(
                  Collection<Profile> interpolatedProfiles, DefaultProfileActivationContext profileActivationContext) {
              if (isBuildRequestWithActivation()) {
 -                Collection<Profile> eligibleProfiles = interpolatedProfiles;
                  if (externalOrigin) {
                      // A model resolved to satisfy dependency resolution -- a dependency POM
 -                    // itself, or one of its parents, reached transitively -- evaluates only
 -                    // platform-derived activation (JDK version, operating system,
 -                    // activeByDefault). Repository stripping is intentionally omitted here:
 -                    // file and property activation is already excluded by the
 -                    // hasFileOrPropertyOrConditionActivation filter below, so only
 -                    // legitimately-active profiles reach injection. Stripping their
 -                    // repositories would break the established project → dep1 → dep2 pattern
 -                    // where dep1 declares dep2's repository inside an activeByDefault or
 -                    // JDK-activated profile.
 -                    eligibleProfiles = interpolatedProfiles.stream()
 -                            .filter(profile -> !hasFileOrPropertyOrConditionActivation(profile))
 +                    // itself, or one of its parents, reached transitively -- evaluates profiles
 +                    // against a sandboxed activation context.  The sandbox:
 +                    //  - merges model properties into system property lookups (so POM-declared
 +                    //    <properties> drive activation via the existing property lookup chain),
 +                    //  - suppresses user properties (consumer -D flags must not activate
 +                    //    dependency profiles — they were not set for that artifact),
 +                    //  - disables file existence checks (publisher paths don't exist here).
 +                    // Model properties are merged into system properties rather than adding
 +                    // a separate lookup step in PropertyProfileActivator, because changing the
 +                    // activator would affect ALL profile evaluations (including the build's own
 +                    // project) and cause unintended profile activation when a POM declares a
 +                    // property that matches a profile's activation condition.
 +                    // File-activated profiles are pre-filtered (not just sandboxed) because
 +                    // returning false from exists() would incorrectly activate <missing> profiles.
-                     // Repository contributions from external profiles are always stripped.
++                    // Repository stripping is intentionally omitted: only legitimately-active
++                    // profiles (JDK/OS/activeByDefault and POM-property-gated) reach injection,
++                    // and stripping their repositories would break the established
++                    // project → dep1 → dep2 pattern. See #13100.
++                    // TODO(#13146): repositories contributed by external-model profiles can shadow
++                    // central; a WARN/FAIL policy for URL mismatches should be added separately.
 +                    Collection<Profile> nonFileProfiles = interpolatedProfiles.stream()
 +                            .filter(p -> p.getActivation() == null
 +                                    || p.getActivation().getFile() == null)
                              .toList();
 +                    ProfileActivationContext externalContext =
 +                            profileActivationContext.withoutUserPropertiesAndFilesystem();
-                     List<Profile> activeProfiles =
-                             profileSelector.getActiveProfiles(nonFileProfiles, externalContext, this);
-                     return activeProfiles.stream()
-                             .map(profile -> profile.withRepositories(List.of()).withPluginRepositories(List.of()))
-                             .toList();
++                    return profileSelector.getActiveProfiles(nonFileProfiles, externalContext, this);
                  }
 -                // TODO(#13146): repositories contributed by external-model profiles can shadow
 -                // central; a WARN/FAIL policy for URL mismatches should be added separately.
 -                return profileSelector.getActiveProfiles(eligibleProfiles, profileActivationContext, this);
 +                return profileSelector.getActiveProfiles(interpolatedProfiles, profileActivationContext, this);
              } else {
                  // BUILD_CONSUMER: activate only deterministic profiles whose activation is a
                  // function of the build platform (OS, JDK version, activeByDefault) rather than