File CVE-2024-6827.patch of Package python-gunicorn.38005
Index: gunicorn-19.7.1/gunicorn/config.py
===================================================================
--- gunicorn-19.7.1.orig/gunicorn/config.py
+++ gunicorn-19.7.1/gunicorn/config.py
@@ -1964,21 +1964,3 @@ class HeaderMap(Setting):
.. versionadded:: 22.0.0
"""
-
-
-class TolerateDangerousFraming(Setting):
- name = "tolerate_dangerous_framing"
- section = "Server Mechanics"
- cli = ["--tolerate-dangerous-framing"]
- validator = validate_bool
- action = "store_true"
- default = False
- desc = """\
- Process requests with both Transfer-Encoding and Content-Length
-
- This is known to induce vulnerabilities, but not strictly forbidden by RFC9112.
-
- Use with care and only if necessary. May be removed in a future version.
-
- .. versionadded:: 22.0.0
- """
Index: gunicorn-19.7.1/gunicorn/http/message.py
===================================================================
--- gunicorn-19.7.1.orig/gunicorn/http/message.py
+++ gunicorn-19.7.1/gunicorn/http/message.py
@@ -136,31 +136,27 @@ class Message(object):
if name == "CONTENT-LENGTH":
content_length = value
elif name == "TRANSFER-ENCODING":
- if value.lower() == "chunked":
- # DANGER: transer codings stack, and stacked chunking is never intended
- if chunked:
- raise InvalidHeader("TRANSFER-ENCODING", req=self)
- chunked = True
- elif value.lower() == "identity":
- # does not do much, could still plausibly desync from what the proxy does
- # safe option: nuke it, its never needed
- if chunked:
- raise InvalidHeader("TRANSFER-ENCODING", req=self)
- elif value.lower() == "":
- # lacking security review on this case
- # offer the option to restore previous behaviour, but refuse by default, for now
- self.force_close()
- if not self.cfg.tolerate_dangerous_framing:
+ # T-E can be a list
+ # https://datatracker.ietf.org/doc/html/rfc9112#name-transfer-encoding
+ vals = [v.strip() for v in value.split(',')]
+ for val in vals:
+ if val.lower() == "chunked":
+ # DANGER: transfer codings stack, and stacked chunking is never intended
+ if chunked:
+ raise InvalidHeader("TRANSFER-ENCODING", req=self)
+ chunked = True
+ elif val.lower() == "identity":
+ # does not do much, could still plausibly desync from what the proxy does
+ # safe option: nuke it, its never needed
+ if chunked:
+ raise InvalidHeader("TRANSFER-ENCODING", req=self)
+ elif val.lower() in ('compress', 'deflate', 'gzip'):
+ # chunked should be the last one
+ if chunked:
+ raise InvalidHeader("TRANSFER-ENCODING", req=self)
+ self.force_close()
+ else:
raise UnsupportedTransferCoding(value)
- # DANGER: do not change lightly; ref: request smuggling
- # T-E is a list and we *could* support correctly parsing its elements
- # .. but that is only safe after getting all the edge cases right
- # .. for which no real-world need exists, so best to NOT open that can of worms
- else:
- self.force_close()
- # even if parser is extended, retain this branch:
- # the "chunked not last" case remains to be rejected!
- raise UnsupportedTransferCoding(value)
elif name == "SEC-WEBSOCKET-KEY1":
content_length = 8
@@ -170,16 +166,11 @@ class Message(object):
# b) chunked HTTP/1.0 (always faulty)
if self.version < (1, 1):
# framing wonky, see RFC 9112 Section 6.1
- self.force_close()
- if not self.cfg.tolerate_dangerous_framing:
- raise InvalidHeader("TRANSFER-ENCODING", req=self)
+ raise InvalidHeader("TRANSFER-ENCODING", req=self)
if content_length is not None:
# we cannot be certain the message framing we understood matches proxy intent
# -> whatever happens next, remaining input must not be trusted
- self.force_close()
- # either processing or rejecting is permitted in RFC 9112 Section 6.1
- if not self.cfg.tolerate_dangerous_framing:
- raise InvalidHeader("CONTENT-LENGTH", req=self)
+ raise InvalidHeader("CONTENT-LENGTH", req=self)
self.body = Body(ChunkedReader(self, self.unreader))
elif content_length is not None:
try:
Index: gunicorn-19.7.1/tests/requests/invalid/chunked_03.py
===================================================================
--- gunicorn-19.7.1.orig/tests/requests/invalid/chunked_03.py
+++ gunicorn-19.7.1/tests/requests/invalid/chunked_03.py
@@ -1,2 +1,2 @@
-from gunicorn.http.errors import UnsupportedTransferCoding
-request = UnsupportedTransferCoding
+from gunicorn.http.errors import InvalidHeader
+request = InvalidHeader
Index: gunicorn-19.7.1/tests/requests/invalid/chunked_06.py
===================================================================
--- gunicorn-19.7.1.orig/tests/requests/invalid/chunked_06.py
+++ gunicorn-19.7.1/tests/requests/invalid/chunked_06.py
@@ -1,2 +1,2 @@
-from gunicorn.http.errors import UnsupportedTransferCoding
-request = UnsupportedTransferCoding
+from gunicorn.http.errors import InvalidHeader
+request = InvalidHeader
Index: gunicorn-19.7.1/tests/requests/valid/025.http
===================================================================
--- gunicorn-19.7.1.orig/tests/requests/valid/025.http
+++ gunicorn-19.7.1/tests/requests/valid/025.http
@@ -1,24 +1,10 @@
-POST /chunked_cont_h_at_first HTTP/1.1\r\n
+POST /chunked HTTP/1.1\r\n
+Transfer-Encoding: gzip\r\n
Transfer-Encoding: chunked\r\n
\r\n
-5; some; parameters=stuff\r\n
+5\r\n
hello\r\n
-6 \t;\tblahblah; blah\r\n
+6\r\n
world\r\n
0\r\n
\r\n
-PUT /chunked_cont_h_at_last HTTP/1.1\r\n
-Transfer-Encoding: chunked\r\n
-Content-Length: -1\r\n
-\r\n
-5; some; parameters=stuff\r\n
-hello\r\n
-6; blahblah; blah\r\n
- world\r\n
-0\r\n
-\r\n
-PUT /ignored_after_dangerous_framing HTTP/1.1\r\n
-Content-Length: 3\r\n
-\r\n
-foo\r\n
-\r\n
Index: gunicorn-19.7.1/tests/requests/valid/025.py
===================================================================
--- gunicorn-19.7.1.orig/tests/requests/valid/025.py
+++ gunicorn-19.7.1/tests/requests/valid/025.py
@@ -1,27 +1,10 @@
-from gunicorn.config import Config
-
-cfg = Config()
-cfg.set("tolerate_dangerous_framing", True)
-
-req1 = {
+request = {
"method": "POST",
- "uri": uri("/chunked_cont_h_at_first"),
+ "uri": uri("/chunked"),
"version": (1, 1),
"headers": [
- ("TRANSFER-ENCODING", "chunked")
+ ('TRANSFER-ENCODING', 'gzip'),
+ ('TRANSFER-ENCODING', 'chunked')
],
"body": b"hello world"
}
-
-req2 = {
- "method": "PUT",
- "uri": uri("/chunked_cont_h_at_last"),
- "version": (1, 1),
- "headers": [
- ("TRANSFER-ENCODING", "chunked"),
- ("CONTENT-LENGTH", "-1"),
- ],
- "body": b"hello world"
-}
-
-request = [req1, req2]
Index: gunicorn-19.7.1/tests/requests/valid/025_line.http
===================================================================
--- /dev/null
+++ gunicorn-19.7.1/tests/requests/valid/025_line.http
@@ -0,0 +1,9 @@
+POST /chunked HTTP/1.1\r\n
+Transfer-Encoding: gzip,chunked\r\n
+\r\n
+5\r\n
+hello\r\n
+6\r\n
+ world\r\n
+0\r\n
+\r\n
Index: gunicorn-19.7.1/tests/requests/valid/025_line.py
===================================================================
--- /dev/null
+++ gunicorn-19.7.1/tests/requests/valid/025_line.py
@@ -0,0 +1,10 @@
+request = {
+ "method": "POST",
+ "uri": uri("/chunked"),
+ "version": (1, 1),
+ "headers": [
+ ('TRANSFER-ENCODING', 'gzip,chunked')
+
+ ],
+ "body": b"hello world"
+}
Index: gunicorn-19.7.1/tests/requests/valid/025compat.http
===================================================================
--- gunicorn-19.7.1.orig/tests/requests/valid/025compat.http
+++ /dev/null
@@ -1,18 +0,0 @@
-POST /chunked_cont_h_at_first HTTP/1.1\r\n
-Transfer-Encoding: chunked\r\n
-\r\n
-5; some; parameters=stuff\r\n
-hello\r\n
-6; blahblah; blah\r\n
- world\r\n
-0\r\n
-\r\n
-PUT /chunked_cont_h_at_last HTTP/1.1\r\n
-Transfer-Encoding: chunked\r\n
-Content-Length: -1\r\n
-\r\n
-5; some; parameters=stuff\r\n
-hello\r\n
-6; blahblah; blah\r\n
- world\r\n
-0\r\n
Index: gunicorn-19.7.1/tests/requests/valid/025compat.py
===================================================================
--- gunicorn-19.7.1.orig/tests/requests/valid/025compat.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from gunicorn.config import Config
-
-cfg = Config()
-cfg.set("tolerate_dangerous_framing", True)
-
-req1 = {
- "method": "POST",
- "uri": uri("/chunked_cont_h_at_first"),
- "version": (1, 1),
- "headers": [
- ("TRANSFER-ENCODING", "chunked")
- ],
- "body": b"hello world"
-}
-
-req2 = {
- "method": "PUT",
- "uri": uri("/chunked_cont_h_at_last"),
- "version": (1, 1),
- "headers": [
- ("TRANSFER-ENCODING", "chunked"),
- ("CONTENT-LENGTH", "-1"),
- ],
- "body": b"hello world"
-}
-
-request = [req1, req2]