Skip to content

Commit 6da28a1

Browse files
sethmlarsonedgarrmondragon
authored andcommitted
[3.10] gh-143935: Email preserve parens when folding comments (GH-143936)
Fix a bug in the folding of comments when flattening an email message using a modern email policy. Comments consisting of a very long sequence of non-foldable characters could trigger a forced line wrap that omitted the required leading space on the continuation line, causing the remainder of the comment to be interpreted as a new header field. This enabled header injection with carefully crafted inputs. (cherry picked from commit 17d1490) Co-authored-by: Seth Michael Larson seth@python.org Co-authored-by: Denis Ledoux dle@odoo.com - Issue: Fix folding of long comments of unfoldable characters in email headers #143935 Signed-off-by: Edgar Ramírez Mondragón <edgarrm358@gmail.com>
1 parent 6027dbb commit 6da28a1

File tree

3 files changed

+43
-1
lines changed

3 files changed

+43
-1
lines changed

Lib/email/_header_value_parser.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,12 @@ def make_quoted_pairs(value):
101101
return str(value).replace('\\', '\\\\').replace('"', '\\"')
102102

103103

104+
def make_parenthesis_pairs(value):
105+
"""Escape parenthesis and backslash for use within a comment."""
106+
return str(value).replace('\\', '\\\\') \
107+
.replace('(', '\\(').replace(')', '\\)')
108+
109+
104110
def quote_string(value):
105111
escaped = make_quoted_pairs(value)
106112
return f'"{escaped}"'
@@ -927,7 +933,7 @@ def value(self):
927933
return ' '
928934

929935
def startswith_fws(self):
930-
return True
936+
return self and self[0] in WSP
931937

932938

933939
class ValueTerminal(Terminal):
@@ -2865,6 +2871,13 @@ def _refold_parse_tree(parse_tree, *, policy):
28652871
[ValueTerminal(make_quoted_pairs(p), 'ptext')
28662872
for p in newparts] +
28672873
[ValueTerminal('"', 'ptext')])
2874+
if part.token_type == 'comment':
2875+
newparts = (
2876+
[ValueTerminal('(', 'ptext')] +
2877+
[ValueTerminal(make_parenthesis_pairs(p), 'ptext')
2878+
if p.token_type == 'ptext' else p
2879+
for p in newparts] +
2880+
[ValueTerminal(')', 'ptext')])
28682881
if not part.as_ew_allowed:
28692882
wrap_as_ew_blocked += 1
28702883
newparts.append(end_ew_not_allowed)

Lib/test/test_email/test__header_value_parser.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2973,6 +2973,29 @@ def test_address_list_with_specials_in_long_quoted_string(self):
29732973
with self.subTest(to=to):
29742974
self._test(parser.get_address_list(to)[0], folded, policy=policy)
29752975

2976+
def test_address_list_with_long_unwrapable_comment(self):
2977+
policy = self.policy.clone(max_line_length=40)
2978+
cases = [
2979+
# (to, folded)
2980+
('(loremipsumdolorsitametconsecteturadipi)<spy@example.org>',
2981+
'(loremipsumdolorsitametconsecteturadipi)<spy@example.org>\n'),
2982+
('<spy@example.org>(loremipsumdolorsitametconsecteturadipi)',
2983+
'<spy@example.org>(loremipsumdolorsitametconsecteturadipi)\n'),
2984+
('(loremipsum dolorsitametconsecteturadipi)<spy@example.org>',
2985+
'(loremipsum dolorsitametconsecteturadipi)<spy@example.org>\n'),
2986+
('<spy@example.org>(loremipsum dolorsitametconsecteturadipi)',
2987+
'<spy@example.org>(loremipsum\n dolorsitametconsecteturadipi)\n'),
2988+
('(Escaped \\( \\) chars \\\\ in comments stay escaped)<spy@example.org>',
2989+
'(Escaped \\( \\) chars \\\\ in comments stay\n escaped)<spy@example.org>\n'),
2990+
('((loremipsum)(loremipsum)(loremipsum)(loremipsum))<spy@example.org>',
2991+
'((loremipsum)(loremipsum)(loremipsum)(loremipsum))<spy@example.org>\n'),
2992+
('((loremipsum)(loremipsum)(loremipsum) (loremipsum))<spy@example.org>',
2993+
'((loremipsum)(loremipsum)(loremipsum)\n (loremipsum))<spy@example.org>\n'),
2994+
]
2995+
for (to, folded) in cases:
2996+
with self.subTest(to=to):
2997+
self._test(parser.get_address_list(to)[0], folded, policy=policy)
2998+
29762999
def test_address_list_with_specials_in_encoded_word(self):
29773000
# An encoded-word parsed from a structured header must remain
29783001
# encoded when it contains specials. Regression for gh-121284.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Fixed a bug in the folding of comments when flattening an email message
2+
using a modern email policy. Comments consisting of a very long sequence of
3+
non-foldable characters could trigger a forced line wrap that omitted the
4+
required leading space on the continuation line, causing the remainder of
5+
the comment to be interpreted as a new header field. This enabled header
6+
injection with carefully crafted inputs.

0 commit comments

Comments
 (0)