summaryrefslogtreecommitdiffstats
path: root/Lib/email/_header_value_parser.py
diff options
context:
space:
mode:
authorMike Edmunds <medmunds@gmail.com>2025-01-19 00:50:52 (GMT)
committerGitHub <noreply@github.com>2025-01-19 00:50:52 (GMT)
commit5aaf41685834901e4ed0a40f4c055b92991a0bb5 (patch)
tree82b4dd8aa0c225c2db38853fb8d1592d6db9f2cd /Lib/email/_header_value_parser.py
parent61b35f74aa4a6ac606635e245147ff3658628d99 (diff)
downloadcpython-5aaf41685834901e4ed0a40f4c055b92991a0bb5.zip
cpython-5aaf41685834901e4ed0a40f4c055b92991a0bb5.tar.gz
cpython-5aaf41685834901e4ed0a40f4c055b92991a0bb5.tar.bz2
gh-80222: Fix email address header folding with long quoted-string (#122753)
Email generators using email.policy.default could incorrectly omit the quote ('"') characters from a quoted-string during header refolding, leading to invalid address headers and enabling header spoofing. This change restores the quote characters on a bare-quoted-string as the header is refolded, and escapes backslash and quote chars in the string.
Diffstat (limited to 'Lib/email/_header_value_parser.py')
-rw-r--r--Lib/email/_header_value_parser.py19
1 files changed, 18 insertions, 1 deletions
diff --git a/Lib/email/_header_value_parser.py b/Lib/email/_header_value_parser.py
index ec2215a..3d845c0 100644
--- a/Lib/email/_header_value_parser.py
+++ b/Lib/email/_header_value_parser.py
@@ -95,8 +95,16 @@ EXTENDED_ATTRIBUTE_ENDS = ATTRIBUTE_ENDS - set('%')
NLSET = {'\n', '\r'}
SPECIALSNL = SPECIALS | NLSET
+
+def make_quoted_pairs(value):
+ """Escape dquote and backslash for use within a quoted-string."""
+ return str(value).replace('\\', '\\\\').replace('"', '\\"')
+
+
def quote_string(value):
- return '"'+str(value).replace('\\', '\\\\').replace('"', r'\"')+'"'
+ escaped = make_quoted_pairs(value)
+ return f'"{escaped}"'
+
# Match a RFC 2047 word, looks like =?utf-8?q?someword?=
rfc2047_matcher = re.compile(r'''
@@ -2905,6 +2913,15 @@ def _refold_parse_tree(parse_tree, *, policy):
if not hasattr(part, 'encode'):
# It's not a terminal, try folding the subparts.
newparts = list(part)
+ if part.token_type == 'bare-quoted-string':
+ # To fold a quoted string we need to create a list of terminal
+ # tokens that will render the leading and trailing quotes
+ # and use quoted pairs in the value as appropriate.
+ newparts = (
+ [ValueTerminal('"', 'ptext')] +
+ [ValueTerminal(make_quoted_pairs(p), 'ptext')
+ for p in newparts] +
+ [ValueTerminal('"', 'ptext')])
if not part.as_ew_allowed:
wrap_as_ew_blocked += 1
newparts.append(end_ew_not_allowed)