Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Lib/email/header.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@

# Find a header embedded in a putative header value. Used to check for
# header injection attack.
_embedded_header = re.compile(r'\n[^ \t]+:')
# A header name may not contain whitespace, but receivers are lenient about
# whitespace before the colon, so an injected line using it still reaches
# them as a header. Match it here too (gh-76787).
_embedded_header = re.compile(r'\n[^ \t]+[ \t]*:')


# Helpers
Expand Down
21 changes: 21 additions & 0 deletions Lib/test/test_email/test_email.py
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,27 @@ def test_embedded_header_via_string_rejected(self):
msg['Dummy'] = 'dummy\nX-Injected-Header: test'
self.assertRaises(errors.HeaderParseError, msg.as_string)

# gh-76787: receivers accept whitespace between a header name and the
# colon, so a line using it is an injected header too.
def test_embedded_header_with_space_before_colon_rejected(self):
for injected in ('dummy\nX-Injected-Header : test',
'dummy\nX-Injected-Header\t: test',
'dummy\nX-Injected-Header \t : test'):
with self.subTest(injected=injected):
msg = Message()
msg['Dummy'] = Header(injected)
self.assertRaises(errors.HeaderParseError, msg.as_string)

msg = Message()
msg['Dummy'] = injected
self.assertRaises(errors.HeaderParseError, msg.as_string)

def test_folded_continuation_line_still_accepted(self):
# A continuation line starts with whitespace and is not an injection.
msg = Message()
msg['Dummy'] = Header('dummy\n continued here: not a header')
self.assertIn('continued here', msg.as_string())

def test_unicode_header_defaults_to_utf8_encoding(self):
# Issue 14291
m = MIMEText('abc\n')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
:class:`email.header.Header` now rejects an embedded header whose name is
separated from the colon by spaces or tabs. Receivers accept that form, so a
value such as ``"addr@example.com\ncc : injected@example.com"`` reached them
as two headers while the check that was meant to catch it only looked for a
colon immediately after the name.
Loading