Fix infinite loop in email folding logic (GH-12732) · python/cpython@f69d5c6 (original) (raw)
4 files changed
lines changed
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -2846,15 +2846,22 @@ def _fold_as_ew(to_encode, lines, maxlen, last_ew, ew_combine_allowed, charset): | ||
2846 | 2846 | trailing_wsp = to_encode[-1] |
2847 | 2847 | to_encode = to_encode[:-1] |
2848 | 2848 | new_last_ew = len(lines[-1]) if last_ew is None else last_ew |
2849 | + | |
2850 | +encode_as = 'utf-8' if charset == 'us-ascii' else charset | |
2851 | + | |
2852 | +# The RFC2047 chrome takes up 7 characters plus the length | |
2853 | +# of the charset name. | |
2854 | +chrome_len = len(encode_as) + 7 | |
2855 | + | |
2856 | +if (chrome_len + 1) >= maxlen: | |
2857 | +raise errors.HeaderParseError( | |
2858 | +"max_line_length is too small to fit an encoded word") | |
2859 | + | |
2849 | 2860 | while to_encode: |
2850 | 2861 | remaining_space = maxlen - len(lines[-1]) |
2851 | -# The RFC2047 chrome takes up 7 characters plus the length | |
2852 | -# of the charset name. | |
2853 | -encode_as = 'utf-8' if charset == 'us-ascii' else charset | |
2854 | -text_space = remaining_space - len(encode_as) - 7 | |
2862 | +text_space = remaining_space - chrome_len | |
2855 | 2863 | if text_space <= 0: |
2856 | 2864 | lines.append(' ') |
2857 | -# XXX We'll get an infinite loop here if maxlen is <= 7 | |
2858 | 2865 | continue |
2859 | 2866 | |
2860 | 2867 | to_encode_word = to_encode[:text_space] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -13,7 +13,6 @@ | ||
13 | 13 | from email._policybase import compat32 |
14 | 14 | |
15 | 15 | |
16 | - | |
17 | 16 | class Parser: |
18 | 17 | def __init__(self, _class=None, *, policy=compat32): |
19 | 18 | """Parser of RFC 2822 and MIME email messages. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -2,6 +2,7 @@ | ||
2 | 2 | import types |
3 | 3 | import textwrap |
4 | 4 | import unittest |
5 | +import email.errors | |
5 | 6 | import email.policy |
6 | 7 | import email.parser |
7 | 8 | import email.generator |
@@ -257,6 +258,25 @@ def test_non_ascii_chars_do_not_cause_inf_loop(self): | ||
257 | 258 | 'Subject: \n' + |
258 | 259 | 12 * ' =?utf-8?q?=C4=85?=\n') |
259 | 260 | |
261 | +def test_short_maxlen_error(self): | |
262 | +# RFC 2047 chrome takes up 7 characters, plus the length of the charset | |
263 | +# name, so folding should fail if maxlen is lower than the minimum | |
264 | +# required length for a line. | |
265 | + | |
266 | +# Note: This is only triggered when there is a single word longer than | |
267 | +# max_line_length, hence the 1234567890 at the end of this whimsical | |
268 | +# subject. This is because when we encounter a word longer than | |
269 | +# max_line_length, it is broken down into encoded words to fit | |
270 | +# max_line_length. If the max_line_length isn't large enough to even | |
271 | +# contain the RFC 2047 chrome (`?=?q??=`), we fail. | |
272 | +subject = "Melt away the pounds with this one simple trick! 1234567890" | |
273 | + | |
274 | +for maxlen in [3, 7, 9]: | |
275 | +with self.subTest(maxlen=maxlen): | |
276 | +policy = email.policy.default.clone(max_line_length=maxlen) | |
277 | +with self.assertRaises(email.errors.HeaderParseError): | |
278 | +policy.fold("Subject", subject) | |
279 | + | |
260 | 280 | # XXX: Need subclassing tests. |
261 | 281 | # For adding subclassed objects, make sure the usual rules apply (subclass |
262 | 282 | # wins), but that the order still works (right overrides left). |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
1 | +Fix infinite loop in email header folding logic that would be triggered when | |
2 | +an email policy's max_line_length is not long enough to include the required | |
3 | +markup and any values in the message. Patch by Paul Ganssle |