#36746 closed Bug (invalid)
IndexError in prep_address() when parsing invalid email addresses like 'to@'
| Reported by: | Mahdi Dehghan | Owned by: | Mahdi Dehghan |
|---|---|---|---|
| Component: | Core (Mail) | Version: | dev |
| Severity: | Normal | Keywords: | |
| Cc: | Mahdi Dehghan | Triage Stage: | Unreviewed |
| Has patch: | no | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description (last modified by )
When parsing invalid email addresses (such as 'to@') in the SMTP backend's prep_address() method, Python's email parser raises an IndexError instead of the expected ValueError. This causes the test test_avoids_sending_to_invalid_addresses to fail.
Steps to reproduce:
- Run Django's test suite:
./runtests.py mail.tests.SMTPBackendTests.test_avoids_sending_to_invalid_addresses - The test fails with IndexError when trying to parse 'to@'
Root cause:
The prep_address() method at line 172 in django/core/mail/backends/smtp.py directly calls AddressHeader.value_parser(address) without exception handling. When parsing malformed addresses like 'to@', Python's email parser first raises HeaderParseError, then during exception handling attempts to parse the address differently, which leads to an IndexError when trying to access value[0] on an empty string.
Proposed solution:
Wrap the AddressHeader.value_parser() call in a try-except block to catch HeaderParseError, IndexError, and ValueError exceptions, converting them to ValueError with an appropriate message. This matches the pattern already used in the deprecated sanitize_address() function in django/core/mail/message.py (line 115).
Expected behavior:
The prep_address() method should catch parsing exceptions and raise ValueError with message "Invalid address" for invalid addresses, matching the test's expectation.
Actual behavior:
IndexError: string index out of range is raised from Python's email._header_value_parser when parsing addresses like 'to@'.
Error traceback:
ERROR: test_avoids_sending_to_invalid_addresses (mail.tests.SMTPBackendTests.test_avoids_sending_to_invalid_addresses) [<object object at 0x12d0aff70>] (email_address='to@')
Verify invalid addresses can't sneak into SMTP commands through
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/email/_header_value_parser.py", line 1965, in get_address
token, value = get_group(value)
^^^^^^^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/email/_header_value_parser.py", line 1923, in get_group
raise errors.HeaderParseError("expected ':' at end of group "
^^^^^^^^^^^^^^^^^
email.errors.HeaderParseError: expected ':' at end of group display name but found '@'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/email/_header_value_parser.py", line 1791, in get_mailbox
token, value = get_name_addr(value)
^^^^^^^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/email/_header_value_parser.py", line 1777, in get_name_addr
token, value = get_angle_addr(value)
^^^^^^^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/email/_header_value_parser.py", line 1702, in get_angle_addr
raise errors.HeaderParseError(
^^^^^^^^^^^^^^^^^
email.errors.HeaderParseError: expected angle-addr but found '@'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/unittest/case.py", line 58, in testPartExecutor
yield
File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/unittest/case.py", line 539, in subTest
yield
File "tests/mail/tests.py", line 3070, in test_avoids_sending_to_invalid_addresses
backend.send_messages([email])
^^^^^^^^^^^^^^^^^
File "django/core/mail/backends/smtp.py", line 138, in send_messages
sent = self._send(message)
^^^^^^^^^^^^^^^^^
File "django/core/mail/backends/smtp.py", line 151, in _send
recipients = [self.prep_address(addr) for addr in email_message.recipients()]
^^^^^^^^^^^^^^^^^
File "django/core/mail/backends/smtp.py", line 172, in prep_address
parsed = AddressHeader.value_parser(address)
^^^^^^^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/email/headerregistry.py", line 333, in value_parser
address_list, value = parser.get_address_list(value)
^^^^^^^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/email/_header_value_parser.py", line 1988, in get_address_list
token, value = get_address(value)
^^^^^^^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/email/_header_value_parser.py", line 1968, in get_address
token, value = get_mailbox(value)
^^^^^^^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/email/_header_value_parser.py", line 1794, in get_mailbox
token, value = get_addr_spec(value)
^^^^^^^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/email/_header_value_parser.py", line 1647, in get_addr_spec
token, value = get_domain(value[1:])
^^^^^^^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/email/_header_value_parser.py", line 1604, in get_domain
if value[0] in CFWS_LEADER:
^^^^^^^^^^^^^^^^^
IndexError: string index out of range
Change History (9)
comment:2 by , 3 weeks ago
| Owner: | set to |
|---|---|
| Status: | new → assigned |
comment:4 by , 3 weeks ago
| Description: | modified (diff) |
|---|
comment:5 by , 3 weeks ago
| Resolution: | → needsinfo |
|---|---|
| Status: | assigned → closed |
Thanks -- what version of Python 3.12 was this? We only officially support the latest point release, which at the time I write is 3.12.12.
comment:6 by , 2 weeks ago
| Description: | modified (diff) |
|---|
comment:7 by , 2 weeks ago
[This was CPython issue https://github.com/python/cpython/issues/85116. It was marked completed in April 2024, but without reference to a specific patch, so I can't tell which Python releases first included the fix. Current supported Python releases parse 'to@' without raising an error, instead identifying it as an InvalidHeaderDefect('invalid address in address-list'). Django 6.0's SMTPBackend.prep_address() converts that defect to a ValueError as expected.]
comment:8 by , 2 weeks ago
| Has patch: | unset |
|---|---|
| Resolution: | needsinfo → invalid |
Thanks Mike. Looks like it was 3.12.4, see the tags at the top of https://github.com/python/cpython/commit/2eaf9ba9ceb4a9d1595b1413bf872db0a4978b13. I don't think it's worth adjusting the test.