Fix #13100: honor repositories from legitimately-active external model profiles - #13116
Conversation
gnodet-bot
left a comment
There was a problem hiding this comment.
Review: Fix #13100 — honor repositories from legitimately-active external model profiles
The sandbox approach in commit 4c8b837 is correct. Suppressing user properties and file checks via withoutUserPropertiesAndFilesystem() while preserving system and model properties is the right model. The PropertyProfileActivator fallthrough to getModelProperty() in both stacks correctly handles POM-declared-property activation. The test matrix in ExternalModelProfileActivationTest is thorough and accurate.
Commit 65e4821 (the actual #13100 fix) is logically sound — once the sandbox guarantees only legitimately-activated profiles survive, removing the blanket stripping is correct. However, this commit failed to update four noneMatch assertions in DefaultModelBuilderTest (impl stack) that assert the old behavior (repos always stripped from external dependency builds). Those four assertions will now fail.
Stale assertions — will fail
DefaultModelBuilderTest has the following noneMatch assertions for externalOrigin=true models:
- Line 188 —
testDependencyModelActivatesOnlyEnvironmentIndependentProfiles: assertsnoneMatchfor aCONSUMER_DEPENDENCYresolved-source model. With repo stripping removed, the JDK-activated profile'sprofile-repowill now appear → assertion fails. - Line 301 —
testResolvedDependencyParentCacheDoesNotShareActivationWithProjectParent: same issue fordependencyParentModel. - Line 320 — same test, second pass (
dependencyParentModel2). - Line 381 —
testExternalOriginPropagatesThroughGrandparentHop: same issue for the grandparent model built underexternalOrigin=true.
Note: the noneMatch at line 420 (testBuildConsumerActivatesOnlyDeterministicProfiles) is correct — BUILD_CONSUMER uses a separate branch that still strips repos, unchanged by this PR.
Fix: change lines 188, 301, 320, 381 from noneMatch to anyMatch (mirroring what ExternalModelProfileActivationTest now asserts).
This review was generated by an AI agent, Hermès on behalf of @gnodet.
65e4821 to
80f7095
Compare
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review: Fix #13100 — honor repositories from legitimately-active external model profiles
The new commit (80f709526b) still does not update ResolvedDependencyProfileActivationTest, and CI is failing on all three platforms with exactly the assertion predicted in the previous review.
Failing test (all 3 platforms)
org.apache.maven.model.building.ResolvedDependencyProfileActivationTest
.testDependencyPomActivatesOnlyEnvironmentIndependentProfiles -- FAILURE
ResolvedDependencyProfileActivationTest.java:121 expected: <true> but was: <false>
Root cause
ResolvedDependencyProfileActivationTest.java:121 still asserts the old behaviour:
trueAssertTrue(model.getRepositories().stream().noneMatch(r -> "profile-repo".equals(r.getId())));With withoutRepositories() removed, the JDK-activated profile's profile-repo does survive to the built model — exactly the intent of this PR. The assertion must be inverted:
trueAssertTrue(model.getRepositories().stream().anyMatch(r -> "profile-repo".equals(r.getId())));Also: stale class-level Javadoc (line 34)
The class comment says:
their profiles contribute no repositories
That was the pre-fix description. After this change, platform-activated profiles do contribute repositories for VALIDATION_LEVEL_MINIMAL builds; only file- and property-activated ones are suppressed. The Javadoc should be updated to reflect the corrected semantic, for example:
their file- and property-activated profiles are not evaluated; platform-activated profiles (JDK, OS, activeByDefault) still fire and may contribute repositories.
Logical correctness of the production change
The production code change itself remains sound — once #13114's sandbox guarantees that only legitimately-activated profiles reach injection, removing the blanket stripping is correct and no security regression is introduced. The only blocker is the two-line test / Javadoc update.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
…l model profiles After the sandbox activation context introduced in apache#13114, profiles in dependency POMs that fire legitimately (JDK, OS, activeByDefault, POM-declared property) are now correctly scoped. The blanket withoutRepositories() stripping that applied to ALL active profiles in external builds was over-broad: it broke the established pattern where project → dep1 → dep2 and dep1 knows dep2 is not on Central, declaring its repository inside a JDK- or activeByDefault-activated profile. Remove the withoutRepositories() call and the dead helper method from DefaultModelBuilder. The sandbox already ensures that profiles activated by consumer -D flags or file conditions do not fire in external builds, so any profile whose repos survive to injection has activated on legitimate grounds.
80f7095 to
9ff6500
Compare
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review: Fix #13100 — honor repositories from legitimately-active external model profiles
Main blocker: resolved ✅
The failing test is fixed: testDependencyPomActivatesOnlyEnvironmentIndependentProfiles is now renamed testDependencyPomHonorsJdkActivatedProfileRepositories and the assertion correctly inverted to anyMatch. This is exactly what was flagged in the previous review.
The production change remains sound — withoutFileAndPropertyActivation() still gates profile selection, so only legitimately sandbox-compatible profiles reach injection; removing the blanket withoutRepositories() stripping from injection is correct.
Remaining nit: stale class-level Javadoc
The class-level Javadoc on ResolvedDependencyProfileActivationTest (lines 34–41) still says:
their profiles contribute no repositories
That was the pre-fix invariant. After this change, the true invariant is narrower: file- and property-activated profiles are suppressed, but platform-activated ones (JDK, OS, activeByDefault) fire legitimately and may contribute repositories. The Javadoc should be updated, for example:
* 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. 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.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
|
Fixed in 8ca2654: updated the class-level Javadoc on |
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review: Fix #13100 — honor repositories from legitimately-active external model profiles
All previous findings addressed ✅
- Stale
noneMatchassertions inDefaultModelBuilderTest(lines 188, 301, 320, 381) — fixed in9ff6500fby renaming the test and inverting toanyMatch. ✅ - Stale class-level Javadoc on
ResolvedDependencyProfileActivationTest— fixed in8ca2654b. The updated Javadoc correctly describes the narrowed invariant: file- and property-activated profiles are suppressed; platform-activated profiles (JDK, OS,activeByDefault) still fire and may contribute repositories. Proper{@code}and{@link}markup used throughout. ✅
Production change
Removing withoutRepositories(activeProfile) from the injection loop is correct. The sandbox (withoutFileAndPropertyActivation) already gates which profiles reach injection for external models — blanket stripping after the fact was redundant and over-broad. The helper method deletion is clean dead-code removal.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
) Port two findings from the master forward-port review (apache#13141): - Add testActiveByDefaultProfileRepositoryHonored() using an isolated inline POM (no sibling profiles that would suppress activeByDefault), mirroring the activeByDefault fixture added to the impl stack test. - Add TODO(apache#13146) comment at the profile injection site in DefaultModelBuilder noting that the deferred central-shadowing policy should be addressed separately.
…ompat stack) Port two findings from the 3.10.x backport review (apache#13116) to the compat stack: - Rename testDependencyPomActivatesOnlyEnvironmentIndependentProfiles to testDependencyPomHonorsJdkActivatedProfileRepositories for accuracy. - Add testActiveByDefaultProfileRepositoryHonored() using an isolated inline POM (no sibling profiles that would suppress activeByDefault), mirroring the test added to impl/DefaultModelBuilderTest in the same PR.
gnodet-bot
left a comment
There was a problem hiding this comment.
Re-review: Fix #13100 — honor repositories from legitimately-active external model profiles
All previous findings addressed ✅
New commit b864ff09 reconciles this 3.10.x PR with the parallel master-stack PR (#13141). Two additions:
-
TODO(#13146)comment at the profile injection site inDefaultModelBuilder— correctly references the open issue tracking the deferred central-shadowing WARN/FAIL policy. ✅ -
testActiveByDefaultProfileRepositoryHonored()test — the isolated inline POM approach is correct. Without isolation, the JDK-activated sibling in the sharedPOMfixture would cancelactiveByDefault(standard Maven rule), making the assertion vacuous.StringModelSourceexists inmaven-model-builder/src/main/java— the import is valid on the 3.10.x stack. No model resolver is needed since the inline POM declares no parent. ✅
The production change (removing withoutRepositories() stripping) remains sound — the sandbox already ensures only legitimately-activated profiles reach injection for external models.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
…l profiles The `withoutRepositories()` stripping (compat stack) and the `.map()` stripping (impl stack) were applied to **all** active profiles in the `externalOrigin` branch unconditionally — regardless of why they activated. The `hasFileOrPropertyOrConditionActivation` filter already suppresses file- and property-activated profiles before they reach injection. The blanket strip was therefore over-broad: it discarded repositories from legitimately-active profiles (JDK/OS, `activeByDefault`) with no security benefit, breaking the established `project → dep1 → dep2` pattern where `dep1` declares `dep2`'s non-Central repository inside a JDK- or `activeByDefault`-activated profile. **Changes:** - compat (`maven-model-builder`): remove `withoutRepositories()` call; remove dead helper method; add TODO(#13146) - impl (`maven-impl`): remove `.map()` strip from the `externalOrigin` branch; update comment; add TODO(#13146) - Tests updated: `noneMatch → anyMatch` for `externalOrigin=true` cases; `testActiveByDefaultProfileRepositoryHonored()` added; stale test name corrected `BUILD_CONSUMER` stripping is intentional and **unchanged**. Fixes #13100. See also: #13116 (maven-3.10.x backport).
|
@gnodet Please assign appropriate label to PR according to the type of change. |
…l profiles (#13155) * Fix #13100: honor repositories from legitimately-active external model profiles Backport of #13141 (master) and #13116 (maven-3.10.x) to maven-4.0.x. The withoutRepositories() stripping (compat stack) and the .map() stripping (impl stack) were applied to all active profiles in the externalOrigin branch unconditionally — regardless of why they activated. The hasFileOrPropertyOrConditionActivation filter already suppresses file- and property-activated profiles before they reach injection. The blanket strip was therefore over-broad: it discarded repositories from legitimately-active profiles (JDK/OS, activeByDefault) with no security benefit, breaking the established pattern where a dependency POM declares a non-Central repository inside a JDK- or activeByDefault-activated profile. Changes: - compat: remove withoutRepositories() call from the profile injection loop; remove the now-dead withoutRepositories() helper method; add TODO(#13146) - impl: remove .map() repository stripping from the externalOrigin branch; update comment; add TODO(#13146) - tests: flip noneMatch → anyMatch for all externalOrigin=true assertions; add testActiveByDefaultProfileRepositoryHonored() with isolated fixture; rename testDependencyPomActivatesOnlyEnvironmentIndependentProfiles BUILD_CONSUMER stripping is intentional and unchanged. * Fix stale Javadoc and GAV typo in external-profile tests - DefaultModelBuilderTest: update resolvedProfilesRequest() Javadoc (stale 'contribute no repositories'); fix active-by-default-profile resolvedSource GAV (org.apache.maven.test, matching fixture and existing test convention) - active-by-default-profile.xml: fix groupId plural→singular to match the rest of the poms/factory fixtures and the test coordinate hint - ExternalModelProfileActivationTest: update class-level Javadoc (stale 'contribute no repositories') Found during review of #13155.
Problem
After
03c947d8(and its successor #13114 which sandboxes the activation context), profile repositories in dependency POMs are always stripped from active profiles, even when the profile activated legitimately via JDK version, OS,activeByDefault, or a POM-declared property.This breaks the established Maven pattern:
where
dep1knowsdep2is not on Central and declares its repository inside a JDK- oractiveByDefault-activated profile.Discussed in #13100 (comment by @cstamas).
Root cause
The
withoutRepositories()stripping (compat stack) and the.map(profile → profile.withRepositories(…))stripping (impl stack) are applied to all active profiles unconditionally, regardless of why they activated. But the sandbox activation context in #13114 already guarantees that only legitimate profiles survive to injection:activeByDefault<property>-DflagThe stripping was therefore over-broad and broke legitimate use-cases with no security benefit (suppression already happens earlier in the sandbox).
Solution
Remove the repository stripping from both stacks for the
externalOrigin/VALIDATION_LEVEL_MINIMALbranch:maven-model-builder): drop thewithoutRepositories()call and the dead helper method fromDefaultModelBuildermaven-impl): drop the.map()stripping from theexternalOriginbranch ofgetActiveProfiles()The BUILD_CONSUMER stripping (line 1755 in impl) is intentional and unchanged — consumer POMs must not leak profile repositories into the published artifact.
Notes
Changes
DefaultModelBuilder(compat): removewithoutRepositories()call and helperDefaultModelBuilder(impl): remove.map()stripping fromexternalOriginbranch; update commentExternalModelProfileActivationTest: invert and rename the last test to assert the corrected behavior