Skip to content

Commit 2fef5b0

Browse files
miss-islingtonwojcikk2903
authored andcommitted
bpo-33529, email: Fix infinite loop in email header encoding (GH-12020) (GH-13321)
(cherry picked from commit c1f5667) Co-authored-by: Krzysztof Wojcik <wojcikk2903@users.noreply.github.com>
1 parent 8226832 commit 2fef5b0

File tree

4 files changed

+27
-14
lines changed

4 files changed

+27
-14
lines changed

Lib/email/_header_value_parser.py

+13-10
Original file line numberDiff line numberDiff line change
@@ -2724,16 +2724,19 @@ def _fold_as_ew(to_encode, lines, maxlen, last_ew, ew_combine_allowed, charset):
27242724
lines.append(' ')
27252725
# XXX We'll get an infinite loop here if maxlen is <= 7
27262726
continue
2727-
first_part = to_encode[:text_space]
2728-
ew = _ew.encode(first_part, charset=encode_as)
2729-
excess = len(ew) - remaining_space
2730-
if excess > 0:
2731-
# encode always chooses the shortest encoding, so this
2732-
# is guaranteed to fit at this point.
2733-
first_part = first_part[:-excess]
2734-
ew = _ew.encode(first_part)
2735-
lines[-1] += ew
2736-
to_encode = to_encode[len(first_part):]
2727+
2728+
to_encode_word = to_encode[:text_space]
2729+
encoded_word = _ew.encode(to_encode_word, charset=encode_as)
2730+
excess = len(encoded_word) - remaining_space
2731+
while excess > 0:
2732+
# Since the chunk to encode is guaranteed to fit into less than 100 characters,
2733+
# shrinking it by one at a time shouldn't take long.
2734+
to_encode_word = to_encode_word[:-1]
2735+
encoded_word = _ew.encode(to_encode_word, charset=encode_as)
2736+
excess = len(encoded_word) - remaining_space
2737+
lines[-1] += encoded_word
2738+
to_encode = to_encode[len(to_encode_word):]
2739+
27372740
if to_encode:
27382741
lines.append(' ')
27392742
new_last_ew = len(lines[-1])

Lib/test/test_email/test_headerregistry.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -1643,10 +1643,10 @@ def test_fold_overlong_words_using_RFC2047(self):
16431643
self.assertEqual(
16441644
h.fold(policy=policy.default),
16451645
'X-Report-Abuse: =?utf-8?q?=3Chttps=3A//www=2Emailitapp=2E'
1646-
'com/report=5F?=\n'
1647-
' =?utf-8?q?abuse=2Ephp=3Fmid=3Dxxx-xxx-xxxx'
1648-
'xxxxxxxxxxxxxxxxxxxx=3D=3D-xxx-?=\n'
1649-
' =?utf-8?q?xx-xx=3E?=\n')
1646+
'com/report=5Fabuse?=\n'
1647+
' =?utf-8?q?=2Ephp=3Fmid=3Dxxx-xxx-xxxx'
1648+
'xxxxxxxxxxxxxxxxxxxx=3D=3D-xxx-xx-xx?=\n'
1649+
' =?utf-8?q?=3E?=\n')
16501650

16511651

16521652
if __name__ == '__main__':

Lib/test/test_email/test_policy.py

+8
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,14 @@ def test_adding_default_policies_preserves_default_factory(self):
237237
email.policy.EmailPolicy.header_factory)
238238
self.assertEqual(newpolicy.__dict__, {'raise_on_defect': True})
239239

240+
def test_non_ascii_chars_do_not_cause_inf_loop(self):
241+
policy = email.policy.default.clone(max_line_length=20)
242+
actual = policy.fold('Subject', 'ą' * 12)
243+
self.assertEqual(
244+
actual,
245+
'Subject: \n' +
246+
12 * ' =?utf-8?q?=C4=85?=\n')
247+
240248
# XXX: Need subclassing tests.
241249
# For adding subclassed objects, make sure the usual rules apply (subclass
242250
# wins), but that the order still works (right overrides left).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Prevent fold function used in email header encoding from entering infinite
2+
loop when there are too many non-ASCII characters in a header.

0 commit comments

Comments
 (0)