diff --git a/src/validators/email.py b/src/validators/email.py index 46cc594..3b59d8a 100644 --- a/src/validators/email.py +++ b/src/validators/email.py @@ -91,8 +91,8 @@ def email( + r"(\.[\u0100-\u017F\u0180-\u024F\u00A0-\u00FF0-9a-z!#$%&'*+/=?^_`{}|~\-]+)*$" # quoted-string + r'|^"(' - + r"[\u0100-\u017F\u0180-\u024F\u00A0-\u00FF\001-\010\013\014\016-\037" - + r"!#-\[\]-\177]|\\[\011.]" + + r"[\u0100-\u017F\u0180-\u024F\u00A0-\u00FF" + + r"!#-\[\]-~]|\\[\011.]" + r')*")$', username_part, re.IGNORECASE, diff --git a/tests/test_email_control_character.py b/tests/test_email_control_character.py new file mode 100644 index 0000000..2373d11 --- /dev/null +++ b/tests/test_email_control_character.py @@ -0,0 +1,20 @@ +"""HU-02: reject ASCII control characters in quoted SMTP local parts.""" + +# external +import pytest + +import validators + + +@pytest.mark.parametrize( + "control_code", + [1, 2, 7, 11, 12, 14, 27, 31, 127], +) + +def test_email_rejects_control_characters(control_code: int): + """Reject selected ASCII control characters in quoted local parts.""" + email = f'"user{chr(control_code)}name"@example.com' + + result = validators.email(email) + + assert isinstance(result, validators.ValidationError), repr(result) diff --git a/tests/test_email_ipv6_literal.py b/tests/test_email_ipv6_literal.py new file mode 100644 index 0000000..a4f208a --- /dev/null +++ b/tests/test_email_ipv6_literal.py @@ -0,0 +1,20 @@ +"""HU-02: accept the IPv6: prefix in an SMTP address literal.""" + +# external +import pytest + +import validators + + +@pytest.mark.parametrize( + ("email",), + [ + ("usuario@[IPv6:2001:db8::1]", ), + ], +) +def test_email_accepts_ipv6_literal_with_prefix(email: str): + """Email accepts ipv6 literal with prefix.""" + result = validators.email( + email, ipv6_address=True + ) + assert result is True, repr(result) diff --git a/tests/test_email_quoted_at_sign.py b/tests/test_email_quoted_at_sign.py new file mode 100644 index 0000000..02d7b1f --- /dev/null +++ b/tests/test_email_quoted_at_sign.py @@ -0,0 +1,18 @@ +"""HU-02: accept @ inside a quoted local part.""" + +# external +import pytest + +import validators + + +@pytest.mark.parametrize( + ("email",), + [ + ('"user@name"@example.com', ), + ], +) +def test_email_accepts_at_sign_inside_quoted_local_part(email: str): + """The email accepts at sign inside quoted local part character.""" + result = validators.email(email) + assert result is True, repr(result) diff --git a/tests/test_ipv4_strict_cidr.py b/tests/test_ipv4_strict_cidr.py new file mode 100644 index 0000000..68bdf32 --- /dev/null +++ b/tests/test_ipv4_strict_cidr.py @@ -0,0 +1,18 @@ +"""HU-04: require a prefix length in the documented strict CIDR mode.""" + +# external +import pytest + +import validators + + +@pytest.mark.parametrize( + ("ip",), + [ + ("192.168.1.0/255.255.255.0", ), + ], +) +def test_ipv4_strict_cidr_rejects_dotted_netmask(ip: str): + """The network is valid, but its representation is not CIDR prefix notation.""" + result = validators.ipv4(ip, strict=True) + assert isinstance(result, validators.ValidationError), repr(result) diff --git a/tests/test_ipv6_brackets.py b/tests/test_ipv6_brackets.py new file mode 100644 index 0000000..908cdd4 --- /dev/null +++ b/tests/test_ipv6_brackets.py @@ -0,0 +1,21 @@ +"""HU-04: proposed enhancement; brackets are URL host syntax. + +This test expresses a new requirement, not a defect in the current contract. +""" + +# external +import pytest + +import validators + + +@pytest.mark.parametrize( + ("ip",), + [ + ("[2001:db8::1]", ), + ], +) +def test_ipv6_accepts_address_in_brackets(ip: str): + """Accept iv6 address in brackets.""" + result = validators.ipv6(ip) + assert result is True, repr(result)