From e626bfaa985c6372719ff30d332187ed92eb285a Mon Sep 17 00:00:00 2001 From: Sahana Bogar Date: Tue, 8 Sep 2026 11:53:43 +0530 Subject: [PATCH] reject zone ids and prefix lengths in IPv6 email literals EmailValidator.isValidDomain let InetAddressValidator.isValidInet6Address accept a zone id or CIDR prefix length inside a bracketed IPv6 address literal, which RFC 5321 section 4.1.3 does not allow. Restrict the address group of IP_DOMAIN_REGEX to hex digits, ':' and '.' so such literals no longer match the address-literal pattern. --- .../validator/routines/EmailValidator.java | 6 ++++-- .../validator/routines/EmailValidatorTest.java | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/apache/commons/validator/routines/EmailValidator.java b/src/main/java/org/apache/commons/validator/routines/EmailValidator.java index 460397456..c6cdf28cd 100644 --- a/src/main/java/org/apache/commons/validator/routines/EmailValidator.java +++ b/src/main/java/org/apache/commons/validator/routines/EmailValidator.java @@ -44,9 +44,11 @@ public class EmailValidator implements Serializable { private static final String EMAIL_REGEX = "^(.+)@(\\S+)$"; /** - * RFC 5321 section 4.1.3: an IPv6 address literal carries the "IPv6:" tag (case-insensitive), an IPv4 literal is untagged. + * RFC 5321 section 4.1.3: an IPv6 address literal carries the "IPv6:" tag (case-insensitive), an IPv4 literal is untagged. The address itself is + * built from hex digits, ':' and the embedded IPv4 dotted form only, so a zone id ('%') or prefix length ('/'), which + * {@link InetAddressValidator#isValidInet6Address(String)} tolerates, is kept out of the literal here. */ - private static final String IP_DOMAIN_REGEX = "^\\[((?i)IPv6:)?(.*)\\]$"; + private static final String IP_DOMAIN_REGEX = "^\\[((?i)IPv6:)?([0-9a-fA-F:.]+)\\]$"; private static final String USER_REGEX = "^" + WORD + "(\\." + WORD + ")*$"; private static final Pattern EMAIL_PATTERN = Pattern.compile(EMAIL_REGEX); diff --git a/src/test/java/org/apache/commons/validator/routines/EmailValidatorTest.java b/src/test/java/org/apache/commons/validator/routines/EmailValidatorTest.java index 7c5d2aed9..fafd9434f 100644 --- a/src/test/java/org/apache/commons/validator/routines/EmailValidatorTest.java +++ b/src/test/java/org/apache/commons/validator/routines/EmailValidatorTest.java @@ -461,6 +461,24 @@ void testEmailWithIpv6AddressLiteral() { assertFalse(validator.isValid("someone@[IPv6:216.109.118.76]")); } + /** + * Tests that an IPv6 address literal carrying a zone (scope) id or a prefix length is rejected. RFC 5321 section 4.1.3 builds IPv6-addr from + * hex groups and an optional embedded IPv4 address only, so neither belongs in a mailbox address literal even though + * {@link InetAddressValidator#isValidInet6Address(String)} accepts both. + */ + @Test + void testEmailWithIpv6AddressLiteralZoneOrPrefix() { + assertFalse(validator.isValid("someone@[IPv6:fe80::1%eth0]")); + assertFalse(validator.isValid("someone@[IPv6:fe80::1%25eth0]")); + assertFalse(validator.isValid("someone@[IPv6:2001:db8::1/64]")); + assertFalse(validator.isValid("someone@[IPv6:::1/128]")); + assertFalse(validator.isValid("someone@[216.109.118.76/24]")); + // The bare address forms, including the embedded IPv4 form, still validate. + assertTrue(validator.isValid("someone@[IPv6:fe80::1]")); + assertTrue(validator.isValid("someone@[IPv6:2001:db8::1]")); + assertTrue(validator.isValid("someone@[IPv6:::ffff:216.109.118.76]")); + } + /** * Tests the email validation with numeric domains. */