Skip to content

fix(api): reject trailing newline in email() validator#39235

Open
lntutor wants to merge 1 commit into
langgenius:mainfrom
lntutor:fix/email-validator-trailing-newline
Open

fix(api): reject trailing newline in email() validator#39235
lntutor wants to merge 1 commit into
langgenius:mainfrom
lntutor:fix/email-validator-trailing-newline

Conversation

@lntutor

@lntutor lntutor commented Jul 19, 2026

Copy link
Copy Markdown

Closes #39234

Summary

libs/helper.py::email() validated with re.match against a $-anchored pattern. In Python $ matches at end-of-string or just before a trailing newline, so email("user@example.com\n") returned the value as valid instead of raising.

email() backs the EmailStr type used across the auth surface (login, registration, forgot-password, activate, workspace member invite). A CR/LF in an accepted address is a mail header-injection primitive and defeats exact-match uniqueness/normalization ("a@b.com\n" != "a@b.com").

Change

-    if re.match(pattern, email) is not None:
+    if re.fullmatch(pattern, email) is not None:

re.fullmatch requires the entire string to match, rejecting a trailing newline. Behavior changes only for the trailing-newline case — every currently-valid address still validates (verified: re.match and re.fullmatch differ only on strings with content after the $ match, i.e. a trailing \n).

Tests

Adds TestEmailValidator to tests/unit_tests/libs/test_helper.py (the validator had no unit tests): accepts valid / special-local-part addresses, rejects trailing newline, missing domain, and plain strings. All 22 test_helper tests pass; ruff check and ruff format --check clean.

libs/helper.email() validated with re.match against a pattern anchored
by `$`. In Python `$` matches at the end of the string OR just before a
trailing newline, so "user@example.com\n" passed validation. email()
backs the EmailStr type used across the auth surface (login, register,
forgot-password, activate, workspace member invite), where a CR/LF in an
accepted address is a mail header-injection primitive and defeats
exact-match uniqueness/normalization.

Switch to re.fullmatch, which requires the entire string to match and so
rejects a trailing newline. This changes behavior only for the
trailing-newline case; all currently-valid addresses still validate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N6RtoHuxrDqTUo9Mw9h4Cv
@dosubot dosubot Bot added the size:XS This PR changes 0-9 lines, ignoring generated files. label Jul 19, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Pyrefly Type Coverage

Metric Base PR Delta
Type coverage 54.01% 54.01% -0.00%
Strict coverage 53.50% 53.49% -0.00%
Typed symbols 34,407 34,407 0
Untyped symbols 29,577 29,582 +5
Modules 3029 3029 0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XS This PR changes 0-9 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

email() validator accepts a trailing newline (mail header-injection vector on the auth surface)

1 participant