https://github.com/python/cpython/commit/5db017755b261281aec661a7c1578b46b2a18565
commit: 5db017755b261281aec661a7c1578b46b2a18565
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: vstinner <[email protected]>
date: 2026-03-06T12:45:34Z
summary:

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

gh-144370: Disallow usage of control characters in status in wsgiref.handlers 
for security (GH-144371)

Disallow usage of control characters in status in wsgiref.handlers
to prevent HTTP header injections.
(cherry picked from commit d931725bc850cd096f6703bc285e885f1e015f05)

Co-authored-by: Benedikt Johannes <[email protected]>
Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Co-authored-by: Victor Stinner <[email protected]>

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 010958e223b9b0..34e52fe40f500e 100644
--- a/Lib/test/test_wsgiref.py
+++ b/Lib/test/test_wsgiref.py
@@ -855,6 +855,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 cafe872c7aae9b..f8fe89f6e13436 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
 
@@ -249,6 +249,8 @@ def start_response(self, status, headers,exc_info=None):
         return self.write
 
     def _validate_status(self, status):
+        if _name_disallowed_re.search(status):
+            raise ValueError("Control characters are not allowed in status")
         if len(status) < 4:
             raise AssertionError("Status must be at least 4 characters")
         if not status[:3].isdigit():
diff --git a/Misc/ACKS b/Misc/ACKS
index c7515c28cb6b18..d4f0e5f9f80625 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -1055,6 +1055,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