Skip to content

Commit 3900a2c

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 <[email protected]> Co-authored-by: Denis Ledoux <[email protected]>
1 parent 4802b96 commit 3900a2c

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}"'
@@ -933,7 +939,7 @@ def value(self):
933939
return ' '
934940

935941
def startswith_fws(self):
936-
return True
942+
return self and self[0] in WSP
937943

938944

939945
class ValueTerminal(Terminal):
@@ -2922,6 +2928,13 @@ def _refold_parse_tree(parse_tree, *, policy):
29222928
[ValueTerminal(make_quoted_pairs(p), 'ptext')
29232929
for p in newparts] +
29242930
[ValueTerminal('"', 'ptext')])
2931+
if part.token_type == 'comment':
2932+
newparts = (
2933+
[ValueTerminal('(', 'ptext')] +
2934+
[ValueTerminal(make_parenthesis_pairs(p), 'ptext')
2935+
if p.token_type == 'ptext' else p
2936+
for p in newparts] +
2937+
[ValueTerminal(')', 'ptext')])
29252938
if not part.as_ew_allowed:
29262939
wrap_as_ew_blocked += 1
29272940
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
@@ -3141,6 +3141,29 @@ def test_address_list_with_specials_in_long_quoted_string(self):
31413141
with self.subTest(to=to):
31423142
self._test(parser.get_address_list(to)[0], folded, policy=policy)
31433143

3144+
def test_address_list_with_long_unwrapable_comment(self):
3145+
policy = self.policy.clone(max_line_length=40)
3146+
cases = [
3147+
# (to, folded)
3148+
('(loremipsumdolorsitametconsecteturadipi)<[email protected]>',
3149+
'(loremipsumdolorsitametconsecteturadipi)<[email protected]>\n'),
3150+
('<[email protected]>(loremipsumdolorsitametconsecteturadipi)',
3151+
'<[email protected]>(loremipsumdolorsitametconsecteturadipi)\n'),
3152+
('(loremipsum dolorsitametconsecteturadipi)<[email protected]>',
3153+
'(loremipsum dolorsitametconsecteturadipi)<[email protected]>\n'),
3154+
('<[email protected]>(loremipsum dolorsitametconsecteturadipi)',
3155+
'<[email protected]>(loremipsum\n dolorsitametconsecteturadipi)\n'),
3156+
('(Escaped \\( \\) chars \\\\ in comments stay escaped)<[email protected]>',
3157+
'(Escaped \\( \\) chars \\\\ in comments stay\n escaped)<[email protected]>\n'),
3158+
('((loremipsum)(loremipsum)(loremipsum)(loremipsum))<[email protected]>',
3159+
'((loremipsum)(loremipsum)(loremipsum)(loremipsum))<[email protected]>\n'),
3160+
('((loremipsum)(loremipsum)(loremipsum) (loremipsum))<[email protected]>',
3161+
'((loremipsum)(loremipsum)(loremipsum)\n (loremipsum))<[email protected]>\n'),
3162+
]
3163+
for (to, folded) in cases:
3164+
with self.subTest(to=to):
3165+
self._test(parser.get_address_list(to)[0], folded, policy=policy)
3166+
31443167
# XXX Need tests with comments on various sides of a unicode token,
31453168
# and with unicode tokens in the comments. Spaces inside the quotes
31463169
# 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)