]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-148192: Fix Generator._make_boundary behavior with CRLF line endings. (#148193)
authorHenry Jones <44321887+henryivesjones@users.noreply.github.com>
Tue, 14 Apr 2026 13:10:08 +0000 (01:10 +1200)
committerGitHub <noreply@github.com>
Tue, 14 Apr 2026 13:10:08 +0000 (09:10 -0400)
The Generator._make_boundary regex did not match on boundary phrases correctly when using CRLF line endings due to re.MULTILINE not considering \r\n as a line ending.

Lib/email/generator.py
Lib/test/test_email/test_generator.py
Misc/NEWS.d/next/Library/2026-04-07-14-13-40.gh-issue-148192.34AUYQ.rst [new file with mode: 0644]

index cebbc416087fee38af4606d6d0ebe07ab4682713..ba11d63fba600a3b2e0665a80845f188013485b8 100644 (file)
@@ -392,7 +392,7 @@ class Generator:
         b = boundary
         counter = 0
         while True:
-            cre = cls._compile_re('^--' + re.escape(b) + '(--)?$', re.MULTILINE)
+            cre = cls._compile_re('^--' + re.escape(b) + '(--)?\r?$', re.MULTILINE)
             if not cre.search(text):
                 break
             b = boundary + '.' + str(counter)
index c2d7d09d591e86138b7d7c1d8913853cae8a7c14..3c9a86f3e8cf291145c45da53fbf9f96cf4d4d6a 100644 (file)
@@ -1,13 +1,20 @@
 import io
 import textwrap
 import unittest
+import random
+import sys
 from email import message_from_string, message_from_bytes
 from email.message import EmailMessage
+from email.mime.multipart import MIMEMultipart
+from email.mime.text import MIMEText
 from email.generator import Generator, BytesGenerator
+import email.generator
 from email.headerregistry import Address
 from email import policy
 import email.errors
 from test.test_email import TestEmailBase, parameterize
+import test.support
+
 
 
 @parameterize
@@ -288,6 +295,36 @@ class TestGeneratorBase:
         g.flatten(msg)
         self.assertEqual(s.getvalue(), self.typ(expected))
 
+    def _test_boundary_detection(self, linesep):
+        # Generate a boundary token in the same way as _make_boundary
+        token = random.randrange(sys.maxsize)
+
+        def _patch_random_randrange(*args, **kwargs):
+            return token
+
+        with test.support.swap_attr(
+            random, "randrange", _patch_random_randrange
+        ):
+            boundary = self.genclass._make_boundary(text=None)
+            boundary_in_part = (
+                "this goes before the boundary\n--"
+                + boundary
+                + "\nthis goes after\n"
+            )
+            msg = MIMEMultipart()
+            msg.attach(MIMEText(boundary_in_part))
+            self.genclass(self.ioclass()).flatten(msg, linesep=linesep)
+            # Generator checks the message content for the string it is about
+            # to use as a boundary ('token' in this test) and when it finds it
+            # in our attachment appends .0 to make the boundary it uses unique.
+            self.assertEqual(msg.get_boundary(), boundary + ".0")
+
+    def test_lf_boundary_detection(self):
+        self._test_boundary_detection("\n")
+
+    def test_crlf_boundary_detection(self):
+        self._test_boundary_detection("\r\n")
+
 
 class TestGenerator(TestGeneratorBase, TestEmailBase):
 
diff --git a/Misc/NEWS.d/next/Library/2026-04-07-14-13-40.gh-issue-148192.34AUYQ.rst b/Misc/NEWS.d/next/Library/2026-04-07-14-13-40.gh-issue-148192.34AUYQ.rst
new file mode 100644 (file)
index 0000000..87a568b
--- /dev/null
@@ -0,0 +1,3 @@
+``email.generator.Generator._make_boundary`` could fail to detect a duplicate
+boundary string if linesep was not \n. It now correctly detects boundary
+strings when linesep is \r\n as well.