File CVE-2026-0865-wsgiref-ctrl-chars.patch of Package python.42782

From 123bfbbe9074ef7fa28e1e7b25575665296560fa Mon Sep 17 00:00:00 2001
From: "Gregory P. Smith" <68491+gpshead@users.noreply.github.com>
Date: Sat, 17 Jan 2026 10:23:57 -0800
Subject: [PATCH] [3.10] gh-143916: Reject control characters in
 wsgiref.headers.Headers (GH-143917) (GH-143973)

gh-143916: Reject control characters in wsgiref.headers.Headers  (GH-143917)

* Add 'test.support' fixture for C0 control characters
* gh-143916: Reject control characters in wsgiref.headers.Headers

(cherry picked from commit f7fceed79ca1bceae8dbe5ba5bc8928564da7211)
(cherry picked from commit 22e4d55285cee52bc4dbe061324e5f30bd4dee58)

Co-authored-by: Gregory P. Smith <68491+gpshead@users.noreply.github.com>
Co-authored-by: Seth Michael Larson <seth@python.org>
---
 Lib/test/test_wsgiref.py                                                 |   14 +++-
 Lib/wsgiref/headers.py                                                   |   31 +++++++---
 Misc/NEWS.d/next/Security/2026-01-16-11-07-36.gh-issue-143916.dpWeOD.rst |    2 
 3 files changed, 37 insertions(+), 10 deletions(-)
 create mode 100644 Misc/NEWS.d/next/Security/2026-01-16-11-07-36.gh-issue-143916.dpWeOD.rst

Index: Python-2.7.18/Lib/test/test_wsgiref.py
===================================================================
--- Python-2.7.18.orig/Lib/test/test_wsgiref.py	2020-04-19 23:13:39.000000000 +0200
+++ Python-2.7.18/Lib/test/test_wsgiref.py	2026-02-13 21:48:59.446031367 +0100
@@ -13,7 +13,7 @@
 import re
 import sys
 
-from test import support
+from test.support import run_unittest, control_characters_c0
 
 class MockServer(WSGIServer):
     """Non-socket HTTP server"""
@@ -354,6 +354,16 @@
         )
 
 
+    def testRaisesControlCharacters(self):
+        headers = Headers([])
+        for c0 in control_characters_c0():
+            self.assertRaises(ValueError, headers.__setitem__, "key{}".format(c0), "val")
+            self.assertRaises(ValueError, headers.__setitem__, "key", "val{}".format(c0))
+            self.assertRaises(ValueError, headers.add_header, "key{}".format(c0), "val", param="param")
+            self.assertRaises(ValueError, headers.add_header, "key", "val{}".format(c0), param="param")
+            self.assertRaises(ValueError, headers.add_header, "key", "val", param="param{}".format(c0))
+
+
 class ErrorHandler(BaseCGIHandler):
     """Simple handler subclass for testing BaseHandler"""
 
@@ -595,7 +605,7 @@
 
 
 def test_main():
-    support.run_unittest(__name__)
+    run_unittest(__name__)
 
 if __name__ == "__main__":
     test_main()
Index: Python-2.7.18/Lib/wsgiref/headers.py
===================================================================
--- Python-2.7.18.orig/Lib/wsgiref/headers.py	2020-04-19 23:13:39.000000000 +0200
+++ Python-2.7.18/Lib/wsgiref/headers.py	2026-02-13 21:58:38.977730950 +0100
@@ -11,6 +11,7 @@
 # existence of which force quoting of the parameter value.
 import re
 tspecials = re.compile(r'[ \(\)<>@,;:\\"/\[\]\?=]')
+_control_chars_re = re.compile(r'[\x00-\x1F\x7F]')
 
 def _formatparam(param, value=None, quote=1):
     """Convenience function to format and return a key=value pair.
@@ -40,17 +41,27 @@
         """Return the total number of headers, including duplicates."""
         return len(self._headers)
 
+    def _convert_string_type(self, value):
+        """Convert/check value type."""
+        if type(value) is str:
+            if _control_chars_re.search(value):
+                raise ValueError("Control characters not allowed in headers")
+            return value
+        raise AssertionError("Header names/values must be"
+            " of type str (got {0})".format(repr(value)))
+
     def __setitem__(self, name, val):
         """Set the value of a header."""
         del self[name]
-        self._headers.append((name, val))
+        self._headers.append((self._convert_string_type(name),
+                              self._convert_string_type(val)))
 
     def __delitem__(self,name):
         """Delete all occurrences of a header, if present.
 
         Does *not* raise an exception if the header is missing.
         """
-        name = name.lower()
+        name = self._convert_string_type(name.lower())
         self._headers[:] = [kv for kv in self._headers if kv[0].lower() != name]
 
     def __getitem__(self,name):
@@ -79,13 +90,13 @@
         fields deleted and re-inserted are always appended to the header list.
         If no fields exist with the given name, returns an empty list.
         """
-        name = name.lower()
+        name = self._convert_string_type(name.lower())
         return [kv[1] for kv in self._headers if kv[0].lower()==name]
 
 
     def get(self,name,default=None):
         """Get the first header value for 'name', or return 'default'"""
-        name = name.lower()
+        name = self._convert_string_type(name.lower())
         for k,v in self._headers:
             if k.lower()==name:
                 return v
@@ -137,7 +148,8 @@
         and value 'value'."""
         result = self.get(name)
         if result is None:
-            self._headers.append((name,value))
+            self._headers.append((self._convert_string_type(name),
+                                  self._convert_string_type(value)))
             return value
         else:
             return result
@@ -160,10 +172,13 @@
         """
         parts = []
         if _value is not None:
-            parts.append(_value)
+            parts.append(self._convert_string_type(_value))
         for k, v in _params.items():
+            k = self._convert_string_type(k)
             if v is None:
                 parts.append(k.replace('_', '-'))
             else:
-                parts.append(_formatparam(k.replace('_', '-'), v))
-        self._headers.append((_name, "; ".join(parts)))
+                parts.append(_formatparam(k.replace('_', '-'),
+                                          self._convert_string_type(v)))
+        self._headers.append((self._convert_string_type(_name),
+                              "; ".join(parts)))
Index: Python-2.7.18/Misc/NEWS.d/next/Security/2026-01-16-11-07-36.gh-issue-143916.dpWeOD.rst
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ Python-2.7.18/Misc/NEWS.d/next/Security/2026-01-16-11-07-36.gh-issue-143916.dpWeOD.rst	2026-02-13 21:48:59.446547460 +0100
@@ -0,0 +1,2 @@
+Reject C0 control characters within wsgiref.headers.Headers fields, values,
+and parameters.
openSUSE Build Service is sponsored by