mirror of
https://github.com/openembedded/meta-openembedded.git
synced 2026-10-15 06:17:16 +00:00
python3-tornado: patch CVE-2026-82397
Details: https://nvd.nist.gov/vuln/detail/cve-2026-82397 Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com> Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
This commit is contained in:
1 parent
53a277b804
commit
7aabebd1f4
2 files changed
+150
No files matched your search
@@ -0,0 +1,149 @@
|
||||
From eba9e5652af3edbf9d8f2375194b5c63362d0c91 Mon Sep 17 00:00:00 2001
|
||||
From: Ben Darnell <ben@bendarnell.com>
|
||||
Date: Wed, 5 Aug 2026 21:20:58 -0400
|
||||
Subject: [PATCH] httputil: Enforce a new limit on the number of arguments in a
|
||||
request
|
||||
|
||||
Large POST bodies can be very expensive to parse in the worst case,
|
||||
so use the (new in Python 3.8) max_num_fields argument to limit the
|
||||
cost. A new field in ParseBodyConfig allows users to configure this
|
||||
limit. The default is 1000, which is the same as that used in php and
|
||||
node.js.
|
||||
|
||||
(cherry picked from commit 8d6363ed7b69d5f0da806efe34d256627a2191de)
|
||||
|
||||
CVE: CVE-2026-82397
|
||||
Upstream-Status: Backport [https://github.com/tornadoweb/tornado/commit/8d6363ed7b69d5f0da806efe34d256627a2191de]
|
||||
|
||||
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
|
||||
---
|
||||
tornado/escape.py | 16 ++++++++++++++--
|
||||
tornado/httputil.py | 26 +++++++++++++++++++++++++-
|
||||
tornado/test/httputil_test.py | 18 ++++++++++++++++++
|
||||
3 files changed, 57 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/tornado/escape.py b/tornado/escape.py
|
||||
index 8515bf58..a1c16b36 100644
|
||||
--- a/tornado/escape.py
|
||||
+++ b/tornado/escape.py
|
||||
@@ -171,7 +171,11 @@ def url_unescape(
|
||||
|
||||
|
||||
def parse_qs_bytes(
|
||||
- qs: Union[str, bytes], keep_blank_values: bool = False, strict_parsing: bool = False
|
||||
+ qs: Union[str, bytes],
|
||||
+ keep_blank_values: bool = False,
|
||||
+ strict_parsing: bool = False,
|
||||
+ *,
|
||||
+ max_num_fields: Optional[int] = None,
|
||||
) -> Dict[str, List[bytes]]:
|
||||
"""Parses a query string like urlparse.parse_qs,
|
||||
but takes bytes and returns the values as byte strings.
|
||||
@@ -179,13 +183,21 @@ def parse_qs_bytes(
|
||||
Keys still become type str (interpreted as latin1 in python3!)
|
||||
because it's too painful to keep them as byte strings in
|
||||
python3 and in practice they're nearly always ascii anyway.
|
||||
+
|
||||
+ .. versionadded:: 6.5.8
|
||||
+ The ``max_num_fields`` argument. ValueError is raised if this limit is exceeded.
|
||||
"""
|
||||
# This is gross, but python3 doesn't give us another way.
|
||||
# Latin1 is the universal donor of character encodings.
|
||||
if isinstance(qs, bytes):
|
||||
qs = qs.decode("latin1")
|
||||
result = urllib.parse.parse_qs(
|
||||
- qs, keep_blank_values, strict_parsing, encoding="latin1", errors="strict"
|
||||
+ qs,
|
||||
+ keep_blank_values,
|
||||
+ strict_parsing,
|
||||
+ encoding="latin1",
|
||||
+ errors="strict",
|
||||
+ max_num_fields=max_num_fields,
|
||||
)
|
||||
encoded = {}
|
||||
for k, v in result.items():
|
||||
diff --git a/tornado/httputil.py b/tornado/httputil.py
|
||||
index f698db21..b80c796c 100644
|
||||
--- a/tornado/httputil.py
|
||||
+++ b/tornado/httputil.py
|
||||
@@ -947,6 +947,23 @@ class ParseMultipartConfig:
|
||||
"""
|
||||
|
||||
|
||||
+@dataclasses.dataclass
|
||||
+class ParseUrlEncodedConfig:
|
||||
+ """This class configures the parsing of ``application/x-www-form-urlencoded`` request bodies.
|
||||
+
|
||||
+ Its primary purpose is to place limits on the size and complexity of request messages
|
||||
+ to avoid potential denial-of-service attacks.
|
||||
+
|
||||
+ .. versionadded:: 6.5.8
|
||||
+ """
|
||||
+
|
||||
+ max_arguments: int = 1000
|
||||
+ """The maximum number of arguments accepted in a urlencoded request.
|
||||
+
|
||||
+ Each ``<input>`` element in an HTML form corresponds to at least one argument.
|
||||
+ """
|
||||
+
|
||||
+
|
||||
@dataclasses.dataclass
|
||||
class ParseBodyConfig:
|
||||
"""This class configures the parsing of request bodies.
|
||||
@@ -957,6 +974,9 @@ class ParseBodyConfig:
|
||||
multipart: ParseMultipartConfig = dataclasses.field(
|
||||
default_factory=ParseMultipartConfig
|
||||
)
|
||||
+ urlencoded: ParseUrlEncodedConfig = dataclasses.field(
|
||||
+ default_factory=ParseUrlEncodedConfig
|
||||
+ )
|
||||
"""Configuration for ``multipart/form-data`` request bodies."""
|
||||
|
||||
|
||||
@@ -1015,7 +1035,11 @@ def parse_body_arguments(
|
||||
)
|
||||
try:
|
||||
# real charset decoding will happen in RequestHandler.decode_argument()
|
||||
- uri_arguments = parse_qs_bytes(body, keep_blank_values=True)
|
||||
+ uri_arguments = parse_qs_bytes(
|
||||
+ body,
|
||||
+ keep_blank_values=True,
|
||||
+ max_num_fields=config.urlencoded.max_arguments,
|
||||
+ )
|
||||
except Exception as e:
|
||||
raise HTTPInputError("Invalid x-www-form-urlencoded body: %s" % e) from e
|
||||
for name, values in uri_arguments.items():
|
||||
diff --git a/tornado/test/httputil_test.py b/tornado/test/httputil_test.py
|
||||
index 92683ae9..afb15879 100644
|
||||
--- a/tornado/test/httputil_test.py
|
||||
+++ b/tornado/test/httputil_test.py
|
||||
@@ -1,4 +1,5 @@
|
||||
from tornado.httputil import (
|
||||
+ parse_body_arguments,
|
||||
url_concat,
|
||||
parse_multipart_form_data,
|
||||
HTTPHeaders,
|
||||
@@ -95,6 +96,23 @@ class QsParseTest(unittest.TestCase):
|
||||
self.assertIn(("b", "2"), qsl)
|
||||
|
||||
|
||||
+class UrlEncodedDataTest(unittest.TestCase):
|
||||
+ def test_urlencoded_data(self):
|
||||
+ data = b"a=1&b=2&a=3"
|
||||
+ args, files = form_data_args()
|
||||
+ parse_body_arguments("application/x-www-form-urlencoded", data, args, files)
|
||||
+ self.assertEqual(args["a"], [b"1", b"3"])
|
||||
+ self.assertEqual(args["b"], [b"2"])
|
||||
+ self.assertEqual(files, {})
|
||||
+
|
||||
+ def test_max_arguments(self):
|
||||
+ data = b"".join(b"a=1&" for _ in range(1001))
|
||||
+ args, files = form_data_args()
|
||||
+ with self.assertRaises(HTTPInputError) as cm:
|
||||
+ parse_body_arguments("application/x-www-form-urlencoded", data, args, files)
|
||||
+ self.assertIn("Max number of fields exceeded", str(cm.exception))
|
||||
+
|
||||
+
|
||||
class MultipartFormDataTest(unittest.TestCase):
|
||||
def test_file_upload(self):
|
||||
data = b"""\
|
||||
@@ -9,6 +9,7 @@ LIC_FILES_CHKSUM = "file://LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57"
|
||||
SRC_URI[sha256sum] = "66c513a76cda70d53907bc27cf1447557699c2e95aa48ba27a442ff61c3ddfc2"
|
||||
|
||||
SRC_URI += "file://CVE-2026-91990.patch \
|
||||
file://CVE-2026-82397.patch \
|
||||
"
|
||||
|
||||
inherit pypi python_setuptools_build_meta
|
||||
|
||||
Reference in new issue
Block a user