Repository navigation
Fix quadratic time in split_email on long input - #189
Merged
Merged
Conversation
split_string_at_unquoted_special normalized text[i:] at every position to see whether a character combines with the ones after it, which copies and normalizes the rest of the string each time. limit the lookahead to a 32-character window so parsing is linear in the length of the input.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
split_email decides whether each character combines with the ones after it by calling unicodedata.normalize("NFC", text[i:]), which copies and normalizes the rest of the string at every position, so parsing is quadratic and runs before any length check. on main (Apple M5, CPython 3.13), validate_email("́" * 100000 + "@example.com"), about 200 KB, takes 60 s of CPU to reject and 200,000 "é" take 18 s, an easy algorithmic-complexity denial of service for a signup form that hands a request field to validate_email the way WTForms' Email validator does. normalizing text[i:i + 32] brings those to 64 ms and 27 ms with linear growth; 32 is the character, the 30 combining characters in a row allowed by the unicode stream-safe text format (UAX #15), and one more. capping the input length up front would reject long display names, which validate today because they are not counted toward the 254-character limit. in about 80,000 differential runs against main with the allow_* options on, nothing changed between valid and invalid, and the only differences were error messages for inputs with more than 30 combining characters in a row. test_email_long_input_is_not_quadratic fails on main after about 15 s and passes here in 0.05 s; flake8 and the suite pass on 3.10 to 3.15, mypy on 3.10 to 3.14.