Skip to content

Commit c97350b

Browse files
sethmlarsonbeledouxdenis
authored andcommitted
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>
1 parent cf039bc commit c97350b

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):
@@ -2883,6 +2889,13 @@ def _refold_parse_tree(parse_tree, *, policy):
28832889
[ValueTerminal(make_quoted_pairs(p), 'ptext')
28842890
for p in newparts] +
28852891
[ValueTerminal('"', 'ptext')])
2892+
if part.token_type == 'comment':
2893+
newparts = (
2894+
[ValueTerminal('(', 'ptext')] +
2895+
[ValueTerminal(make_parenthesis_pairs(p), 'ptext')
2896+
if p.token_type == 'ptext' else p
2897+
for p in newparts] +
2898+
[ValueTerminal(')', 'ptext')])
28862899
if not part.as_ew_allowed:
28872900
wrap_as_ew_blocked += 1
28882901
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
@@ -3050,6 +3050,29 @@ def test_address_list_with_specials_in_long_quoted_string(self):
30503050
with self.subTest(to=to):
30513051
self._test(parser.get_address_list(to)[0], folded, policy=policy)
30523052

3053+
def test_address_list_with_long_unwrapable_comment(self):
3054+
policy = self.policy.clone(max_line_length=40)
3055+
cases = [
3056+
# (to, folded)
3057+
('(loremipsumdolorsitametconsecteturadipi)<spy@example.org>',
3058+
'(loremipsumdolorsitametconsecteturadipi)<spy@example.org>\n'),
3059+
('<spy@example.org>(loremipsumdolorsitametconsecteturadipi)',
3060+
'<spy@example.org>(loremipsumdolorsitametconsecteturadipi)\n'),
3061+
('(loremipsum dolorsitametconsecteturadipi)<spy@example.org>',
3062+
'(loremipsum dolorsitametconsecteturadipi)<spy@example.org>\n'),
3063+
('<spy@example.org>(loremipsum dolorsitametconsecteturadipi)',
3064+
'<spy@example.org>(loremipsum\n dolorsitametconsecteturadipi)\n'),
3065+
('(Escaped \\( \\) chars \\\\ in comments stay escaped)<spy@example.org>',
3066+
'(Escaped \\( \\) chars \\\\ in comments stay\n escaped)<spy@example.org>\n'),
3067+
('((loremipsum)(loremipsum)(loremipsum)(loremipsum))<spy@example.org>',
3068+
'((loremipsum)(loremipsum)(loremipsum)(loremipsum))<spy@example.org>\n'),
3069+
('((loremipsum)(loremipsum)(loremipsum) (loremipsum))<spy@example.org>',
3070+
'((loremipsum)(loremipsum)(loremipsum)\n (loremipsum))<spy@example.org>\n'),
3071+
]
3072+
for (to, folded) in cases:
3073+
with self.subTest(to=to):
3074+
self._test(parser.get_address_list(to)[0], folded, policy=policy)
3075+
30533076
# XXX Need tests with comments on various sides of a unicode token,
30543077
# and with unicode tokens in the comments. Spaces inside the quotes
30553078
# currently don't do the right thing.
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)