File CVE-2026-0672-http-hdr-inject-cookie-Morsel.patch of Package python.42782

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/cookie.rst                                                   |    4 
 Lib/Cookie.py                                                            |   41 ++++++++-
 Lib/test/support/__init__.py                                             |    9 +-
 Lib/test/test_cookie.py                                                  |   44 +++++++++-
 Misc/NEWS.d/next/Security/2026-01-16-11-13-15.gh-issue-143919.kchwZV.rst |    1 
 5 files changed, 91 insertions(+), 8 deletions(-)
 create mode 100644 Misc/NEWS.d/next/Security/2026-01-16-11-13-15.gh-issue-143919.kchwZV.rst

Index: Python-2.7.18/Doc/library/cookie.rst
===================================================================
--- Python-2.7.18.orig/Doc/library/cookie.rst	2020-04-19 23:13:39.000000000 +0200
+++ Python-2.7.18/Doc/library/cookie.rst	2026-02-13 23:53:40.669828966 +0100
@@ -265,9 +265,9 @@
    Set-Cookie: chips=ahoy
    Set-Cookie: vienna=finger
    >>> C = Cookie.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 = Cookie.SimpleCookie()
    >>> C["oreo"] = "doublestuff"
    >>> C["oreo"]["path"] = "/"
Index: Python-2.7.18/Lib/Cookie.py
===================================================================
--- Python-2.7.18.orig/Lib/Cookie.py	2020-04-19 23:13:39.000000000 +0200
+++ Python-2.7.18/Lib/Cookie.py	2026-02-13 23:56:53.140889038 +0100
@@ -96,9 +96,9 @@
 such trickeries do not confuse it.
 
    >>> C = Cookie.SmartCookie()
-   >>> 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
@@ -309,6 +309,20 @@
 
 _idmap = ''.join(chr(x) for x in xrange(256))
 
+_control_character_re = re.compile(r'[\x00-\x1F\x7F]')
+_legal_key_re = re.compile('[%s]+' % re.escape(_LegalChars))
+
+def _is_legal_key(s):
+    m = _legal_key_re.match(s)
+    return m if m and m.end() == len(s) else None
+
+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,
            idmap=_idmap, translate=string.translate):
     #
@@ -441,6 +455,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)
     # end __setitem__
 
@@ -455,6 +473,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 "" != translate(key, idmap, LegalChars):
             raise CookieError("Illegal key value: %s" % key)
 
@@ -605,8 +635,11 @@
         result = []
         items = self.items()
         items.sort()
-        for K,V in items:
-            result.append( V.output(attrs, header) )
+        for key, value in items:
+            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)
     # end output
 
Index: Python-2.7.18/Lib/test/support/__init__.py
===================================================================
--- Python-2.7.18.orig/Lib/test/support/__init__.py	2020-04-19 23:13:39.000000000 +0200
+++ Python-2.7.18/Lib/test/support/__init__.py	2026-02-13 23:53:40.671595581 +0100
@@ -45,7 +45,8 @@
            "check_impl_detail", "get_attribute", "py3k_bytes",
            "import_fresh_module", "threading_cleanup", "reap_children",
            "strip_python_stderr", "IPV6_ENABLED", "run_with_tz",
-           "SuppressCrashReport"]
+           "SuppressCrashReport",
+           "control_characters_c0"]
 
 class Error(Exception):
     """Base class for regression test exceptions."""
@@ -2175,3 +2176,9 @@
     def restore(self):
         for signum, handler in self.handlers.items():
             self.signal.signal(signum, handler)
+
+def control_characters_c0():
+    """Returns a list of C0 control characters as strings.
+    C0 control characters defined as the byte range 0x00-0x1F, and 0x7F.
+    """
+    return [chr(c) for c in range(0x00, 0x20)] + ["\x7F"]
Index: Python-2.7.18/Lib/test/test_cookie.py
===================================================================
--- Python-2.7.18.orig/Lib/test/test_cookie.py	2020-04-19 23:13:39.000000000 +0200
+++ Python-2.7.18/Lib/test/test_cookie.py	2026-02-13 23:53:40.671828970 +0100
@@ -1,6 +1,10 @@
 # Simple test suite for Cookie.py
 
-from test.test_support import run_unittest, run_doctest, check_warnings
+from test.test_support import (
+        run_unittest,
+        run_doctest,
+        check_warnings,
+        control_characters_c0)
 import unittest
 import Cookie
 import pickle
@@ -168,6 +172,44 @@
             C1 = pickle.loads(pickle.dumps(C, protocol=proto))
             self.assertEqual(C1.output(), expected_output)
 
+    def test_control_characters(self):
+        for c0 in control_characters_c0():
+            morsel = Cookie.Morsel()
+
+            # .__setitem__()
+            with self.assertRaises(Cookie.CookieError):
+                morsel[c0] = "val"
+            with self.assertRaises(Cookie.CookieError):
+                morsel["path"] = c0
+
+            # .set()
+            with self.assertRaises(Cookie.CookieError):
+                morsel.set(c0, "val", "coded-value")
+            with self.assertRaises(Cookie.CookieError):
+                morsel.set("path", c0, "coded-value")
+            with self.assertRaises(Cookie.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 = Cookie.Morsel()
+            morsel.set("key", "value", "coded-value")
+            morsel.key = c0  # Override internal variable.
+            cookie = Cookie.SimpleCookie()
+            cookie["cookie"] = morsel
+            with self.assertRaises(Cookie.CookieError):
+                cookie.output()
+
+            morsel = Cookie.Morsel()
+            morsel.set("key", "value", "coded-value")
+            morsel.coded_value = c0  # Override internal variable.
+            cookie = Cookie.SimpleCookie()
+            cookie["cookie"] = morsel
+            with self.assertRaises(Cookie.CookieError):
+                cookie.output()
+
 
 def test_main():
     run_unittest(CookieTests)
Index: Python-2.7.18/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-2.7.18/Misc/NEWS.d/next/Security/2026-01-16-11-13-15.gh-issue-143919.kchwZV.rst	2026-02-13 23:53:40.672202173 +0100
@@ -0,0 +1 @@
+Reject control characters in :class:`http.cookies.Morsel` fields and values.
openSUSE Build Service is sponsored by