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/15 07:54:58 UTC
(maven) branch maven-4.0.x updated: Treat all loopback hosts as local for external mirror rules (fix #8320) (#13496) (#13525)
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 2c376b2ef0 Treat all loopback hosts as local for external mirror rules (fix #8320) (#13496) (#13525)
2c376b2ef0 is described below
commit 2c376b2ef0d6b185f0d2f42d9a5e50b68421a21f
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Thu Oct 15 09:54:51 2026 +0200
Treat all loopback hosts as local for external mirror rules (fix #8320) (#13496) (#13525)
external:* and external:http:* only treated localhost and 127.0.0.1 as local. Other addresses of the 127.0.0.0/8 range and IPv6 loopback addresses were considered external, and a bracketed IPv6 host was not even parsed. Recognise both, without resolving any name, in the v3 bridge and in the compat mirror selector.
Fixes #8320
(cherry picked from commit 7f2bc3a809d28c9f97b1ee31be8331cb75c4b74a)
Co-authored-by: Arturo Bernal <ab...@apache.org>
---
.../maven/repository/DefaultMirrorSelector.java | 31 ++++++++++++++++++--
.../repository/DefaultMirrorSelectorTest.java | 33 ++++++++++++++++++++++
.../apache/maven/bridge/MavenRepositorySystem.java | 31 ++++++++++++++++++--
.../maven/bridge/MavenRepositorySystemTest.java | 33 ++++++++++++++++++++++
4 files changed, 124 insertions(+), 4 deletions(-)
diff --git a/compat/maven-compat/src/main/java/org/apache/maven/repository/DefaultMirrorSelector.java b/compat/maven-compat/src/main/java/org/apache/maven/repository/DefaultMirrorSelector.java
index 8d11bb1a3d..e87097455c 100644
--- a/compat/maven-compat/src/main/java/org/apache/maven/repository/DefaultMirrorSelector.java
+++ b/compat/maven-compat/src/main/java/org/apache/maven/repository/DefaultMirrorSelector.java
@@ -21,6 +21,8 @@
import javax.inject.Named;
import javax.inject.Singleton;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -129,7 +131,10 @@ else if (EXTERNAL_HTTP_WILDCARD.equals(repo) && isExternalHttpRepo(originalRepos
* external:*} and {@code external:http:*} mirror matching below.
*/
private static final Pattern URL_PROTOCOL_AND_HOST =
- Pattern.compile("([^:/]+(:[^:/]{2,}+(?=://))?):(//([^@/]*@)?([^/:]+))?.*");
+ Pattern.compile("([^:/]+(:[^:/]{2,}+(?=://))?):(//([^@/]*@)?(\\[[^\\]]*\\]|[^/:]+))?.*");
+
+ /** An IPv4 address of the loopback range 127.0.0.0/8. */
+ private static final Pattern IPV4_LOOPBACK = Pattern.compile("127(\\.(25[0-5]|2[0-4]\\d|1?\\d?\\d)){3}");
/**
* Checks the URL to see if this repository refers to an external repository. A URL that
@@ -149,8 +154,30 @@ static boolean isExternalRepo(ArtifactRepository originalRepository) {
return !(isLocal(m.group(5)) || "file".equalsIgnoreCase(m.group(1)));
}
+ /**
+ * Checks whether a host names the local machine: {@code localhost}, an IPv4 address of the loopback
+ * range {@code 127.0.0.0/8} or an IPv6 loopback address in brackets. An IPv4-mapped IPv6 address is judged by its
+ * IPv4 address. The names are not resolved.
+ *
+ * @param host the host of a repository URL, may be {@code null}
+ * @return true if the host is local
+ */
private static boolean isLocal(String host) {
- return "localhost".equals(host) || "127.0.0.1".equals(host);
+ if (host == null) {
+ return false;
+ }
+ if ("localhost".equals(host) || IPV4_LOOPBACK.matcher(host).matches()) {
+ return true;
+ }
+ if (host.startsWith("[") && host.endsWith("]")) {
+ try {
+ // an IP literal is parsed without any name lookup
+ return InetAddress.getByName(host).isLoopbackAddress();
+ } catch (UnknownHostException e) {
+ return false;
+ }
+ }
+ return false;
}
/**
diff --git a/compat/maven-compat/src/test/java/org/apache/maven/repository/DefaultMirrorSelectorTest.java b/compat/maven-compat/src/test/java/org/apache/maven/repository/DefaultMirrorSelectorTest.java
index 358669daef..e985f78f39 100644
--- a/compat/maven-compat/src/test/java/org/apache/maven/repository/DefaultMirrorSelectorTest.java
+++ b/compat/maven-compat/src/test/java/org/apache/maven/repository/DefaultMirrorSelectorTest.java
@@ -65,6 +65,39 @@ void testExternalRepoClassification() {
assertFalse(DefaultMirrorSelector.isExternalRepo(repo("http://127.0.0.1/m2/")));
}
+ @Test
+ void testLoopbackAddressesAreLocal() {
+ for (String url : new String[] {
+ "http://127.0.0.2/m2/",
+ "http://127.255.255.254:8080/m2/",
+ "http://[::1]/m2/",
+ "http://[::1]:8080/m2/",
+ "http://[0:0:0:0:0:0:0:1]:8080/m2/",
+ // an IPv4 loopback address written as IPv4-mapped IPv6 address
+ "http://[::ffff:127.0.0.1]/m2/",
+ "http://[::ffff:7f00:1]:8080/m2/"
+ }) {
+ assertFalse(DefaultMirrorSelector.isExternalRepo(repo(url)), url);
+ assertFalse(DefaultMirrorSelector.isExternalHttpRepo(repo(url)), url);
+ }
+ }
+
+ @Test
+ void testOtherAddressesAreExternal() {
+ for (String url : new String[] {
+ "http://128.0.0.1/m2/",
+ "http://127.0.0.1.example.com/m2/",
+ "http://127.example.com/m2/",
+ "http://[2001:db8::1]/m2/",
+ "http://[2001:db8::1]:8080/m2/",
+ // an IPv4-mapped IPv6 address which is not a loopback one
+ "http://[::ffff:8.8.8.8]/m2/"
+ }) {
+ assertTrue(DefaultMirrorSelector.isExternalRepo(repo(url)), url);
+ assertTrue(DefaultMirrorSelector.isExternalHttpRepo(repo(url)), url);
+ }
+ }
+
private static ArtifactRepository repo(String url) {
return new DefaultArtifactRepository("test", url, null);
}
diff --git a/impl/maven-core/src/main/java/org/apache/maven/bridge/MavenRepositorySystem.java b/impl/maven-core/src/main/java/org/apache/maven/bridge/MavenRepositorySystem.java
index a4f7c1d7ce..00151e8652 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/bridge/MavenRepositorySystem.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/bridge/MavenRepositorySystem.java
@@ -23,6 +23,8 @@
import javax.inject.Singleton;
import java.io.File;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
@@ -777,7 +779,10 @@ else if (EXTERNAL_HTTP_WILDCARD.equals(repo) && isExternalHttpRepo(originalRepos
* external:*} and {@code external:http:*} mirror matching below.
*/
private static final Pattern URL_PROTOCOL_AND_HOST =
- Pattern.compile("([^:/]+(:[^:/]{2,}+(?=://))?):(//([^@/]*@)?([^/:]+))?.*");
+ Pattern.compile("([^:/]+(:[^:/]{2,}+(?=://))?):(//([^@/]*@)?(\\[[^\\]]*\\]|[^/:]+))?.*");
+
+ /** An IPv4 address of the loopback range 127.0.0.0/8. */
+ private static final Pattern IPV4_LOOPBACK = Pattern.compile("127(\\.(25[0-5]|2[0-4]\\d|1?\\d?\\d)){3}");
/**
* Checks the URL to see if this repository refers to an external repository. A URL that
@@ -797,8 +802,30 @@ static boolean isExternalRepo(ArtifactRepository originalRepository) {
return !(isLocal(m.group(5)) || "file".equalsIgnoreCase(m.group(1)));
}
+ /**
+ * Checks whether a host names the local machine: {@code localhost}, an IPv4 address of the loopback
+ * range {@code 127.0.0.0/8} or an IPv6 loopback address in brackets. An IPv4-mapped IPv6 address is judged by its
+ * IPv4 address. The names are not resolved.
+ *
+ * @param host the host of a repository URL, may be {@code null}
+ * @return true if the host is local
+ */
private static boolean isLocal(String host) {
- return "localhost".equals(host) || "127.0.0.1".equals(host);
+ if (host == null) {
+ return false;
+ }
+ if ("localhost".equals(host) || IPV4_LOOPBACK.matcher(host).matches()) {
+ return true;
+ }
+ if (host.startsWith("[") && host.endsWith("]")) {
+ try {
+ // an IP literal is parsed without any name lookup
+ return InetAddress.getByName(host).isLoopbackAddress();
+ } catch (UnknownHostException e) {
+ return false;
+ }
+ }
+ return false;
}
/**
diff --git a/impl/maven-core/src/test/java/org/apache/maven/bridge/MavenRepositorySystemTest.java b/impl/maven-core/src/test/java/org/apache/maven/bridge/MavenRepositorySystemTest.java
index 76c06262c6..fcc1abb460 100644
--- a/impl/maven-core/src/test/java/org/apache/maven/bridge/MavenRepositorySystemTest.java
+++ b/impl/maven-core/src/test/java/org/apache/maven/bridge/MavenRepositorySystemTest.java
@@ -58,6 +58,39 @@ void testExternalRepoClassification() {
assertFalse(MavenRepositorySystem.isExternalRepo(repo("http://127.0.0.1/m2/")));
}
+ @Test
+ void testLoopbackAddressesAreLocal() {
+ for (String url : new String[] {
+ "http://127.0.0.2/m2/",
+ "http://127.255.255.254:8080/m2/",
+ "http://[::1]/m2/",
+ "http://[::1]:8080/m2/",
+ "http://[0:0:0:0:0:0:0:1]:8080/m2/",
+ // an IPv4 loopback address written as IPv4-mapped IPv6 address
+ "http://[::ffff:127.0.0.1]/m2/",
+ "http://[::ffff:7f00:1]:8080/m2/"
+ }) {
+ assertFalse(MavenRepositorySystem.isExternalRepo(repo(url)), url);
+ assertFalse(MavenRepositorySystem.isExternalHttpRepo(repo(url)), url);
+ }
+ }
+
+ @Test
+ void testOtherAddressesAreExternal() {
+ for (String url : new String[] {
+ "http://128.0.0.1/m2/",
+ "http://127.0.0.1.example.com/m2/",
+ "http://127.example.com/m2/",
+ "http://[2001:db8::1]/m2/",
+ "http://[2001:db8::1]:8080/m2/",
+ // an IPv4-mapped IPv6 address which is not a loopback one
+ "http://[::ffff:8.8.8.8]/m2/"
+ }) {
+ assertTrue(MavenRepositorySystem.isExternalRepo(repo(url)), url);
+ assertTrue(MavenRepositorySystem.isExternalHttpRepo(repo(url)), url);
+ }
+ }
+
private static ArtifactRepository repo(String url) {
return new MavenArtifactRepository("test", url, null, null, null);
}