fix: use re.fullmatch in email validator to reject trailing newlines (#39234) (#39320)

Co-authored-by: Asuka Minato <i@asukaminato.eu.org>
This commit is contained in:
shakti 2026-07-23 09:49:44 +05:30 committed by GitHub
parent 2b8e40af32
commit 1835e8ef31
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 33 additions and 3 deletions

View File

@ -221,8 +221,11 @@ def current_timestamp() -> int:
def email(email):
# Define a regex pattern for email addresses
pattern = r"^[\w\.!#$%&'*+\-/=?^_`{|}~]+@([\w-]+\.)+[\w-]{2,}$"
# Check if the email matches the pattern
if re.match(pattern, email) is not None:
# Use re.fullmatch instead of re.match to reject trailing newlines.
# In Python, '$' matches at end-of-string OR just before a trailing newline,
# so re.match accepts "user@example.com\n". re.fullmatch requires the entire
# string to match, closing the mail header-injection vector. (#39234)
if re.fullmatch(pattern, email) is not None:
return email
error = f"{email} is not a valid email."

View File

@ -2,7 +2,7 @@ from datetime import datetime
import pytest
from libs.helper import OptionalTimestampField, escape_like_pattern, extract_tenant_id
from libs.helper import OptionalTimestampField, email, escape_like_pattern, extract_tenant_id
from models.account import Account
from models.model import EndUser
@ -126,3 +126,30 @@ class TestEscapeLikePattern:
result = escape_like_pattern("test\\%_value")
# Should be: test\\\%\_value
assert result == "test\\\\\\%\\_value"
class TestEmailValidator:
"""Tests for the email() validator — regression for #39234."""
def test_valid_email_accepted(self):
assert email("user@example.com") == "user@example.com"
def test_trailing_newline_rejected(self):
with pytest.raises(ValueError, match="not a valid email"):
email("user@example.com\n")
def test_trailing_carriage_return_newline_rejected(self):
with pytest.raises(ValueError, match="not a valid email"):
email("user@example.com\r\n")
def test_multiple_newlines_rejected(self):
with pytest.raises(ValueError, match="not a valid email"):
email("user@example.com\n\n")
def test_empty_string_rejected(self):
with pytest.raises(ValueError, match="not a valid email"):
email("")
def test_invalid_email_rejected(self):
with pytest.raises(ValueError, match="not a valid email"):
email("not-an-email")