Skip to content

Commit 555694d

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 555694d

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
@@ -2998,6 +2998,29 @@ def test_address_list_with_specials_in_encoded_word(self):
29982998
with self.subTest(to=to):
29992999
self._test(parser.get_address_list(to)[0], folded, policy=policy)
30003000

3001+
def test_address_list_with_long_unwrapable_comment(self):
3002+
policy = self.policy.clone(max_line_length=40)
3003+
cases = [
3004+
# (to, folded)
3005+
('(loremipsumdolorsitametconsecteturadipi)<spy@example.org>',
3006+
'(loremipsumdolorsitametconsecteturadipi)<spy@example.org>\n'),
3007+
('<spy@example.org>(loremipsumdolorsitametconsecteturadipi)',
3008+
'<spy@example.org>(loremipsumdolorsitametconsecteturadipi)\n'),
3009+
('(loremipsum dolorsitametconsecteturadipi)<spy@example.org>',
3010+
'(loremipsum dolorsitametconsecteturadipi)<spy@example.org>\n'),
3011+
('<spy@example.org>(loremipsum dolorsitametconsecteturadipi)',
3012+
'<spy@example.org>(loremipsum\n dolorsitametconsecteturadipi)\n'),
3013+
('(Escaped \\( \\) chars \\\\ in comments stay escaped)<spy@example.org>',
3014+
'(Escaped \\( \\) chars \\\\ in comments stay\n escaped)<spy@example.org>\n'),
3015+
('((loremipsum)(loremipsum)(loremipsum)(loremipsum))<spy@example.org>',
3016+
'((loremipsum)(loremipsum)(loremipsum)(loremipsum))<spy@example.org>\n'),
3017+
('((loremipsum)(loremipsum)(loremipsum) (loremipsum))<spy@example.org>',
3018+
'((loremipsum)(loremipsum)(loremipsum)\n (loremipsum))<spy@example.org>\n'),
3019+
]
3020+
for (to, folded) in cases:
3021+
with self.subTest(to=to):
3022+
self._test(parser.get_address_list(to)[0], folded, policy=policy)
3023+
30013024
# XXX Need tests with comments on various sides of a unicode token,
30023025
# and with unicode tokens in the comments. Spaces inside the quotes
30033026
# 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)