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/10/21 05:52:59 UTC
(maven) branch maven-4.0.x updated: Detect recursive configuration property references (#13643, fixes #13638) (#13654)
This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch maven-4.0.x
in repository https://gitbox.apache.org/repos/asf/maven.git
The following commit(s) were added to refs/heads/maven-4.0.x by this push:
new ef9466b98a Detect recursive configuration property references (#13643, fixes #13638) (#13654)
ef9466b98a is described below
commit ef9466b98aec7b808cb73aeb50d26f3a7284eca9
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Wed Oct 21 07:52:52 2026 +0200
Detect recursive configuration property references (#13643, fixes #13638) (#13654)
Cyclic `configProps` references (e.g. `a=${a}` or mutually referencing
`a=${b}, b=${a}`) caused a `StackOverflowError` because `resolveVariable`
returned raw, uninterpolated values from `configProps` without passing
them through the cycle-detection machinery.
Fix: when a `configProps` lookup returns a value that itself contains
`${...}`, pass it through `doSubstVars` with the existing `cycleMap`.
This hooks the recursive interpolation into the same cycle-detection path
already used for direct variable loops, turning infinite recursion into a
clean `InterpolatorException`.
New tests in `DefaultInterpolatorTest`:
- `testSelfReferencingConfigPropertyDifferentCurrentKey` — direct
self-reference with a different current key
- `testSelfReferencingConfigPropertyNullCurrentKey` — direct
self-reference when `currentKey` is null
- `testIndirectCycleConfigProperties` — indirect two-variable cycle
(`a→b→a`)
- `testValidChainedReferenceConfigProperties` — valid chained reference
(`a→b→"hello"`) still resolves correctly
Fixes #13638
Co-authored-by: HARSHA B <ha...@gmail.com>
---
.../maven/impl/model/DefaultInterpolator.java | 4 +++
.../maven/impl/model/DefaultInterpolatorTest.java | 32 ++++++++++++++++++++++
2 files changed, 36 insertions(+)
diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultInterpolator.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultInterpolator.java
index f18a38a7f1..8f23f7b2c3 100644
--- a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultInterpolator.java
+++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultInterpolator.java
@@ -376,6 +376,10 @@ private static String resolveVariable(
// Try configuration properties first
if (configProps != null) {
substValue = configProps.get(variable);
+ if (substValue != null && substValue.contains(DELIM_START)) {
+ substValue = doSubstVars(
+ substValue, variable, cycleMap, configProps, callback, postprocessor, defaultsToEmptyString);
+ }
}
if (substValue == null && !variable.isEmpty() && callback != null) {
String s1 = callback.apply(variable);
diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultInterpolatorTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultInterpolatorTest.java
index 9d332e9a51..0246fd4a07 100644
--- a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultInterpolatorTest.java
+++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultInterpolatorTest.java
@@ -81,6 +81,38 @@ void testSubstLoop() {
"Expected substVars() to throw an InterpolatorException, but it didn't");
}
+ @Test
+ void testSelfReferencingConfigPropertyDifferentCurrentKey() {
+ assertThrows(
+ InterpolatorException.class,
+ () -> substVars("${a}", "outer", Map.of("a", "${a}")),
+ "Expected substVars() to throw an InterpolatorException for self-referencing property");
+ }
+
+ @Test
+ void testSelfReferencingConfigPropertyNullCurrentKey() {
+ assertThrows(
+ InterpolatorException.class,
+ () -> substVars("${a}", null, Map.of("a", "${a}")),
+ "Expected substVars() to throw an InterpolatorException when currentKey is null");
+ }
+
+ @Test
+ void testIndirectCycleConfigProperties() {
+ assertThrows(
+ InterpolatorException.class,
+ () -> substVars("${a}", "outer", Map.of("a", "${b}", "b", "${a}")),
+ "Expected substVars() to throw an InterpolatorException for indirect 2-variable cycle");
+ }
+
+ @Test
+ void testValidChainedReferenceConfigProperties() {
+ assertEquals(
+ "hello",
+ substVars("${a}", "outer", Map.of("a", "${b}", "b", "hello")),
+ "Expected substVars() to resolve chained reference");
+ }
+
@Test
void testLoopEmpty() {
assertEquals("${a}", substVars("${a}", null, null, null, false));