Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion email_validator/syntax.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,14 @@ def split_string_at_unquoted_special(text: str, specials: tuple[str, ...]) -> tu
# the < as the start of "<email>" syntax in that case. Likewise,
# if anything combines with an @ or ", we should probably not
# treat it as a special character.
if unicodedata.normalize("NFC", text[i:])[0] != c:
#
# Only the characters right after c can combine with it, so
# look at a short window rather than normalizing the rest of
# the string at every position, which is quadratic in the
# length of the input. The window is c, the 30 combining
# characters in a row allowed by the Unicode Stream-Safe Text
# Format (UAX #15), and one more character.
if unicodedata.normalize("NFC", text[i:i + 32])[0] != c:
left_part += c

elif inside_quote:
Expand Down
11 changes: 11 additions & 0 deletions tests/test_syntax.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import time
from typing import Any

import pytest
Expand Down Expand Up @@ -519,6 +520,7 @@ def test_domain_literal() -> None:
('<me@example.com', 'An open angle bracket at the start of the email address has to be followed by a close angle bracket at the end.'),
('<me@example.com> !', 'There can\'t be anything after the email address.'),
('<\u0338me@example.com', 'The email address contains invalid characters before the @-sign: \'<\'.'),
('<\u0301\u0338me@example.com', 'The email address contains invalid characters before the @-sign: \'<\'.'),
('DisplayName <me@-example.com>', 'An email address cannot have a hyphen immediately after the @-sign.'),
('DisplayName <me@example.com>', 'A display name and angle brackets around the email address are not permitted here.'),
('Display Name <me@example.com>', 'A display name and angle brackets around the email address are not permitted here.'),
Expand All @@ -535,6 +537,15 @@ def test_email_invalid_syntax(email_input: str, error_msg: str) -> None:
assert str(exc_info.value) == error_msg


def test_email_long_input_is_not_quadratic() -> None:
# split_email used to NFC-normalize the rest of the string at every
# character, so rejecting this took about 15 seconds.
start = time.perf_counter()
with pytest.raises(EmailSyntaxError, match="unsafe characters"):
validate_email("\u0301" * 50000 + "@example.com", check_deliverability=False)
assert time.perf_counter() - start < 2


@pytest.mark.parametrize(
'email_input,error_msg',
[
Expand Down
Loading