https://github.com/python/cpython/commit/10cf7e2bf76171f7c3b050a9acd762320e00486b
commit: 10cf7e2bf76171f7c3b050a9acd762320e00486b
branch: 3.10
author: Victor Stinner <[email protected]>
committer: pablogsal <[email protected]>
date: 2026-05-17T11:33:20-07:00
summary:

[3.10] gh-144370: Disallow usage of control characters in status in 
wsgiref.handlers for security (#144371) (#145673)

files:
A Misc/NEWS.d/next/Security/2026-01-31-21-56-54.gh-issue-144370.fp9m8t.rst
M Lib/test/test_wsgiref.py
M Lib/wsgiref/handlers.py
M Misc/ACKS

diff --git a/Lib/test/test_wsgiref.py b/Lib/test/test_wsgiref.py
index 7c6c72f6e81384..a2b07f6aa345d7 100644
--- a/Lib/test/test_wsgiref.py
+++ b/Lib/test/test_wsgiref.py
@@ -879,6 +879,25 @@ def write(self, b):
         self.assertIsNotNone(h.status)
         self.assertIsNotNone(h.environ)
 
+    def testRaisesControlCharacters(self):
+        for c0 in control_characters_c0():
+            with self.subTest(c0):
+                base = BaseHandler()
+                with self.assertRaises(ValueError):
+                    base.start_response(c0, [('x', 'y')])
+
+                base = BaseHandler()
+                with self.assertRaises(ValueError):
+                    base.start_response('200 OK', [(c0, 'y')])
+
+                # HTAB (\x09) is allowed in header values, but not in names.
+                base = BaseHandler()
+                if c0 != "\t":
+                    with self.assertRaises(ValueError):
+                        base.start_response('200 OK', [('x', c0)])
+                else:
+                    base.start_response('200 OK', [('x', c0)])
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/Lib/wsgiref/handlers.py b/Lib/wsgiref/handlers.py
index 31360e58785ac6..df30af39293d2c 100644
--- a/Lib/wsgiref/handlers.py
+++ b/Lib/wsgiref/handlers.py
@@ -1,7 +1,7 @@
 """Base classes for server/gateway implementations"""
 
 from .util import FileWrapper, guess_scheme, is_hop_by_hop
-from .headers import Headers
+from .headers import Headers, _name_disallowed_re
 
 import sys, os, time
 
@@ -238,6 +238,8 @@ def start_response(self, status, headers,exc_info=None):
         self.status = status
         self.headers = self.headers_class(headers)
         status = self._convert_string_type(status, "Status")
+        if _name_disallowed_re.search(status):
+            raise ValueError("Control characters are not allowed in status")
         assert len(status)>=4,"Status must be at least 4 characters"
         assert status[:3].isdigit(), "Status message must begin w/3-digit code"
         assert status[3]==" ", "Status message must have a space after code"
diff --git a/Misc/ACKS b/Misc/ACKS
index 8483e110508889..9c28337a4b4343 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -1006,6 +1006,7 @@ Wolfgang Langner
 Detlef Lannert
 Rémi Lapeyre
 Soren Larsen
+Seth Michael Larson
 Amos Latteier
 Piers Lauder
 Ben Laurie
diff --git 
a/Misc/NEWS.d/next/Security/2026-01-31-21-56-54.gh-issue-144370.fp9m8t.rst 
b/Misc/NEWS.d/next/Security/2026-01-31-21-56-54.gh-issue-144370.fp9m8t.rst
new file mode 100644
index 00000000000000..2d13a0611322c5
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2026-01-31-21-56-54.gh-issue-144370.fp9m8t.rst
@@ -0,0 +1,2 @@
+Disallow usage of control characters in status in :mod:`wsgiref.handlers` to 
prevent HTTP header injections.
+Patch by Benedikt Johannes.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to