#36746: IndexError in prep_address() when parsing invalid email addresses like
'to@'
-------------------------------+-----------------------------------------
     Reporter:  Mahdi Dehghan  |                    Owner:  Mahdi Dehghan
         Type:  Bug            |                   Status:  assigned
    Component:  Core (Mail)    |                  Version:  dev
     Severity:  Normal         |               Resolution:
     Keywords:                 |             Triage Stage:  Unreviewed
    Has patch:  1              |      Needs documentation:  0
  Needs tests:  0              |  Patch needs improvement:  0
Easy pickings:  0              |                    UI/UX:  0
-------------------------------+-----------------------------------------
Description changed by Mariusz Felisiak:

Old description:

> 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:
> 1. Run Django's test suite: `./runtests.py
> mail.tests.SMTPBackendTests.test_avoids_sending_to_invalid_addresses`
> 2. 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

New description:

 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:
 1. Run Django's test suite: `./runtests.py
 mail.tests.SMTPBackendTests.test_avoids_sending_to_invalid_addresses`
 2. 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
 }}}

--
-- 
Ticket URL: <https://code.djangoproject.com/ticket/36746#comment:4>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/django-updates/0107019aa145eb9b-352dc382-27cf-4899-93fa-7bec1cdbb729-000000%40eu-central-1.amazonses.com.

Reply via email to