From f33c3f8eb49ceec783cc50968e413d2f3e144fac Mon Sep 17 00:00:00 2001 From: Giovanni van der Schelde Date: Tue, 16 Dec 2025 14:07:35 +0100 Subject: [PATCH 1/2] [ISSUE-10329] Document behaviour of UrlNormalizer --- .../api/services/model/UrlNormalizer.java | 12 ++++ .../maven/impl/DefaultUrlNormalizerTest.java | 69 +++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 impl/maven-impl/src/test/java/org/apache/maven/impl/DefaultUrlNormalizerTest.java diff --git a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/UrlNormalizer.java b/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/UrlNormalizer.java index 29dab47f0589..231a8272814c 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/UrlNormalizer.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/UrlNormalizer.java @@ -18,6 +18,18 @@ */ package org.apache.maven.api.services.model; +/** + * Provide a service of {@link UrlNormalizer} that simplifies URL strings by removing parent directory + * references ("/../") and collapsing path segments. This implementation performs purely + * string-based normalization without full URL parsing or validation. + * + *

The normalization process iteratively removes "/../" segments by eliminating the preceding path segment, + * effectively resolving relative path traversals. + * + *

Note that this implementation does not guarantee that the resulting URL is valid or reachable; it simply + * produces a more canonical representation of the input string. + * + */ public interface UrlNormalizer { /** diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/DefaultUrlNormalizerTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/DefaultUrlNormalizerTest.java new file mode 100644 index 000000000000..1ea3fa007860 --- /dev/null +++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/DefaultUrlNormalizerTest.java @@ -0,0 +1,69 @@ +/* + * 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.impl; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +class DefaultUrlNormalizerTest { + + private final DefaultUrlNormalizer sut = new DefaultUrlNormalizer(); + + @Test + void normalizeShouldHandleNullAndEdgeCases() { + assertNull(sut.normalize(null)); + assertEquals("", sut.normalize("")); + assertEquals("/", sut.normalize("/../")); + assertEquals("", sut.normalize("a/../")); + assertEquals("b", sut.normalize("a/../b")); + assertEquals("b/d", sut.normalize("a/../b/c/../d")); + assertEquals("b/c/d", sut.normalize("a/../b/c/d")); + assertEquals("b/c", sut.normalize("a/../b/c")); + assertEquals("b/", sut.normalize("a/../b/c/../")); + assertEquals("../", sut.normalize("../")); + } + + @Test + void normalizeShouldPreserveHttpUrlTrailingSlash() { + assertEquals("https://example.com/path", sut.normalize("https://example.com/path")); + assertEquals("https://example.com/path/", sut.normalize("https://example.com/path/")); + } + + @Test + void normalizeShouldCollapseParentReferencesInUrl() { + assertEquals("https://example.com/child", sut.normalize("https://example.com/parent/../child")); + assertEquals("https://example.com/child", sut.normalize("https://example.com/grand/parent/../../child")); + } + + @Test + void normalizeHandlesDoubleSlashesAfterParent() { + assertEquals("https://example.com//child", sut.normalize("https://example.com/parent/..//child")); + assertEquals("https://example.com/child", sut.normalize("https://example.com/parent//../child")); + } + + @Test + void normalizeShouldPreserveOriginalUrlStructure() { + assertEquals("file:////some/server", sut.normalize("file:////some/server")); + assertEquals("https://example.com/a%20b/c%20d", sut.normalize("https://example.com/a%20b/c%20d")); + assertEquals("https://example.com/a b/c d", sut.normalize("https://example.com/a b/c d")); + assertEquals("ht!tps:/bad_url", sut.normalize("ht!tps:/bad_url")); + } +} From cfb5dace385b043108a793b6cae7f74bb8923e8b Mon Sep 17 00:00:00 2001 From: Giovanni van der Schelde Date: Wed, 17 Dec 2025 16:36:43 +0100 Subject: [PATCH 2/2] [ISSUE-10329] Clearify docs --- .../org/apache/maven/api/services/model/UrlNormalizer.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/UrlNormalizer.java b/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/UrlNormalizer.java index 231a8272814c..a1c45bdfc4eb 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/UrlNormalizer.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/api/services/model/UrlNormalizer.java @@ -19,14 +19,13 @@ package org.apache.maven.api.services.model; /** - * Provide a service of {@link UrlNormalizer} that simplifies URL strings by removing parent directory - * references ("/../") and collapsing path segments. This implementation performs purely - * string-based normalization without full URL parsing or validation. + * Simplifies URLs by removing parent directory references ("/../") and collapsing path segments. + * This performs purely string-based normalization without full URL parsing or validation. * *

The normalization process iteratively removes "/../" segments by eliminating the preceding path segment, * effectively resolving relative path traversals. * - *

Note that this implementation does not guarantee that the resulting URL is valid or reachable; it simply + *

This does not guarantee that the resulting URL is valid or reachable; it simply * produces a more canonical representation of the input string. * */