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/07/28 09:32:14 UTC
(maven) 01/01: Fix #12427: Reject path-traversal segments in coordinate ids and versions
This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch backport/12410-path-traversal
in repository https://gitbox.apache.org/repos/asf/maven.git
commit e0ef1e7b8af5ff5f28c581ca0a0ab35150fd038a
Author: jmestwa-coder <jm...@gmail.com>
AuthorDate: Tue Jul 28 15:01:04 2026 +0530
Fix #12427: Reject path-traversal segments in coordinate ids and versions
Coordinate ids (groupId, artifactId) and versions equal to '.' or '..'
pass character validation but become path-traversal segments in the
local repository layout. Both the maven-impl and compat
maven-model-builder validators now reject these values in id checks,
wildcard-id checks, and version checks. Tests added for both modules.
Closes #12410
---
.../model/validation/DefaultModelValidator.java | 28 +++++++++++++++
.../validation/DefaultModelValidatorTest.java | 37 ++++++++++++++++++++
.../coordinate-ids-path-traversal-dot-pom.xml | 34 ++++++++++++++++++
.../coordinate-ids-path-traversal-pom.xml | 34 ++++++++++++++++++
.../maven/impl/model/DefaultModelValidator.java | 28 +++++++++++++++
.../impl/model/DefaultModelValidatorTest.java | 40 ++++++++++++++++++++++
.../coordinate-ids-path-traversal-dot-pom.xml | 34 ++++++++++++++++++
.../coordinate-ids-path-traversal-pom.xml | 34 ++++++++++++++++++
8 files changed, 269 insertions(+)
diff --git a/compat/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java b/compat/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java
index 20e04147c6..704991b5a0 100644
--- a/compat/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java
+++ b/compat/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java
@@ -1151,6 +1151,9 @@ private boolean validateId(
}
private boolean isValidId(String id) {
+ if (isPathTraversalSegment(id)) {
+ return false;
+ }
for (int i = 0; i < id.length(); i++) {
char c = id.charAt(i);
if (!isValidIdCharacter(c)) {
@@ -1160,6 +1163,16 @@ private boolean isValidId(String id) {
return true;
}
+ /**
+ * {@code .} and {@code ..} pass the allowed-character checks, but the default local repository layout uses
+ * ids and versions verbatim as directory names, so these values map onto the {@code .} and {@code ..}
+ * filesystem path segments and escape the coordinate's directory. They are rejected because of that mapping,
+ * not because the names themselves are otherwise invalid.
+ */
+ private static boolean isPathTraversalSegment(String id) {
+ return ".".equals(id) || "..".equals(id);
+ }
+
private boolean isValidIdCharacter(char c) {
return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '-' || c == '_' || c == '.';
}
@@ -1193,6 +1206,9 @@ private boolean validateIdWithWildcards(
}
private boolean isValidIdWithWildCards(String id) {
+ if (isPathTraversalSegment(id)) {
+ return false;
+ }
for (int i = 0; i < id.length(); i++) {
char c = id.charAt(i);
if (!isValidIdWithWildCardCharacter(c)) {
@@ -1644,6 +1660,18 @@ private boolean validateVersion(
return false;
}
+ if (isPathTraversalSegment(string)) {
+ addViolation(
+ problems,
+ severity,
+ version,
+ prefix + fieldName,
+ sourceHint,
+ "must be a valid version but is '" + string + "'.",
+ tracker);
+ return false;
+ }
+
return validateBannedCharacters(
prefix, fieldName, problems, severity, version, string, sourceHint, tracker, ILLEGAL_VERSION_CHARS);
}
diff --git a/compat/maven-model-builder/src/test/java/org/apache/maven/model/validation/DefaultModelValidatorTest.java b/compat/maven-model-builder/src/test/java/org/apache/maven/model/validation/DefaultModelValidatorTest.java
index cfa3b44a70..2952d29c68 100644
--- a/compat/maven-model-builder/src/test/java/org/apache/maven/model/validation/DefaultModelValidatorTest.java
+++ b/compat/maven-model-builder/src/test/java/org/apache/maven/model/validation/DefaultModelValidatorTest.java
@@ -180,6 +180,43 @@ void testInvalidCoordinateIds() throws Exception {
result.getErrors().get(1));
}
+ @Test
+ void testCoordinateIdsWithPathTraversal() throws Exception {
+ SimpleProblemCollector result = validate("coordinate-ids-path-traversal-pom.xml");
+
+ assertTrue(
+ result.getErrors().stream()
+ .anyMatch(m -> m.contains("'artifactId'") && m.contains("does not match a valid id pattern")),
+ "artifactId '..' must be rejected: " + result.getErrors());
+
+ assertTrue(
+ result.getErrors().stream()
+ .anyMatch(m ->
+ m.contains("dependencies.dependency.version") && m.contains("must be a valid version")),
+ "dependency version '..' must be rejected: " + result.getErrors());
+ }
+
+ @Test
+ void testCoordinateIdsWithSingleDotPathTraversal() throws Exception {
+ SimpleProblemCollector result = validate("coordinate-ids-path-traversal-dot-pom.xml");
+
+ assertTrue(
+ result.getErrors().stream()
+ .anyMatch(m -> m.contains("'groupId'") && m.contains("does not match a valid id pattern")),
+ "groupId '..' must be rejected: " + result.getErrors());
+
+ assertTrue(
+ result.getErrors().stream()
+ .anyMatch(m -> m.contains("'artifactId'") && m.contains("does not match a valid id pattern")),
+ "artifactId '.' must be rejected: " + result.getErrors());
+
+ assertTrue(
+ result.getErrors().stream()
+ .anyMatch(m ->
+ m.contains("dependencies.dependency.version") && m.contains("must be a valid version")),
+ "dependency version '.' must be rejected: " + result.getErrors());
+ }
+
@Test
void testMissingType() throws Exception {
SimpleProblemCollector result = validate("missing-type-pom.xml");
diff --git a/compat/maven-model-builder/src/test/resources/poms/validation/coordinate-ids-path-traversal-dot-pom.xml b/compat/maven-model-builder/src/test/resources/poms/validation/coordinate-ids-path-traversal-dot-pom.xml
new file mode 100644
index 0000000000..109789d890
--- /dev/null
+++ b/compat/maven-model-builder/src/test/resources/poms/validation/coordinate-ids-path-traversal-dot-pom.xml
@@ -0,0 +1,34 @@
+<!--
+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>
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>..</groupId>
+ <artifactId>.</artifactId>
+ <version>1.0</version>
+ <packaging>jar</packaging>
+ <dependencies>
+ <dependency>
+ <groupId>other.group</groupId>
+ <artifactId>lib</artifactId>
+ <version>.</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/compat/maven-model-builder/src/test/resources/poms/validation/coordinate-ids-path-traversal-pom.xml b/compat/maven-model-builder/src/test/resources/poms/validation/coordinate-ids-path-traversal-pom.xml
new file mode 100644
index 0000000000..e42e51f2b4
--- /dev/null
+++ b/compat/maven-model-builder/src/test/resources/poms/validation/coordinate-ids-path-traversal-pom.xml
@@ -0,0 +1,34 @@
+<!--
+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>
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>test.group</groupId>
+ <artifactId>..</artifactId>
+ <version>1.0</version>
+ <packaging>jar</packaging>
+ <dependencies>
+ <dependency>
+ <groupId>other.group</groupId>
+ <artifactId>lib</artifactId>
+ <version>..</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelValidator.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelValidator.java
index e503c1f200..e213c852eb 100644
--- a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelValidator.java
+++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelValidator.java
@@ -1687,6 +1687,9 @@ private boolean validateCoordinatesId(
}
private boolean isValidCoordinatesId(String id) {
+ if (isPathTraversalSegment(id)) {
+ return false;
+ }
for (int i = 0; i < id.length(); i++) {
char c = id.charAt(i);
if (!isValidCoordinatesIdCharacter(c)) {
@@ -1696,6 +1699,16 @@ private boolean isValidCoordinatesId(String id) {
return true;
}
+ /**
+ * {@code .} and {@code ..} pass the allowed-character checks, but the default local repository layout uses
+ * coordinate ids and versions verbatim as directory names, so these values map onto the {@code .} and
+ * {@code ..} filesystem path segments and escape the coordinate's directory. They are rejected because of
+ * that mapping, not because the names themselves are otherwise invalid.
+ */
+ private static boolean isPathTraversalSegment(String id) {
+ return ".".equals(id) || "..".equals(id);
+ }
+
private boolean isValidCoordinatesIdCharacter(char c) {
return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '-' || c == '_' || c == '.';
}
@@ -1771,6 +1784,9 @@ private boolean validateCoordinatesIdWithWildcards(
}
private boolean isValidCoordinatesIdWithWildCards(String id) {
+ if (isPathTraversalSegment(id)) {
+ return false;
+ }
for (int i = 0; i < id.length(); i++) {
char c = id.charAt(i);
if (!isValidCoordinatesIdWithWildCardCharacter(c)) {
@@ -2234,6 +2250,18 @@ private boolean validateVersion(
return false;
}
+ if (isPathTraversalSegment(string)) {
+ addViolation(
+ problems,
+ severity,
+ version,
+ prefix + fieldName,
+ sourceHint,
+ "must be a valid version but is '" + string + "'.",
+ tracker);
+ return false;
+ }
+
return validateBannedCharacters(
prefix, fieldName, problems, severity, version, string, sourceHint, tracker, ILLEGAL_VERSION_CHARS);
}
diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelValidatorTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelValidatorTest.java
index c65391872c..1f0329795d 100644
--- a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelValidatorTest.java
+++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelValidatorTest.java
@@ -224,6 +224,46 @@ void testInvalidCoordinateIds() throws Exception {
result.getErrors().get(1));
}
+ @Test
+ void testCoordinateIdsWithPathTraversal() throws Exception {
+ SimpleProblemCollector result = validate("coordinate-ids-path-traversal-pom.xml");
+
+ assertTrue(
+ result.getErrors().stream()
+ .anyMatch(m -> m.contains("'artifactId'")
+ && m.contains("does not match a valid coordinate id pattern")),
+ "artifactId '..' must be rejected: " + result.getErrors());
+
+ assertTrue(
+ result.getErrors().stream()
+ .anyMatch(m ->
+ m.contains("dependencies.dependency.version") && m.contains("must be a valid version")),
+ "dependency version '..' must be rejected: " + result.getErrors());
+ }
+
+ @Test
+ void testCoordinateIdsWithSingleDotPathTraversal() throws Exception {
+ SimpleProblemCollector result = validate("coordinate-ids-path-traversal-dot-pom.xml");
+
+ assertTrue(
+ result.getErrors().stream()
+ .anyMatch(m ->
+ m.contains("'groupId'") && m.contains("does not match a valid coordinate id pattern")),
+ "groupId '..' must be rejected: " + result.getErrors());
+
+ assertTrue(
+ result.getErrors().stream()
+ .anyMatch(m -> m.contains("'artifactId'")
+ && m.contains("does not match a valid coordinate id pattern")),
+ "artifactId '.' must be rejected: " + result.getErrors());
+
+ assertTrue(
+ result.getErrors().stream()
+ .anyMatch(m ->
+ m.contains("dependencies.dependency.version") && m.contains("must be a valid version")),
+ "dependency version '.' must be rejected: " + result.getErrors());
+ }
+
@Test
void testMissingType() throws Exception {
SimpleProblemCollector result = validate("missing-type-pom.xml");
diff --git a/impl/maven-impl/src/test/resources/poms/validation/coordinate-ids-path-traversal-dot-pom.xml b/impl/maven-impl/src/test/resources/poms/validation/coordinate-ids-path-traversal-dot-pom.xml
new file mode 100644
index 0000000000..109789d890
--- /dev/null
+++ b/impl/maven-impl/src/test/resources/poms/validation/coordinate-ids-path-traversal-dot-pom.xml
@@ -0,0 +1,34 @@
+<!--
+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>
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>..</groupId>
+ <artifactId>.</artifactId>
+ <version>1.0</version>
+ <packaging>jar</packaging>
+ <dependencies>
+ <dependency>
+ <groupId>other.group</groupId>
+ <artifactId>lib</artifactId>
+ <version>.</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/impl/maven-impl/src/test/resources/poms/validation/coordinate-ids-path-traversal-pom.xml b/impl/maven-impl/src/test/resources/poms/validation/coordinate-ids-path-traversal-pom.xml
new file mode 100644
index 0000000000..e42e51f2b4
--- /dev/null
+++ b/impl/maven-impl/src/test/resources/poms/validation/coordinate-ids-path-traversal-pom.xml
@@ -0,0 +1,34 @@
+<!--
+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>
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>test.group</groupId>
+ <artifactId>..</artifactId>
+ <version>1.0</version>
+ <packaging>jar</packaging>
+ <dependencies>
+ <dependency>
+ <groupId>other.group</groupId>
+ <artifactId>lib</artifactId>
+ <version>..</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+</project>