File CVE-2026-1299-email-encode-EOL-headers.patch of Package python.43089

From d7cf62cf9f630975a0e876708c4a23907a23aba3 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <encukou@gmail.com>
Date: Wed, 31 Jul 2024 00:19:48 +0200
Subject: [PATCH 1/4] [3.8] gh-121650: Encode newlines in headers, and verify
 headers are sound (GH-122233)

Per RFC 2047:

> [...] these encoding schemes allow the
> encoding of arbitrary octet values, mail readers that implement this
> decoding should also ensure that display of the decoded data on the
> recipient's terminal will not cause unwanted side-effects

It seems that the "quoted-word" scheme is a valid way to include
a newline character in a header value, just like we already allow
undecodable bytes or control characters.
They do need to be properly quoted when serialized to text, though.

This should fail for custom fold() implementations that aren't careful
about newlines.

(cherry picked from commit 097633981879b3c9de9a1dd120d3aa585ecc2384)

Co-authored-by: Petr Viktorin <encukou@gmail.com>
Co-authored-by: Bas Bloemsaat <bas@bloemsaat.org>
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
---
 Doc/library/email.errors.rst                                            |    6 ++++
 Lib/email/errors.py                                                     |    4 +++
 Lib/email/generator.py                                                  |    7 ++++-
 Lib/email/header.py                                                     |   13 ++++++++++
 Misc/NEWS.d/next/Library/2024-07-27-16-10-41.gh-issue-121650.nf6oc9.rst |    5 +++
 5 files changed, 34 insertions(+), 1 deletion(-)
 create mode 100644 Misc/NEWS.d/next/Library/2024-07-27-16-10-41.gh-issue-121650.nf6oc9.rst

Index: Python-2.7.18/Doc/library/email.errors.rst
===================================================================
--- Python-2.7.18.orig/Doc/library/email.errors.rst	2026-03-08 00:12:52.645863367 +0100
+++ Python-2.7.18/Doc/library/email.errors.rst	2026-03-08 00:13:24.072614878 +0100
@@ -61,6 +61,12 @@
    :class:`~email.mime.nonmultipart.MIMENonMultipart` (e.g.
    :class:`~email.mime.image.MIMEImage`).
 
+.. exception:: HeaderWriteError()
+
+   Raised when an error occurs when the :mod:`~email.generator` outputs
+   headers.
+
+
 Here's the list of the defects that the :class:`~email.parser.FeedParser`
 can find while parsing messages.  Note that the defects are added to the message
 where the problem was found, so for example, if a message nested inside a
Index: Python-2.7.18/Lib/email/errors.py
===================================================================
--- Python-2.7.18.orig/Lib/email/errors.py	2026-03-08 00:12:52.645863367 +0100
+++ Python-2.7.18/Lib/email/errors.py	2026-03-08 00:13:24.072943738 +0100
@@ -30,6 +30,10 @@
     """An illegal charset was given."""
 
 
+class HeaderWriteError(MessageError):
+    """Error while writing headers."""
+
+
 
 # These are parsing defects which the parser was able to work around.
 class MessageDefect:
Index: Python-2.7.18/Lib/email/generator.py
===================================================================
--- Python-2.7.18.orig/Lib/email/generator.py	2026-03-08 00:12:52.645863367 +0100
+++ Python-2.7.18/Lib/email/generator.py	2026-03-08 00:13:24.073521037 +0100
@@ -12,7 +12,8 @@
 import warnings
 
 from cStringIO import StringIO
-from email.header import Header
+from email.header import Header, removesuffix, NEWLINE_WITHOUT_FWSP
+from email.errors import HeaderWriteError
 
 UNDERSCORE = '_'
 NL = '\n'
@@ -139,6 +140,10 @@
 
     def _write_headers(self, msg):
         for h, v in msg.items():
+            if isinstance(v, (str, unicode)) and \
+                    NEWLINE_WITHOUT_FWSP.search(removesuffix(v, "\n")):
+                raise HeaderWriteError(
+                        'folded header contains newline: {!r}'.format(v))
             print >> self._fp, '%s:' % h,
             if self._maxheaderlen == 0:
                 # Explicit no-wrapping
Index: Python-2.7.18/Lib/email/header.py
===================================================================
--- Python-2.7.18.orig/Lib/email/header.py	2026-03-08 00:12:52.645863367 +0100
+++ Python-2.7.18/Lib/email/header.py	2026-03-08 00:13:24.073859275 +0100
@@ -8,6 +8,7 @@
     'Header',
     'decode_header',
     'make_header',
+    'removesuffix',
     ]
 
 import re
@@ -16,7 +17,7 @@
 import email.quoprimime
 import email.base64mime
 
-from email.errors import HeaderParseError
+from email.errors import HeaderParseError, HeaderWriteError
 from email.charset import Charset
 
 NL = '\n'
@@ -51,6 +52,8 @@
 # header injection attack.
 _embeded_header = re.compile(r'\n[^ \t]+:')
 
+# For fixing CVE-2026-1299 (bsc#1257181, gh#python/cpython#144125)
+NEWLINE_WITHOUT_FWSP = re.compile(r'\r\n[^ \t]|\r[^ \n\t]|\n[^ \t]')
 
 
 # Helpers
@@ -116,6 +119,13 @@
             del parts[0:3]
     return decoded
 
+
+def removesuffix(text, suffix):
+    if text.endswith(suffix):
+        return text[:-len(suffix)]
+    else:
+        return text
+
 
 
 def make_header(decoded_seq, maxlinelen=None, header_name=None,
@@ -180,6 +190,9 @@
         # BAW: I believe `chunks' and `maxlinelen' should be non-public.
         self._chunks = []
         if s is not None:
+            if NEWLINE_WITHOUT_FWSP.search(removesuffix(s, "\n")):
+                raise HeaderWriteError(
+                        'folded header contains newline: {!r}'.format(s))
             self.append(s, charset, errors)
         if maxlinelen is None:
             maxlinelen = MAXLINELEN
Index: Python-2.7.18/Misc/NEWS.d/next/Library/2024-07-27-16-10-41.gh-issue-121650.nf6oc9.rst
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ Python-2.7.18/Misc/NEWS.d/next/Library/2024-07-27-16-10-41.gh-issue-121650.nf6oc9.rst	2026-03-08 00:13:24.074092554 +0100
@@ -0,0 +1,5 @@
+:mod:`email` headers with embedded newlines are now quoted on output. The
+:mod:`~email.generator` will now refuse to serialize (write) headers that
+are unsafely folded or delimited; see
+:attr:`~email.policy.Policy.verify_generated_headers`. (Contributed by Bas
+Bloemsaat and Petr Viktorin in :gh:`121650`.)
Index: Python-2.7.18/Lib/email/test/test_email.py
===================================================================
--- Python-2.7.18.orig/Lib/email/test/test_email.py	2026-03-07 22:54:48.964374841 +0100
+++ Python-2.7.18/Lib/email/test/test_email.py	2026-03-08 01:20:55.270105365 +0100
@@ -564,13 +564,13 @@
     # (header injection attack).
     def test_embedded_header_via_Header_rejected(self):
         msg = Message()
-        msg['Dummy'] = Header('dummy\nX-Injected-Header: test')
-        self.assertRaises(Errors.HeaderParseError, msg.as_string)
+        self.assertRaises(Errors.HeaderWriteError,
+                          Header, 'dummy\nX-Injected-Header: test')
 
     def test_embedded_header_via_string_rejected(self):
         msg = Message()
         msg['Dummy'] = 'dummy\nX-Injected-Header: test'
-        self.assertRaises(Errors.HeaderParseError, msg.as_string)
+        self.assertRaises(Errors.HeaderWriteError, msg.as_string)
 
 
 # Test the email.Encoders module
openSUSE Build Service is sponsored by