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. */