Skip to content

Commit

Permalink
Fixed a regular expression denial of service issue by limiting whites… (
Browse files Browse the repository at this point in the history
#7360)

Co-authored-by: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com>
  • Loading branch information
prodigysml and adriangb committed Sep 20, 2023
1 parent 4be48b6 commit e4393ae
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 3 deletions.
16 changes: 14 additions & 2 deletions pydantic/networks.py
Expand Up @@ -416,16 +416,21 @@ def _validate(cls, __input_value: NetworkType, _: core_schema.ValidationInfo) ->
return cls(__input_value) # type: ignore[return-value]


def _build_pretty_email_regex() -> re.Pattern:
def _build_pretty_email_regex() -> re.Pattern[str]:
name_chars = r'[\w!#$%&\'*+\-/=?^_`{|}~]'
unquoted_name_group = fr'((?:{name_chars}+\s+)*{name_chars}+)'
quoted_name_group = r'"((?:[^"]|\")+)"'
email_group = r'<\s*(.+)\s*>'
email_group = r'<\s*(.{0,254})\s*>'
return re.compile(rf'\s*(?:{unquoted_name_group}|{quoted_name_group})?\s*{email_group}\s*')


pretty_email_regex = _build_pretty_email_regex()

MAX_EMAIL_LENGTH = 2048
"""Maximum length for an email.
A somewhat arbitrary but very generous number compared to what is allowed by most implementations.
"""


def validate_email(value: str) -> tuple[str, str]:
"""Email address validation using [email-validator](https://pypi.org/project/email-validator/).
Expand All @@ -440,6 +445,13 @@ def validate_email(value: str) -> tuple[str, str]:
if email_validator is None:
import_email_validator()

if len(value) > MAX_EMAIL_LENGTH:
raise PydanticCustomError(
'value_error',
'value is not a valid email address: {reason}',
{'reason': f'Length must not exceed {MAX_EMAIL_LENGTH} characters'},
)

m = pretty_email_regex.fullmatch(value)
name: str | None = None
if m:
Expand Down
3 changes: 2 additions & 1 deletion tests/test_networks.py
Expand Up @@ -812,9 +812,10 @@ def test_address_valid(value, name, email):
('foobar <<foobar<@example.com>', None),
('foobar <>', None),
('first.last <first.last@example.com>', None),
pytest.param('foobar <' + 'a' * 4096 + '@example.com>', 'Length must not exceed 2048 characters', id='long'),
],
)
def test_address_invalid(value, reason):
def test_address_invalid(value: str, reason: Union[str, None]):
with pytest.raises(PydanticCustomError, match=f'value is not a valid email address: {reason or ""}'):
validate_email(value)

Expand Down

0 comments on commit e4393ae

Please sign in to comment.