https://github.com/python/cpython/commit/d931725bc850cd096f6703bc285e885f1e015f05
commit: d931725bc850cd096f6703bc285e885f1e015f05
branch: main
author: Benedikt Johannes <[email protected]>
committer: vstinner <[email protected]>
date: 2026-03-06T13:22:21+01:00
summary:

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

Disallow usage of control characters in status in wsgiref.handlers
to prevent HTTP header injections.

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 a7a5c5ba33d493..3379df37d38ca8 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)])
+
 
 class TestModule(unittest.TestCase):
     def test_deprecated__version__(self):
diff --git a/Lib/wsgiref/handlers.py b/Lib/wsgiref/handlers.py
index 9353fb678625b3..b82862deea7d74 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
 
@@ -250,6 +250,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 e38bda37bfa92d..88c0a68f1e6c87 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -1086,6 +1086,7 @@ Wolfgang Langner
 Detlef Lannert
 Rémi Lapeyre
 Soren Larsen
+Seth Michael Larson
 Amos Latteier
 Keenan Lau
 Piers Lauder
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