[3.9] gh-80222: Fix email address header folding with long quoted-string (GH-122753) (GH-129111) (GH-132371)

Email generators using email.policy.default could incorrectly omit the
quote ('"') characters from a quoted-string during header refolding,
leading to invalid address headers and enabling header spoofing. This
change restores the quote characters on a bare-quoted-string as the
header is refolded, and escapes backslash and quote chars in the string.

(cherry picked from commit 5aaf416858)
(cherry picked from commit a4ef689ce6)

Co-authored-by: R. David Murray <rdmurray@bitdance.com>
Co-authored-by: Mike Edmunds <medmunds@gmail.com>
Co-authored-by: Łukasz Langa <lukasz@langa.pl>
This commit is contained in:
Brian Schubert 2025-06-02 11:57:06 -04:00 committed by GitHub
parent 558e27abf1
commit d4df3c55e4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 51 additions and 1 deletions

View file

@ -2946,6 +2946,33 @@ def test_address_list_with_unicode_names_in_quotes(self):
'=?utf-8?q?H=C3=BCbsch?= Kaktus <beautiful@example.com>,\n'
' =?utf-8?q?bei=C3=9Ft_bei=C3=9Ft?= <biter@example.com>\n')
def test_address_list_with_specials_in_long_quoted_string(self):
# Regression for gh-80222.
policy = self.policy.clone(max_line_length=40)
cases = [
# (to, folded)
('"Exfiltrator <spy@example.org> (unclosed comment?" <to@example.com>',
'"Exfiltrator <spy@example.org> (unclosed\n'
' comment?" <to@example.com>\n'),
('"Escaped \\" chars \\\\ in quoted-string stay escaped" <to@example.com>',
'"Escaped \\" chars \\\\ in quoted-string\n'
' stay escaped" <to@example.com>\n'),
('This long display name does not need quotes <to@example.com>',
'This long display name does not need\n'
' quotes <to@example.com>\n'),
('"Quotes are not required but are retained here" <to@example.com>',
'"Quotes are not required but are\n'
' retained here" <to@example.com>\n'),
('"A quoted-string, it can be a valid local-part"@example.com',
'"A quoted-string, it can be a valid\n'
' local-part"@example.com\n'),
('"local-part-with-specials@but-no-fws.cannot-fold"@example.com',
'"local-part-with-specials@but-no-fws.cannot-fold"@example.com\n'),
]
for (to, folded) in cases:
with self.subTest(to=to):
self._test(parser.get_address_list(to)[0], folded, policy=policy)
def test_address_list_with_specials_in_encoded_word(self):
# An encoded-word parsed from a structured header must remain
# encoded when it contains specials. Regression for gh-121284.