Skip to content

Commit 1132e45

Browse files
beledouxdenisencukoubasbloemsaat
authored andcommitted
gh-144125: email: verify headers are sound in BytesGenerator
GH-122233 added an implementation to `Generator` to refuse to serialize (write) headers that are unsafely folded or delimited. This revision adds the same implementation to `BytesGenerator`, so it gets the same safety protections for unsafely folded or delimited headers Co-authored-by: Denis Ledoux <5822488+beledouxdenis@users.noreply.github.com> Co-authored-by: Petr Viktorin <302922+encukou@users.noreply.github.com> Co-authored-by: Bas Bloemsaat <1586868+basbloemsaat@users.noreply.github.com>
1 parent cf71e34 commit 1132e45

File tree

4 files changed

+21
-1
lines changed

4 files changed

+21
-1
lines changed

Lib/email/generator.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
NLCRE = re.compile(r'\r\n|\r|\n')
2323
fcre = re.compile(r'^From ', re.MULTILINE)
2424
NEWLINE_WITHOUT_FWSP = re.compile(r'\r\n[^ \t]|\r[^ \n\t]|\n[^ \t]')
25+
NEWLINE_WITHOUT_FWSP_BYTES = re.compile(br'\r\n[^ \t]|\r[^ \n\t]|\n[^ \t]')
2526

2627

2728
class Generator:
@@ -429,7 +430,16 @@ def _write_headers(self, msg):
429430
# This is almost the same as the string version, except for handling
430431
# strings with 8bit bytes.
431432
for h, v in msg.raw_items():
432-
self._fp.write(self.policy.fold_binary(h, v))
433+
folded = self.policy.fold_binary(h, v)
434+
if self.policy.verify_generated_headers:
435+
linesep = self.policy.linesep.encode()
436+
if not folded.endswith(linesep):
437+
raise HeaderWriteError(
438+
f'folded header does not end with {linesep!r}: {folded!r}')
439+
if NEWLINE_WITHOUT_FWSP_BYTES.search(folded.removesuffix(linesep)):
440+
raise HeaderWriteError(
441+
f'folded header contains newline: {folded!r}')
442+
self._fp.write(folded)
433443
# A blank line always separates headers from body
434444
self.write(self._NL)
435445

Lib/test/test_email/test_generator.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,8 @@ def fold(self, **kwargs):
334334

335335
with self.assertRaises(email.errors.HeaderWriteError):
336336
message.as_string()
337+
with self.assertRaises(email.errors.HeaderWriteError):
338+
message.as_bytes()
337339

338340

339341
class TestBytesGenerator(TestGeneratorBase, TestEmailBase):

Lib/test/test_email/test_policy.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,10 @@ def fold(self, **kwargs):
319319
message.as_string(),
320320
f"{text}\nBody",
321321
)
322+
self.assertEqual(
323+
message.as_bytes(),
324+
f"{text}\nBody".encode(),
325+
)
322326

323327
# XXX: Need subclassing tests.
324328
# For adding subclassed objects, make sure the usual rules apply (subclass
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
:mod:`~email.BytesGenerator` will now refuse to serialize (write) headers
2+
that are unsafely folded or delimited; see
3+
:attr:`~email.policy.Policy.verify_generated_headers`. (Contributed by Bas
4+
Bloemsaat and Petr Viktorin in :gh:`121650`).

0 commit comments

Comments
 (0)