File CVE-2026-0672-http-hdr-inject-cookie-Morsel.patch of Package python3.42754

From 57c5ecd7e61fbb24e7de76eafd95332bd0ae4dea Mon Sep 17 00:00:00 2001
From: Seth Michael Larson <seth@python.org>
Date: Tue, 20 Jan 2026 15:23:42 -0600
Subject: [PATCH] [3.10] gh-143919: Reject control characters in http cookies
 (cherry picked from commit 95746b3a13a985787ef53b977129041971ed7f70)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Seth Michael Larson <seth@python.org>
Co-authored-by: Bartosz Sławecki <bartosz@ilikepython.com>
Co-authored-by: sobolevn <mail@sobolevn.me>
---
 Doc/library/http.cookies.rst                                             |    4 
 Lib/http/cookies.py                                                      |   41 ++++++++
 Lib/test/test_http_cookies.py                                            |   46 +++++++++-
 Misc/NEWS.d/next/Security/2026-01-16-11-13-15.gh-issue-143919.kchwZV.rst |    1 
 4 files changed, 85 insertions(+), 7 deletions(-)
 create mode 100644 Misc/NEWS.d/next/Security/2026-01-16-11-13-15.gh-issue-143919.kchwZV.rst

Index: Python-3.4.10/Doc/library/http.cookies.rst
===================================================================
--- Python-3.4.10.orig/Doc/library/http.cookies.rst	2019-03-18 17:51:26.000000000 +0100
+++ Python-3.4.10/Doc/library/http.cookies.rst	2026-02-13 00:20:32.065184759 +0100
@@ -226,9 +226,9 @@
    Set-Cookie: chips=ahoy
    Set-Cookie: vienna=finger
    >>> C = cookies.SimpleCookie()
-   >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";')
+   >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=;";')
    >>> print(C)
-   Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;"
+   Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=;"
    >>> C = cookies.SimpleCookie()
    >>> C["oreo"] = "doublestuff"
    >>> C["oreo"]["path"] = "/"
Index: Python-3.4.10/Lib/http/cookies.py
===================================================================
--- Python-3.4.10.orig/Lib/http/cookies.py	2026-02-13 00:14:17.197676635 +0100
+++ Python-3.4.10/Lib/http/cookies.py	2026-02-13 00:20:32.065595022 +0100
@@ -87,9 +87,9 @@
 such trickeries do not confuse it.
 
    >>> C = cookies.SimpleCookie()
-   >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";')
+   >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=;";')
    >>> print(C)
-   Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;"
+   Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=;"
 
 Each element of the Cookie also supports all of the RFC 2109
 Cookie attributes.  Here's an example which sets the Path
@@ -222,6 +222,17 @@
     '\375' : '\\375',  '\376' : '\\376',  '\377' : '\\377'
     }
 
+_is_legal_key = re.compile('[%s]+' % re.escape(_LegalChars)).fullmatch
+_control_character_re = re.compile(r'[\x00-\x1F\x7F]')
+
+
+def _has_control_character(*val):
+    """Detects control characters within a value.
+    Supports any type, as header values can be any type.
+    """
+    return any(_control_character_re.search(str(v)) for v in val)
+
+
 def _quote(str, LegalChars=_LegalChars):
     r"""Quote a string for use in a cookie header.
 
@@ -235,7 +246,8 @@
         return '"' + _nulljoin(_Translator.get(s, s) for s in str) + '"'
 
 
-_unquote_sub = re.compile(r'\\(?:([0-3][0-7][0-7])|(.))').sub
+_unquote_sub = re.compile(r"\\(?:([0-3][0-7][0-7])|(.))").sub
+
 
 def _unquote_replace(m):
     if m.group(1):
@@ -243,6 +255,7 @@
     else:
         return m.group(2)
 
+
 def _unquote(str):
     # If there aren't any doublequotes,
     # then there can't be any special characters.  See RFC 2109.
@@ -263,6 +276,7 @@
     #
     return _unquote_sub(_unquote_replace, str)
 
+
 # The _getdate() routine is used to set the expiration time in the cookie's HTTP
 # header.  By default, _getdate() returns the current time in the appropriate
 # "expires" format for a Set-Cookie header.  The one optional argument is an
@@ -331,6 +345,10 @@
         K = K.lower()
         if not K in self._reserved:
             raise CookieError("Invalid Attribute %s" % K)
+        if _has_control_character(K, V):
+            raise CookieError(
+                "Control characters are not allowed in cookies {!r} {!r}".format(K, V)
+            )
         dict.__setitem__(self, K, V)
 
     def isReservedKey(self, K):
@@ -341,6 +359,18 @@
         # Second we make sure it only contains legal characters
         if key.lower() in self._reserved:
             raise CookieError("Attempt to set a reserved key: %s" % key)
+        # The cookie value can contain control characters as long as its
+        # network representation (coded_val) does not. The output path checks
+        # for control characters again.
+        if _has_control_character(key, coded_val):
+            raise CookieError(
+                "Control characters are not allowed in cookies %r %r %r"
+                % (
+                    key,
+                    val,
+                    coded_val,
+                )
+            )
         if any(c not in LegalChars for c in key):
             raise CookieError("Illegal key value: %s" % key)
 
@@ -481,7 +511,10 @@
         result = []
         items = sorted(self.items())
         for key, value in items:
-            result.append(value.output(attrs, header))
+            value_output = value.output(attrs, header)
+            if _has_control_character(value_output):
+                raise CookieError("Control characters are not allowed in cookies")
+            result.append(value_output)
         return sep.join(result)
 
     __str__ = output
Index: Python-3.4.10/Lib/test/test_http_cookies.py
===================================================================
--- Python-3.4.10.orig/Lib/test/test_http_cookies.py	2026-02-13 00:14:17.198357842 +0100
+++ Python-3.4.10/Lib/test/test_http_cookies.py	2026-02-13 00:20:32.065843379 +0100
@@ -1,6 +1,12 @@
 # Simple test suite for http/cookies.py
 
-from test.support import run_unittest, run_doctest, check_warnings, requires_resource
+from test.support import (
+    run_unittest,
+    run_doctest,
+    check_warnings,
+    requires_resource,
+    control_characters_c0,
+)
 import unittest
 from http import cookies
 import pickle
@@ -297,6 +303,44 @@
             self.assertRaises(cookies.CookieError,
                               M.set, i, '%s_value' % i, '%s_value' % i)
 
+    def test_control_characters(self):
+        for c0 in control_characters_c0():
+            morsel = cookies.Morsel()
+
+            # .__setitem__()
+            with self.assertRaises(cookies.CookieError):
+                morsel[c0] = "val"
+            with self.assertRaises(cookies.CookieError):
+                morsel["path"] = c0
+
+            # .set()
+            with self.assertRaises(cookies.CookieError):
+                morsel.set(c0, "val", "coded-value")
+            with self.assertRaises(cookies.CookieError):
+                morsel.set("path", c0, "coded-value")
+            with self.assertRaises(cookies.CookieError):
+                morsel.set("path", "val", c0)
+
+    def test_control_characters_output(self):
+        # Tests that even if the internals of Morsel are modified
+        # that a call to .output() has control character safeguards.
+        for c0 in control_characters_c0():
+            morsel = cookies.Morsel()
+            morsel.set("key", "value", "coded-value")
+            morsel.key = c0  # Override internal variable.
+            cookie = cookies.SimpleCookie()
+            cookie["cookie"] = morsel
+            with self.assertRaises(cookies.CookieError):
+                cookie.output()
+
+            morsel = cookies.Morsel()
+            morsel.set("key", "value", "coded-value")
+            morsel.coded_value = c0  # Override internal variable.
+            cookie = cookies.SimpleCookie()
+            cookie["cookie"] = morsel
+            with self.assertRaises(cookies.CookieError):
+                cookie.output()
+
 
 def test_main():
     run_unittest(CookieTests, MorselTests)
Index: Python-3.4.10/Misc/NEWS.d/next/Security/2026-01-16-11-13-15.gh-issue-143919.kchwZV.rst
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ Python-3.4.10/Misc/NEWS.d/next/Security/2026-01-16-11-13-15.gh-issue-143919.kchwZV.rst	2026-02-13 00:20:32.066497693 +0100
@@ -0,0 +1 @@
+Reject control characters in :class:`http.cookies.Morsel` fields and values.
openSUSE Build Service is sponsored by