From 6fc35e587f5824e6fbf355fda9244b5c02361723 Mon Sep 17 00:00:00 2001 From: sahvx655-wq Date: Sat, 20 Jun 2026 19:49:39 +0530 Subject: [PATCH] accept IPv4-embedded IPv6 addresses in UrlValidator isValidAuthority matched a bracketed IPv6 host against IPV6_REGEX before handing it to InetAddressValidator.isValidInet6Address, but the regex only special-cased the upper-case ::FFFF: mapped form and otherwise excluded the dot needed for an embedded IPv4 part, so lower-case ::ffff:1.2.3.4, ::1.2.3.4 and 2001:db8::1.2.3.4 were rejected while the upper-case form was accepted. Widen the bracket pattern to hex, colon and dot and let isValidInet6Address make the decision. --- .../commons/validator/routines/UrlValidator.java | 5 +++-- .../commons/validator/routines/UrlValidatorTest.java | 12 ++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/apache/commons/validator/routines/UrlValidator.java b/src/main/java/org/apache/commons/validator/routines/UrlValidator.java index 6395f2d92..5c6b9059e 100644 --- a/src/main/java/org/apache/commons/validator/routines/UrlValidator.java +++ b/src/main/java/org/apache/commons/validator/routines/UrlValidator.java @@ -118,8 +118,9 @@ public class UrlValidator implements Serializable { // TODO does not allow for optional userinfo. // Validation of character set is done by isValidAuthority private static final String AUTHORITY_CHARS_REGEX = "\\p{Alnum}\\-\\."; // allows for IPV4 but not IPV6 - // Allow for IPv4 mapped addresses: ::FFF:123.123.123.123 - private static final String IPV6_REGEX = "::FFFF:(?:\\d{1,3}\\.){3}\\d{1,3}|[0-9a-fA-F:]+"; // do this as separate match because : could cause ambiguity with port prefix + // Captured inside [ ] in AUTHORITY_REGEX and validated by InetAddressValidator.isValidInet6Address, so the + // dot is allowed for IPv4-mapped/embedded forms (for example ::ffff:1.2.3.4 or 2001:db8::1.2.3.4), not just ::FFFF: + private static final String IPV6_REGEX = "[0-9a-fA-F:.]+"; // the brackets remove the port-prefix ':' ambiguity // userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) // unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" diff --git a/src/test/java/org/apache/commons/validator/routines/UrlValidatorTest.java b/src/test/java/org/apache/commons/validator/routines/UrlValidatorTest.java index 169e17bf7..8c92c2faa 100644 --- a/src/test/java/org/apache/commons/validator/routines/UrlValidatorTest.java +++ b/src/test/java/org/apache/commons/validator/routines/UrlValidatorTest.java @@ -624,6 +624,18 @@ void testValidator452() { assertTrue(urlValidator.isValid("http://[::FFFF:129.144.52.38]:80/index.html")); } + @Test + void testIpv6EmbeddedIpv4() { + final UrlValidator urlValidator = new UrlValidator(); + // ::FFFF: in upper case already worked (testValidator452); the lower-case mapped form + // and the other IPv4-embedded notations must validate the same way. + assertTrue(urlValidator.isValid("http://[::ffff:129.144.52.38]:80/index.html")); + assertTrue(urlValidator.isValid("http://[::1.2.3.4]/")); + assertTrue(urlValidator.isValid("http://[2001:db8::1.2.3.4]/")); + // an embedded IPv4 part with an out-of-range octet is still rejected + assertFalse(urlValidator.isValid("http://[::ffff:129.144.52.999]/")); + } + @Test void testValidator464() { final String[] schemes = { "file" };