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

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


The following commit(s) were added to refs/heads/master by this push:
     new 714fc5184c Prevent infinite loop in RootLocator when .mvn directory 
exists in subdirectory (fixes #11321) (#11323)
714fc5184c is described below

commit 714fc5184cca6217614c04fbda34fcc28e04d8f4
Author: Guillaume Nodet <[email protected]>
AuthorDate: Tue Oct 28 19:12:24 2025 +0100

    Prevent infinite loop in RootLocator when .mvn directory exists in 
subdirectory (fixes #11321) (#11323)
    
    This is a fix that adds validation to prevent reading parent POMs
    that are located above the discovered root directory. This prevents
    infinite loops when a .mvn directory exists in a subdirectory and
    Maven is invoked with -f pointing to that subdirectory.
    
    The fix includes:
    - Validation in doReadFileModel() to check parent POM location
    - Validation in getEnhancedProperties() to prevent infinite loops
    - Helper method isParentWithinRootDirectory() for path validation
    - Integration test to reproduce and verify the fix
---
 .../maven/impl/model/DefaultModelBuilder.java      | 47 ++++++++++++++-
 .../org/apache/maven/it/MavenITgh11321Test.java    | 67 ++++++++++++++++++++++
 .../deps/.mvn/extensions.xml                       | 23 ++++++++
 .../gh-11321-parent-above-root/deps/pom.xml        | 27 +++++++++
 .../resources/gh-11321-parent-above-root/pom.xml   | 34 +++++++++++
 5 files changed, 196 insertions(+), 2 deletions(-)

diff --git 
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
index 1ac6aff664..4fb88d16fc 100644
--- 
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
@@ -678,8 +678,14 @@ private Map<String, String> getEnhancedProperties(Model 
model, Path rootDirector
             if (!Objects.equals(rootDirectory, model.getProjectDirectory())) {
                 Path rootModelPath = 
modelProcessor.locateExistingPom(rootDirectory);
                 if (rootModelPath != null) {
-                    Model rootModel = 
derive(Sources.buildSource(rootModelPath)).readFileModel();
-                    properties.putAll(getPropertiesWithProfiles(rootModel, 
properties));
+                    // Check if the root model path is within the root 
directory to prevent infinite loops
+                    // This can happen when a .mvn directory exists in a 
subdirectory and parent inference
+                    // tries to read models above the discovered root directory
+                    if (isParentWithinRootDirectory(rootModelPath, 
rootDirectory)) {
+                        Model rootModel =
+                                
derive(Sources.buildSource(rootModelPath)).readFileModel();
+                        properties.putAll(getPropertiesWithProfiles(rootModel, 
properties));
+                    }
                 }
             } else {
                 properties.putAll(getPropertiesWithProfiles(model, 
properties));
@@ -1561,6 +1567,18 @@ Model doReadFileModel() throws ModelBuilderException {
                             pomPath = 
modelProcessor.locateExistingPom(pomPath);
                         }
                         if (pomPath != null && Files.isRegularFile(pomPath)) {
+                            // Check if parent POM is above the root directory
+                            if (!isParentWithinRootDirectory(pomPath, 
rootDirectory)) {
+                                add(
+                                        Severity.FATAL,
+                                        Version.BASE,
+                                        "Parent POM " + pomPath + " is located 
above the root directory "
+                                                + rootDirectory
+                                                + ". This setup is invalid 
when a .mvn directory exists in a subdirectory.",
+                                        parent.getLocation("relativePath"));
+                                throw newModelBuilderException();
+                            }
+
                             Model parentModel =
                                     
derive(Sources.buildSource(pomPath)).readFileModel();
                             String parentGroupId = getGroupId(parentModel);
@@ -2588,4 +2606,29 @@ private static <T, A> List<T> map(List<T> resources, 
BiFunction<T, A, T> mapper,
         }
         return newResources;
     }
+
+    /**
+     * Checks if the parent POM path is within the root directory.
+     * This prevents invalid setups where a parent POM is located above the 
root directory.
+     *
+     * @param parentPath the path to the parent POM
+     * @param rootDirectory the root directory
+     * @return true if the parent is within the root directory, false otherwise
+     */
+    private static boolean isParentWithinRootDirectory(Path parentPath, Path 
rootDirectory) {
+        if (parentPath == null || rootDirectory == null) {
+            return true; // Allow if either is null (fallback behavior)
+        }
+
+        try {
+            Path normalizedParent = parentPath.toAbsolutePath().normalize();
+            Path normalizedRoot = rootDirectory.toAbsolutePath().normalize();
+
+            // Check if the parent path starts with the root directory path
+            return normalizedParent.startsWith(normalizedRoot);
+        } catch (Exception e) {
+            // If there's any issue with path resolution, allow it (fallback 
behavior)
+            return true;
+        }
+    }
 }
diff --git 
a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh11321Test.java 
b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh11321Test.java
new file mode 100644
index 0000000000..60dbb49c24
--- /dev/null
+++ 
b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh11321Test.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.it;
+
+import java.io.File;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * This is a test set for <a 
href="https://github.com/apache/maven/issues/11321";>GH-11321</a>.
+ * Verify that Maven properly rejects setups where a parent POM is located 
above the root directory
+ * when a .mvn directory exists in a subdirectory and Maven is invoked with -f 
pointing to that subdirectory.
+ *
+ * @since 4.0.0
+ */
+public class MavenITgh11321Test extends AbstractMavenIntegrationTestCase {
+
+    /**
+     * Verify that Maven properly rejects setups where a parent POM is located 
above the root directory.
+     * When Maven is invoked with -f deps/ where deps contains a .mvn 
directory, and the deps/pom.xml
+     * uses parent inference to find a parent above the root directory, it 
should fail with a proper error message.
+     *
+     * @throws Exception in case of failure
+     */
+    @Test
+    public void testParentAboveRootDirectoryRejected() throws Exception {
+        File testDir = extractResources("/gh-11321-parent-above-root");
+
+        // First, verify that normal build works from the actual root
+        Verifier verifier = newVerifier(testDir.getAbsolutePath());
+        verifier.addCliArgument("validate");
+        verifier.execute();
+        verifier.verifyErrorFreeLog();
+
+        // Now test with -f pointing to the subdirectory that contains .mvn
+        // This should fail with a proper error message about parent being 
above root
+        verifier = newVerifier(testDir.getAbsolutePath());
+        verifier.addCliArgument("-f");
+        verifier.addCliArgument("deps");
+        verifier.addCliArgument("validate");
+        assertThrows(
+                VerificationException.class,
+                verifier::execute,
+                "Expected validation to fail when using invalid project 
structure");
+        verifier.verifyTextInLog("Parent POM");
+        verifier.verifyTextInLog("is located above the root directory");
+        verifier.verifyTextInLog("This setup is invalid when a .mvn directory 
exists in a subdirectory");
+    }
+}
diff --git 
a/its/core-it-suite/src/test/resources/gh-11321-parent-above-root/deps/.mvn/extensions.xml
 
b/its/core-it-suite/src/test/resources/gh-11321-parent-above-root/deps/.mvn/extensions.xml
new file mode 100644
index 0000000000..91012b377a
--- /dev/null
+++ 
b/its/core-it-suite/src/test/resources/gh-11321-parent-above-root/deps/.mvn/extensions.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<extensions xmlns="http://maven.apache.org/EXTENSIONS/1.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+  xsi:schemaLocation="http://maven.apache.org/EXTENSIONS/1.0.0 
https://maven.apache.org/xsd/core-extensions-1.0.0.xsd";>
+  <!-- Empty extensions file to make .mvn directory a valid root marker -->
+</extensions>
diff --git 
a/its/core-it-suite/src/test/resources/gh-11321-parent-above-root/deps/pom.xml 
b/its/core-it-suite/src/test/resources/gh-11321-parent-above-root/deps/pom.xml
new file mode 100644
index 0000000000..1a2ad35b02
--- /dev/null
+++ 
b/its/core-it-suite/src/test/resources/gh-11321-parent-above-root/deps/pom.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.1.0";>
+  <parent />
+  <artifactId>deps</artifactId>
+  <packaging>pom</packaging>
+
+  <name>Maven Integration Test :: gh-11321 :: Deps Module</name>
+  <description>Module with .mvn directory that uses parent 
inference</description>
+</project>
diff --git 
a/its/core-it-suite/src/test/resources/gh-11321-parent-above-root/pom.xml 
b/its/core-it-suite/src/test/resources/gh-11321-parent-above-root/pom.xml
new file mode 100644
index 0000000000..8679dad63b
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/gh-11321-parent-above-root/pom.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.1.0"; root="true">
+  <groupId>org.apache.maven.its.gh11321</groupId>
+  <artifactId>parent-above-root</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <packaging>pom</packaging>
+
+  <name>Maven Integration Test :: gh-11321 :: Parent Above Root</name>
+  <description>Test that Maven rejects setups where parent POM is above root 
directory</description>
+
+  <properties>
+    <maven.compiler.source>11</maven.compiler.source>
+    <maven.compiler.target>11</maven.compiler.target>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+</project>

Reply via email to