Skip to content

Fix #13084 follow-up: fix mutable map leak in compat sandbox context, pre-filter missing()-condition profiles in CONSUMER_DEPENDENCY builds - #13164

Merged
gnodet merged 1 commit into
apache:masterfrom
gnodet:fix/13084-forward-port-sandbox-review-findings
Sep 17, 2026
Merged

gnodet merged 1 commit into
apache:masterfrom
gnodet:fix/13084-forward-port-sandbox-review-findings

Conversation

@gnodet

@gnodet gnodet commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Forward-port of findings from the review of #13158 (4.0.x backport).

Related: #13163 (Windows glob fix for exists()/missing() conditions, also targets master).

Changes

compat — mutable map leak in externalActivationContext()

The lazy getSystemProperties() implementation returned a mutable map in both branches:

  • empty-projectProps: returned raw delegate.getSystemProperties()
  • non-empty branch: stored and returned an unwrapped HashMap

A caller who mutates the underlying system-properties map after creating the sandbox would see those changes inside, silently violating the "system wins over model properties" invariant. Fixed by wrapping both branches with Collections.unmodifiableMap().

impl — missing() condition profiles incorrectly activate in CONSUMER_DEPENDENCY builds

The nonFileProfiles pre-filter excluded profiles with activation.file != null but not condition profiles whose expression calls missing(path). Inside the sandbox, context.exists() always returns false, so missing(path) = !false = true fires unconditionally — the same footgun as <file><missing> that the file pre-filter was introduced to prevent.

Fix: add hasFileConditionExpression() helper (checks for exists(/missing( in <condition>) and extend the filter to also exclude such profiles.

Tests

Added missing-condition profile to resolved-model-with-profiles.xml:

  • fires in BUILD_PROJECT (path genuinely absent — normal behaviour)
  • suppressed in CONSUMER_DEPENDENCY (pre-filtered by hasFileConditionExpression)

…ntext, pre-filter missing()-condition profiles in CONSUMER_DEPENDENCY builds

Forward-port of findings from the review of PR apache#13158 (4.0.x backport):

- compat: externalActivationContext() returned mutable map from getSystemProperties()
  on both the empty-projectProps branch (raw delegate.getSystemProperties()) and the
  non-empty branch (unwrapped HashMap). Wrap both with Collections.unmodifiableMap().

- impl: the nonFileProfiles pre-filter only excluded profiles with file activation.
  A condition profile using missing(path) passes the filter and enters the sandbox
  where context.exists() always returns false, so missing() = !false = true fires
  unconditionally — the same footgun as <file><missing>. Add hasFileConditionExpression()
  helper and extend the filter to also exclude condition profiles containing exists()/missing().

Tests: extended testProjectBuildEvaluatesAllActivators (missing-condition fires in
project build) and testDependencyModelActivatesOnlyEnvironmentIndependentProfiles
(missing-condition suppressed in CONSUMER_DEPENDENCY).

@gnodet-bot gnodet-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both fixes are correct and well-reasoned.

compat — mutable map leak fix: The lazy-init on the anonymous class (mergedSystemProperties field) is single-threaded per instance — externalActivationContext() creates a new anonymous object per call, so the non-volatile field is safe. The Collections.unmodifiableMap() wrapping on the empty-projectProps branch closes the last mutable-map exposure. Straightforward.

impl — missing()-condition pre-filter: The design is correct. withoutUserPropertiesAndFilesystem() returns exists()=false as a safety net, but that alone incorrectly activates missing(path) profiles (since missing = !false = true). The hasFileConditionExpression() pre-filter eliminates these profiles before they reach the sandbox, which is the right fix. The substring check (contains("exists(") / contains("missing(")) is a conservative over-approximation — false positives (filtering profiles that don't strictly need it) are safe. The compat layer omitting this check is correct: Maven 3 Activation has no <condition> element, so there are no condition-based profiles to worry about there.

Tests: The missing-condition profile added to the fixture exercises both code paths (fires in BUILD_PROJECT, suppressed in CONSUMER_DEPENDENCY). Assertions are specific and accurate.

This review was generated by an AI agent, Hermès on behalf of @gnodet.

@gnodet gnodet added this to the 4.1.0 milestone Sep 17, 2026
@gnodet gnodet added the bug Something isn't working label Sep 17, 2026
@gnodet
gnodet merged commit cce49bc into apache:master Sep 17, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants