diff --git a/src/validators/email.py b/src/validators/email.py index cba4453..46cc594 100644 --- a/src/validators/email.py +++ b/src/validators/email.py @@ -83,7 +83,7 @@ def email( rfc_2782=rfc_2782, ) ) - if re.match( + if re.fullmatch( # 2026-10-11: Validate the complete local part. # extended latin r"(^[\u0100-\u017F\u0180-\u024F\u00A0-\u00FF]" # dot-atom diff --git a/tests/test_email.py b/tests/test_email.py index 56c95f3..990ef8e 100644 --- a/tests/test_email.py +++ b/tests/test_email.py @@ -56,3 +56,13 @@ def test_returns_true_on_valid_email(value: str): def test_returns_failed_validation_on_invalid_email(value: str): """Test returns failed validation on invalid email.""" assert isinstance(email(value), ValidationError) + + +@pytest.mark.parametrize( + "local_part", ["person", "person.name", "\u0141\u00f3ka\u015b\u0107", '"person"'] +) +def test_rejects_newline_after_local_part(local_part: str): + """Test the whole local part, including its final character.""" + # 2026-10-11: A newline before the at-sign is not part of a valid local part. + assert email(f"{local_part}@example.com") is True + assert isinstance(email(f"{local_part}\n@example.com"), ValidationError)