Compare commits
1 Commits
Author | SHA256 | Date | |
---|---|---|---|
a26d354df3 |
@@ -1,138 +0,0 @@
|
|||||||
From d5ba4a1695fbf7c6a3e54313262639b198291533 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Ben Darnell <ben@bendarnell.com>
|
|
||||||
Date: Thu, 21 Nov 2024 14:48:05 -0500
|
|
||||||
Subject: [PATCH] httputil: Fix quadratic performance of cookie parsing
|
|
||||||
|
|
||||||
Maliciously-crafted cookies can cause Tornado to
|
|
||||||
spend an unreasonable amount of CPU time and block
|
|
||||||
the event loop.
|
|
||||||
|
|
||||||
This change replaces the quadratic algorithm with
|
|
||||||
a more efficient one. The implementation is copied
|
|
||||||
from the Python 3.13 standard library (the
|
|
||||||
previous one was from Python 3.5).
|
|
||||||
|
|
||||||
Fixes CVE-2024-52804
|
|
||||||
See CVE-2024-7592 for a similar vulnerability in cpython.
|
|
||||||
|
|
||||||
Thanks to github.com/kexinoh for the report.
|
|
||||||
---
|
|
||||||
tornado/httputil.py | 38 ++++++++---------------------
|
|
||||||
tornado/test/httputil_test.py | 46 +++++++++++++++++++++++++++++++++++
|
|
||||||
2 files changed, 56 insertions(+), 28 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/tornado/httputil.py b/tornado/httputil.py
|
|
||||||
index 9ce992d82b..ebdc8059c1 100644
|
|
||||||
--- a/tornado/httputil.py
|
|
||||||
+++ b/tornado/httputil.py
|
|
||||||
@@ -1057,15 +1057,20 @@ def qs_to_qsl(qs: Dict[str, List[AnyStr]]) -> Iterable[Tuple[str, AnyStr]]:
|
|
||||||
yield (k, v)
|
|
||||||
|
|
||||||
|
|
||||||
-_OctalPatt = re.compile(r"\\[0-3][0-7][0-7]")
|
|
||||||
-_QuotePatt = re.compile(r"[\\].")
|
|
||||||
-_nulljoin = "".join
|
|
||||||
+_unquote_sub = re.compile(r"\\(?:([0-3][0-7][0-7])|(.))").sub
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+def _unquote_replace(m: re.Match) -> str:
|
|
||||||
+ if m[1]:
|
|
||||||
+ return chr(int(m[1], 8))
|
|
||||||
+ else:
|
|
||||||
+ return m[2]
|
|
||||||
|
|
||||||
|
|
||||||
def _unquote_cookie(s: str) -> str:
|
|
||||||
"""Handle double quotes and escaping in cookie values.
|
|
||||||
|
|
||||||
- This method is copied verbatim from the Python 3.5 standard
|
|
||||||
+ This method is copied verbatim from the Python 3.13 standard
|
|
||||||
library (http.cookies._unquote) so we don't have to depend on
|
|
||||||
non-public interfaces.
|
|
||||||
"""
|
|
||||||
@@ -1086,30 +1091,7 @@ def _unquote_cookie(s: str) -> str:
|
|
||||||
# \012 --> \n
|
|
||||||
# \" --> "
|
|
||||||
#
|
|
||||||
- i = 0
|
|
||||||
- n = len(s)
|
|
||||||
- res = []
|
|
||||||
- while 0 <= i < n:
|
|
||||||
- o_match = _OctalPatt.search(s, i)
|
|
||||||
- q_match = _QuotePatt.search(s, i)
|
|
||||||
- if not o_match and not q_match: # Neither matched
|
|
||||||
- res.append(s[i:])
|
|
||||||
- break
|
|
||||||
- # else:
|
|
||||||
- j = k = -1
|
|
||||||
- if o_match:
|
|
||||||
- j = o_match.start(0)
|
|
||||||
- if q_match:
|
|
||||||
- k = q_match.start(0)
|
|
||||||
- if q_match and (not o_match or k < j): # QuotePatt matched
|
|
||||||
- res.append(s[i:k])
|
|
||||||
- res.append(s[k + 1])
|
|
||||||
- i = k + 2
|
|
||||||
- else: # OctalPatt matched
|
|
||||||
- res.append(s[i:j])
|
|
||||||
- res.append(chr(int(s[j + 1 : j + 4], 8)))
|
|
||||||
- i = j + 4
|
|
||||||
- return _nulljoin(res)
|
|
||||||
+ return _unquote_sub(_unquote_replace, s)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_cookie(cookie: str) -> Dict[str, str]:
|
|
||||||
diff --git a/tornado/test/httputil_test.py b/tornado/test/httputil_test.py
|
|
||||||
index 6d618839e0..975900aa9c 100644
|
|
||||||
--- a/tornado/test/httputil_test.py
|
|
||||||
+++ b/tornado/test/httputil_test.py
|
|
||||||
@@ -560,3 +560,49 @@ def test_invalid_cookies(self):
|
|
||||||
self.assertEqual(
|
|
||||||
parse_cookie(" = b ; ; = ; c = ; "), {"": "b", "c": ""}
|
|
||||||
)
|
|
||||||
+
|
|
||||||
+ def test_unquote(self):
|
|
||||||
+ # Copied from
|
|
||||||
+ # https://github.com/python/cpython/blob/dc7a2b6522ec7af41282bc34f405bee9b306d611/Lib/test/test_http_cookies.py#L62
|
|
||||||
+ cases = [
|
|
||||||
+ (r'a="b=\""', 'b="'),
|
|
||||||
+ (r'a="b=\\"', "b=\\"),
|
|
||||||
+ (r'a="b=\="', "b=="),
|
|
||||||
+ (r'a="b=\n"', "b=n"),
|
|
||||||
+ (r'a="b=\042"', 'b="'),
|
|
||||||
+ (r'a="b=\134"', "b=\\"),
|
|
||||||
+ (r'a="b=\377"', "b=\xff"),
|
|
||||||
+ (r'a="b=\400"', "b=400"),
|
|
||||||
+ (r'a="b=\42"', "b=42"),
|
|
||||||
+ (r'a="b=\\042"', "b=\\042"),
|
|
||||||
+ (r'a="b=\\134"', "b=\\134"),
|
|
||||||
+ (r'a="b=\\\""', 'b=\\"'),
|
|
||||||
+ (r'a="b=\\\042"', 'b=\\"'),
|
|
||||||
+ (r'a="b=\134\""', 'b=\\"'),
|
|
||||||
+ (r'a="b=\134\042"', 'b=\\"'),
|
|
||||||
+ ]
|
|
||||||
+ for encoded, decoded in cases:
|
|
||||||
+ with self.subTest(encoded):
|
|
||||||
+ c = parse_cookie(encoded)
|
|
||||||
+ self.assertEqual(c["a"], decoded)
|
|
||||||
+
|
|
||||||
+ def test_unquote_large(self):
|
|
||||||
+ # Adapted from
|
|
||||||
+ # https://github.com/python/cpython/blob/dc7a2b6522ec7af41282bc34f405bee9b306d611/Lib/test/test_http_cookies.py#L87
|
|
||||||
+ # Modified from that test because we handle semicolons differently from the stdlib.
|
|
||||||
+ #
|
|
||||||
+ # This is a performance regression test: prior to improvements in Tornado 6.4.2, this test
|
|
||||||
+ # would take over a minute with n= 100k. Now it runs in tens of milliseconds.
|
|
||||||
+ n = 100000
|
|
||||||
+ for encoded in r"\\", r"\134":
|
|
||||||
+ with self.subTest(encoded):
|
|
||||||
+ start = time.time()
|
|
||||||
+ data = 'a="b=' + encoded * n + '"'
|
|
||||||
+ value = parse_cookie(data)["a"]
|
|
||||||
+ end = time.time()
|
|
||||||
+ self.assertEqual(value[:3], "b=\\")
|
|
||||||
+ self.assertEqual(value[-3:], "\\\\\\")
|
|
||||||
+ self.assertEqual(len(value), n + 2)
|
|
||||||
+
|
|
||||||
+ # Very loose performance check to avoid false positives
|
|
||||||
+ self.assertLess(end - start, 1, "Test took too long")
|
|
@@ -1,228 +0,0 @@
|
|||||||
From cc61050e8f26697463142d99864b562e8470b41d Mon Sep 17 00:00:00 2001
|
|
||||||
From: Ben Darnell <ben@bendarnell.com>
|
|
||||||
Date: Thu, 8 May 2025 13:29:43 -0400
|
|
||||||
Subject: [PATCH] httputil: Raise errors instead of logging in
|
|
||||||
multipart/form-data parsing
|
|
||||||
|
|
||||||
We used to continue after logging an error, which allowed repeated
|
|
||||||
errors to spam the logs. The error raised here will still be logged,
|
|
||||||
but only once per request, consistent with other error handling in
|
|
||||||
Tornado.
|
|
||||||
---
|
|
||||||
tornado/httputil.py | 30 +++++++++++-------------------
|
|
||||||
tornado/test/httpserver_test.py | 4 ++--
|
|
||||||
tornado/test/httputil_test.py | 13 ++++++++-----
|
|
||||||
tornado/web.py | 17 +++++++++++++----
|
|
||||||
4 files changed, 34 insertions(+), 30 deletions(-)
|
|
||||||
|
|
||||||
Index: tornado-6.4/tornado/httputil.py
|
|
||||||
===================================================================
|
|
||||||
--- tornado-6.4.orig/tornado/httputil.py
|
|
||||||
+++ tornado-6.4/tornado/httputil.py
|
|
||||||
@@ -34,7 +34,6 @@ import unicodedata
|
|
||||||
from urllib.parse import urlencode, urlparse, urlunparse, parse_qsl
|
|
||||||
|
|
||||||
from tornado.escape import native_str, parse_qs_bytes, utf8
|
|
||||||
-from tornado.log import gen_log
|
|
||||||
from tornado.util import ObjectDict, unicode_type
|
|
||||||
|
|
||||||
|
|
||||||
@@ -759,25 +758,22 @@ def parse_body_arguments(
|
|
||||||
"""
|
|
||||||
if content_type.startswith("application/x-www-form-urlencoded"):
|
|
||||||
if headers and "Content-Encoding" in headers:
|
|
||||||
- gen_log.warning(
|
|
||||||
- "Unsupported Content-Encoding: %s", headers["Content-Encoding"]
|
|
||||||
+ raise HTTPInputError(
|
|
||||||
+ "Unsupported Content-Encoding: %s" % headers["Content-Encoding"]
|
|
||||||
)
|
|
||||||
- return
|
|
||||||
try:
|
|
||||||
# real charset decoding will happen in RequestHandler.decode_argument()
|
|
||||||
uri_arguments = parse_qs_bytes(body, keep_blank_values=True)
|
|
||||||
except Exception as e:
|
|
||||||
- gen_log.warning("Invalid x-www-form-urlencoded body: %s", e)
|
|
||||||
- uri_arguments = {}
|
|
||||||
+ raise HTTPInputError("Invalid x-www-form-urlencoded body: %s" % e) from e
|
|
||||||
for name, values in uri_arguments.items():
|
|
||||||
if values:
|
|
||||||
arguments.setdefault(name, []).extend(values)
|
|
||||||
elif content_type.startswith("multipart/form-data"):
|
|
||||||
if headers and "Content-Encoding" in headers:
|
|
||||||
- gen_log.warning(
|
|
||||||
- "Unsupported Content-Encoding: %s", headers["Content-Encoding"]
|
|
||||||
+ raise HTTPInputError(
|
|
||||||
+ "Unsupported Content-Encoding: %s" % headers["Content-Encoding"]
|
|
||||||
)
|
|
||||||
- return
|
|
||||||
try:
|
|
||||||
fields = content_type.split(";")
|
|
||||||
for field in fields:
|
|
||||||
@@ -786,9 +782,9 @@ def parse_body_arguments(
|
|
||||||
parse_multipart_form_data(utf8(v), body, arguments, files)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
- raise ValueError("multipart boundary not found")
|
|
||||||
+ raise HTTPInputError("multipart boundary not found")
|
|
||||||
except Exception as e:
|
|
||||||
- gen_log.warning("Invalid multipart/form-data: %s", e)
|
|
||||||
+ raise HTTPInputError("Invalid multipart/form-data: %s" % e) from e
|
|
||||||
|
|
||||||
|
|
||||||
def parse_multipart_form_data(
|
|
||||||
@@ -817,26 +813,22 @@ def parse_multipart_form_data(
|
|
||||||
boundary = boundary[1:-1]
|
|
||||||
final_boundary_index = data.rfind(b"--" + boundary + b"--")
|
|
||||||
if final_boundary_index == -1:
|
|
||||||
- gen_log.warning("Invalid multipart/form-data: no final boundary")
|
|
||||||
- return
|
|
||||||
+ raise HTTPInputError("Invalid multipart/form-data: no final boundary found")
|
|
||||||
parts = data[:final_boundary_index].split(b"--" + boundary + b"\r\n")
|
|
||||||
for part in parts:
|
|
||||||
if not part:
|
|
||||||
continue
|
|
||||||
eoh = part.find(b"\r\n\r\n")
|
|
||||||
if eoh == -1:
|
|
||||||
- gen_log.warning("multipart/form-data missing headers")
|
|
||||||
- continue
|
|
||||||
+ raise HTTPInputError("multipart/form-data missing headers")
|
|
||||||
headers = HTTPHeaders.parse(part[:eoh].decode("utf-8"))
|
|
||||||
disp_header = headers.get("Content-Disposition", "")
|
|
||||||
disposition, disp_params = _parse_header(disp_header)
|
|
||||||
if disposition != "form-data" or not part.endswith(b"\r\n"):
|
|
||||||
- gen_log.warning("Invalid multipart/form-data")
|
|
||||||
- continue
|
|
||||||
+ raise HTTPInputError("Invalid multipart/form-data")
|
|
||||||
value = part[eoh + 4 : -2]
|
|
||||||
if not disp_params.get("name"):
|
|
||||||
- gen_log.warning("multipart/form-data value missing name")
|
|
||||||
- continue
|
|
||||||
+ raise HTTPInputError("multipart/form-data missing name")
|
|
||||||
name = disp_params["name"]
|
|
||||||
if disp_params.get("filename"):
|
|
||||||
ctype = headers.get("Content-Type", "application/unknown")
|
|
||||||
Index: tornado-6.4/tornado/test/httpserver_test.py
|
|
||||||
===================================================================
|
|
||||||
--- tornado-6.4.orig/tornado/test/httpserver_test.py
|
|
||||||
+++ tornado-6.4/tornado/test/httpserver_test.py
|
|
||||||
@@ -1061,9 +1061,9 @@ class GzipUnsupportedTest(GzipBaseTest,
|
|
||||||
# Gzip support is opt-in; without it the server fails to parse
|
|
||||||
# the body (but parsing form bodies is currently just a log message,
|
|
||||||
# not a fatal error).
|
|
||||||
- with ExpectLog(gen_log, "Unsupported Content-Encoding"):
|
|
||||||
+ with ExpectLog(gen_log, ".*Unsupported Content-Encoding"):
|
|
||||||
response = self.post_gzip("foo=bar")
|
|
||||||
- self.assertEqual(json_decode(response.body), {})
|
|
||||||
+ self.assertEqual(response.code, 400)
|
|
||||||
|
|
||||||
|
|
||||||
class StreamingChunkSizeTest(AsyncHTTPTestCase):
|
|
||||||
Index: tornado-6.4/tornado/test/httputil_test.py
|
|
||||||
===================================================================
|
|
||||||
--- tornado-6.4.orig/tornado/test/httputil_test.py
|
|
||||||
+++ tornado-6.4/tornado/test/httputil_test.py
|
|
||||||
@@ -12,7 +12,6 @@ from tornado.httputil import (
|
|
||||||
)
|
|
||||||
from tornado.escape import utf8, native_str
|
|
||||||
from tornado.log import gen_log
|
|
||||||
-from tornado.testing import ExpectLog
|
|
||||||
from tornado.test.util import ignore_deprecation
|
|
||||||
|
|
||||||
import copy
|
|
||||||
@@ -195,7 +194,9 @@ Foo
|
|
||||||
b"\n", b"\r\n"
|
|
||||||
)
|
|
||||||
args, files = form_data_args()
|
|
||||||
- with ExpectLog(gen_log, "multipart/form-data missing headers"):
|
|
||||||
+ with self.assertRaises(
|
|
||||||
+ HTTPInputError, msg="multipart/form-data missing headers"
|
|
||||||
+ ):
|
|
||||||
parse_multipart_form_data(b"1234", data, args, files)
|
|
||||||
self.assertEqual(files, {})
|
|
||||||
|
|
||||||
@@ -209,7 +210,7 @@ Foo
|
|
||||||
b"\n", b"\r\n"
|
|
||||||
)
|
|
||||||
args, files = form_data_args()
|
|
||||||
- with ExpectLog(gen_log, "Invalid multipart/form-data"):
|
|
||||||
+ with self.assertRaises(HTTPInputError, msg="Invalid multipart/form-data"):
|
|
||||||
parse_multipart_form_data(b"1234", data, args, files)
|
|
||||||
self.assertEqual(files, {})
|
|
||||||
|
|
||||||
@@ -222,7 +223,7 @@ Foo--1234--""".replace(
|
|
||||||
b"\n", b"\r\n"
|
|
||||||
)
|
|
||||||
args, files = form_data_args()
|
|
||||||
- with ExpectLog(gen_log, "Invalid multipart/form-data"):
|
|
||||||
+ with self.assertRaises(HTTPInputError, msg="Invalid multipart/form-data"):
|
|
||||||
parse_multipart_form_data(b"1234", data, args, files)
|
|
||||||
self.assertEqual(files, {})
|
|
||||||
|
|
||||||
@@ -236,7 +237,9 @@ Foo
|
|
||||||
b"\n", b"\r\n"
|
|
||||||
)
|
|
||||||
args, files = form_data_args()
|
|
||||||
- with ExpectLog(gen_log, "multipart/form-data value missing name"):
|
|
||||||
+ with self.assertRaises(
|
|
||||||
+ HTTPInputError, msg="multipart/form-data value missing name"
|
|
||||||
+ ):
|
|
||||||
parse_multipart_form_data(b"1234", data, args, files)
|
|
||||||
self.assertEqual(files, {})
|
|
||||||
|
|
||||||
Index: tornado-6.4/tornado/web.py
|
|
||||||
===================================================================
|
|
||||||
--- tornado-6.4.orig/tornado/web.py
|
|
||||||
+++ tornado-6.4/tornado/web.py
|
|
||||||
@@ -1751,6 +1751,14 @@ class RequestHandler(object):
|
|
||||||
try:
|
|
||||||
if self.request.method not in self.SUPPORTED_METHODS:
|
|
||||||
raise HTTPError(405)
|
|
||||||
+
|
|
||||||
+ # If we're not in stream_request_body mode, this is the place where we parse the body.
|
|
||||||
+ if not _has_stream_request_body(self.__class__):
|
|
||||||
+ try:
|
|
||||||
+ self.request._parse_body()
|
|
||||||
+ except httputil.HTTPInputError as e:
|
|
||||||
+ raise HTTPError(400, "Invalid body: %s" % e) from e
|
|
||||||
+
|
|
||||||
self.path_args = [self.decode_argument(arg) for arg in args]
|
|
||||||
self.path_kwargs = dict(
|
|
||||||
(k, self.decode_argument(v, name=k)) for (k, v) in kwargs.items()
|
|
||||||
@@ -1941,7 +1949,7 @@ def _has_stream_request_body(cls: Type[R
|
|
||||||
|
|
||||||
|
|
||||||
def removeslash(
|
|
||||||
- method: Callable[..., Optional[Awaitable[None]]]
|
|
||||||
+ method: Callable[..., Optional[Awaitable[None]]],
|
|
||||||
) -> Callable[..., Optional[Awaitable[None]]]:
|
|
||||||
"""Use this decorator to remove trailing slashes from the request path.
|
|
||||||
|
|
||||||
@@ -1970,7 +1978,7 @@ def removeslash(
|
|
||||||
|
|
||||||
|
|
||||||
def addslash(
|
|
||||||
- method: Callable[..., Optional[Awaitable[None]]]
|
|
||||||
+ method: Callable[..., Optional[Awaitable[None]]],
|
|
||||||
) -> Callable[..., Optional[Awaitable[None]]]:
|
|
||||||
"""Use this decorator to add a missing trailing slash to the request path.
|
|
||||||
|
|
||||||
@@ -2394,8 +2402,9 @@ class _HandlerDelegate(httputil.HTTPMess
|
|
||||||
if self.stream_request_body:
|
|
||||||
future_set_result_unless_cancelled(self.request._body_future, None)
|
|
||||||
else:
|
|
||||||
+ # Note that the body gets parsed in RequestHandler._execute so it can be in
|
|
||||||
+ # the right exception handler scope.
|
|
||||||
self.request.body = b"".join(self.chunks)
|
|
||||||
- self.request._parse_body()
|
|
||||||
self.execute()
|
|
||||||
|
|
||||||
def on_connection_close(self) -> None:
|
|
||||||
@@ -3267,7 +3276,7 @@ class GZipContentEncoding(OutputTransfor
|
|
||||||
|
|
||||||
|
|
||||||
def authenticated(
|
|
||||||
- method: Callable[..., Optional[Awaitable[None]]]
|
|
||||||
+ method: Callable[..., Optional[Awaitable[None]]],
|
|
||||||
) -> Callable[..., Optional[Awaitable[None]]]:
|
|
||||||
"""Decorate methods with this to require that the user be logged in.
|
|
||||||
|
|
@@ -1,13 +0,0 @@
|
|||||||
Index: tornado-6.4/tornado/iostream.py
|
|
||||||
===================================================================
|
|
||||||
--- tornado-6.4.orig/tornado/iostream.py
|
|
||||||
+++ tornado-6.4/tornado/iostream.py
|
|
||||||
@@ -1374,7 +1374,7 @@ class SSLIOStream(IOStream):
|
|
||||||
return
|
|
||||||
elif err.args[0] in (ssl.SSL_ERROR_EOF, ssl.SSL_ERROR_ZERO_RETURN):
|
|
||||||
return self.close(exc_info=err)
|
|
||||||
- elif err.args[0] == ssl.SSL_ERROR_SSL:
|
|
||||||
+ elif err.args[0] in (ssl.SSL_ERROR_SSL, ssl.SSL_ERROR_SYSCALL):
|
|
||||||
try:
|
|
||||||
peer = self.socket.getpeername()
|
|
||||||
except Exception:
|
|
@@ -1,16 +1,79 @@
|
|||||||
-------------------------------------------------------------------
|
-------------------------------------------------------------------
|
||||||
Mon May 26 10:20:14 UTC 2025 - Daniel Garcia <daniel.garcia@suse.com>
|
Fri May 16 09:23:08 UTC 2025 - Daniel Garcia <daniel.garcia@suse.com>
|
||||||
|
|
||||||
- Add patch CVE-2025-47287.patch:
|
- Update to 6.5.0 (CVE-2025-47287, bsc#1243268):
|
||||||
* httputil: Raise errors instead of logging in multipart/form-data parsing
|
* Security Improvements:
|
||||||
(CVE-2025-47287, bsc#1243268, gh#tornadoweb/tornado#3497)
|
- Previously, malformed multipart-form-data requests could log
|
||||||
|
multiple warnings and constitute a denial-of-service attack. Now
|
||||||
|
an exception is raised at the first error, so there is only one
|
||||||
|
log message per request. This fixes CVE-2025-47287.
|
||||||
|
* General Changes:
|
||||||
|
- Python 3.14 is now supported. Older versions of Tornado will
|
||||||
|
work on Python 3.14 but may log deprecation warnings.
|
||||||
|
- The free-threading mode of Python 3.13 is now supported on an
|
||||||
|
experimental basis. Prebuilt wheels are not yet available for
|
||||||
|
this configuration, but it can be built from source.
|
||||||
|
- The minimum supported Python version is 3.9.
|
||||||
|
* Deprecation Notices:
|
||||||
|
- Support for obs-fold continuation lines in HTTP headers is
|
||||||
|
deprecated and will be removed in Tornado 7.0, as is the use of
|
||||||
|
carriage returns without line feeds as header separators.
|
||||||
|
- The callback argument to websocket_connect is deprecated and
|
||||||
|
will be removed in Tornado 7.0. Note that on_message_callback is
|
||||||
|
not deprecated.
|
||||||
|
- The log_message and args attributes of tornado.web.HTTPError are
|
||||||
|
deprecated. Use the new get_message method instead.
|
||||||
|
|
||||||
-------------------------------------------------------------------
|
-------------------------------------------------------------------
|
||||||
Wed Nov 27 04:23:13 UTC 2024 - Steve Kowalik <steven.kowalik@suse.com>
|
Mon Nov 25 03:19:20 UTC 2024 - Steve Kowalik <steven.kowalik@suse.com>
|
||||||
|
|
||||||
- Add patch CVE-2024-52804-avoid-quadratic-cookie-parsing.patch:
|
- Update to 6.4.2:
|
||||||
* Avoid quadratic performance of cookie parsing.
|
+ Security Improvements:
|
||||||
(CVE-2024-52804, bsc#1233668)
|
* Parsing of the cookie header is now much more efficient. The older
|
||||||
|
algorithm sometimes had quadratic performance which allowed for a
|
||||||
|
denial-of-service attack in which the server would spend excessive
|
||||||
|
CPU time parsing cookies and block the event loop.
|
||||||
|
(CVE-2024-52804, bsc#1233668)
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Wed Jul 31 09:32:23 UTC 2024 - Dominique Leuenberger <dimstar@opensuse.org>
|
||||||
|
|
||||||
|
- Update to version 6.4.1:
|
||||||
|
+ Security Improvements:
|
||||||
|
- Parsing of the ``Transfer-Encoding`` header is now stricter.
|
||||||
|
Unexpected transfer-encoding values were previously ignored
|
||||||
|
and treated as the HTTP/1.0 default of read-until-close. This
|
||||||
|
can lead to framing issues with certain proxies. We now treat
|
||||||
|
any unexpected value as an error.
|
||||||
|
- Handling of whitespace in headers now matches the RFC more
|
||||||
|
closely. Only space and tab characters are treated as
|
||||||
|
whitespace and stripped from the beginning and end of header
|
||||||
|
values. Other unicode whitespace characters are now left
|
||||||
|
alone. This could also lead to framing issues with certain
|
||||||
|
proxies.
|
||||||
|
- `tornado.curl_httpclient` now prohibits carriage return and
|
||||||
|
linefeed headers in HTTP headers (matching the behavior of
|
||||||
|
`simple_httpclient`). These characters could be used for
|
||||||
|
header injection or request smuggling if untrusted data were
|
||||||
|
used in headers.
|
||||||
|
+ General Changes:
|
||||||
|
- `tornado.iostream`: `SLIOStream` now understands changes to
|
||||||
|
error codes from OpenSSL 3.2. The main result of this change
|
||||||
|
is to reduce the noise in the logs for certain errors.
|
||||||
|
- `tornado.simple_httpclient`: `simple_httpclient` now
|
||||||
|
prohibits carriage return characters in HTTP headers. It had
|
||||||
|
previously prohibited only linefeed characters.
|
||||||
|
- `tornado.testing`: `.AsyncTestCase` subclasses can now be
|
||||||
|
instantiated without being associated with a test method.
|
||||||
|
Improves compatibility with test discovery in Pytest 8.2.
|
||||||
|
- Drop support-pytest-8.2.patch: fixed upstream.
|
||||||
|
- Drop openssl-3.2.patch: fixed upstream.
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Fri May 17 03:37:07 UTC 2024 - Steve Kowalik <steven.kowalik@suse.com>
|
||||||
|
|
||||||
|
- Add patch support-pytest-8.2.patch:
|
||||||
|
* Support pytest >= 8.2 changes.
|
||||||
|
|
||||||
-------------------------------------------------------------------
|
-------------------------------------------------------------------
|
||||||
Thu Jan 11 13:28:34 UTC 2024 - Daniel Garcia <daniel.garcia@suse.com>
|
Thu Jan 11 13:28:34 UTC 2024 - Daniel Garcia <daniel.garcia@suse.com>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# spec file for package python-tornado6
|
# spec file for package python-tornado6
|
||||||
#
|
#
|
||||||
# Copyright (c) 2024 SUSE LLC
|
# Copyright (c) 2025 SUSE LLC
|
||||||
#
|
#
|
||||||
# All modifications and additions to the file contributed by third parties
|
# All modifications and additions to the file contributed by third parties
|
||||||
# remain the property of their copyright owners, unless otherwise agreed
|
# remain the property of their copyright owners, unless otherwise agreed
|
||||||
@@ -17,9 +17,8 @@
|
|||||||
|
|
||||||
|
|
||||||
%{?sle15_python_module_pythons}
|
%{?sle15_python_module_pythons}
|
||||||
%define skip_python2 1
|
|
||||||
Name: python-tornado6
|
Name: python-tornado6
|
||||||
Version: 6.4
|
Version: 6.5
|
||||||
Release: 0
|
Release: 0
|
||||||
Summary: Open source version of scalable, non-blocking web server that power FriendFeed
|
Summary: Open source version of scalable, non-blocking web server that power FriendFeed
|
||||||
License: Apache-2.0
|
License: Apache-2.0
|
||||||
@@ -28,13 +27,6 @@ Source: https://files.pythonhosted.org/packages/source/t/tornado/tornado
|
|||||||
Source99: python-tornado6-rpmlintrc
|
Source99: python-tornado6-rpmlintrc
|
||||||
# PATCH-FIX-OPENSUSE ignore-resourcewarning-doctests.patch -- ignore resource warnings on OBS
|
# PATCH-FIX-OPENSUSE ignore-resourcewarning-doctests.patch -- ignore resource warnings on OBS
|
||||||
Patch0: ignore-resourcewarning-doctests.patch
|
Patch0: ignore-resourcewarning-doctests.patch
|
||||||
# PATCH-FIX-OPENSUSE openssl-3.2.patch gh#tornadoweb/tornado#3355
|
|
||||||
Patch1: openssl-3.2.patch
|
|
||||||
# PATCH-FIX-UPSTREAM CVE-2024-52804 bsc#1233668
|
|
||||||
# gh#tornadoweb/tornado#d5ba4a1695fbf7c6a3e54313262639b198291533
|
|
||||||
Patch2: CVE-2024-52804-avoid-quadratic-cookie-parsing.patch
|
|
||||||
# PATCH-FIX-UPSTREAM CVE-2025-47287.patch bsc#1243268
|
|
||||||
Patch3: CVE-2025-47287.patch
|
|
||||||
BuildRequires: %{python_module base >= 3.8}
|
BuildRequires: %{python_module base >= 3.8}
|
||||||
BuildRequires: %{python_module devel}
|
BuildRequires: %{python_module devel}
|
||||||
BuildRequires: %{python_module pip}
|
BuildRequires: %{python_module pip}
|
||||||
@@ -111,6 +103,6 @@ export TRAVIS=1
|
|||||||
%license LICENSE
|
%license LICENSE
|
||||||
%doc %{_docdir}/%{python_prefix}-tornado6
|
%doc %{_docdir}/%{python_prefix}-tornado6
|
||||||
%{python_sitearch}/tornado
|
%{python_sitearch}/tornado
|
||||||
%{python_sitearch}/tornado-%{version}*-info
|
%{python_sitearch}/tornado-%{version}.dist-info
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
BIN
tornado-6.4.tar.gz
(Stored with Git LFS)
BIN
tornado-6.4.tar.gz
(Stored with Git LFS)
Binary file not shown.
BIN
tornado-6.5.tar.gz
(Stored with Git LFS)
Normal file
BIN
tornado-6.5.tar.gz
(Stored with Git LFS)
Normal file
Binary file not shown.
Reference in New Issue
Block a user