Gyorgy Sarvari c24b8f9ced
python3-aiohttp: patch CVE-2025-69224
Details: https://nvd.nist.gov/vuln/detail/CVE-2025-69224

Backport the patch indicated by the NVD advisory.
Only a part of the tests were backported, because some of the
new tests require a compression method that is not supported
yet by this version.

Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
2026-02-05 06:59:36 +05:30

94 lines
3.9 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From 225412b13f66a76a7222d7719777e6162638faa3 Mon Sep 17 00:00:00 2001
From: Sam Bull <git@sambull.org>
Date: Sat, 3 Jan 2026 00:02:45 +0000
Subject: [PATCH] Reject non-ascii characters in some headers (#11886) (#11902)
(cherry picked from commit 5affd64f86d28a16a8f8e6fea2d217c99bf7831f)
CVE: CVE-2025-69224
Upstream-Status: Backport [https://github.com/aio-libs/aiohttp/commit/32677f2adfd907420c078dda6b79225c6f4ebce0]
Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
---
aiohttp/_http_parser.pyx | 6 +++---
aiohttp/http_parser.py | 8 ++++++--
tests/test_http_parser.py | 16 +++++++++++++++-
3 files changed, 24 insertions(+), 6 deletions(-)
diff --git a/aiohttp/_http_parser.pyx b/aiohttp/_http_parser.pyx
index 16893f0..23f1dd1 100644
--- a/aiohttp/_http_parser.pyx
+++ b/aiohttp/_http_parser.pyx
@@ -421,7 +421,8 @@ cdef class HttpParser:
headers = CIMultiDictProxy(CIMultiDict(self._headers))
if self._cparser.type == cparser.HTTP_REQUEST:
- allowed = upgrade and headers.get("upgrade", "").lower() in ALLOWED_UPGRADES
+ h_upg = headers.get("upgrade", "")
+ allowed = upgrade and h_upg.isascii() and h_upg.lower() in ALLOWED_UPGRADES
if allowed or self._cparser.method == cparser.HTTP_CONNECT:
self._upgraded = True
else:
@@ -436,8 +437,7 @@ cdef class HttpParser:
enc = self._content_encoding
if enc is not None:
self._content_encoding = None
- enc = enc.lower()
- if enc in ('gzip', 'deflate', 'br'):
+ if enc.isascii() and enc.lower() in {"gzip", "deflate", "br"}:
encoding = enc
if self._cparser.type == cparser.HTTP_REQUEST:
diff --git a/aiohttp/http_parser.py b/aiohttp/http_parser.py
index 9f864b2..bc8f7da 100644
--- a/aiohttp/http_parser.py
+++ b/aiohttp/http_parser.py
@@ -232,7 +232,9 @@ class HeadersParser:
def _is_supported_upgrade(headers: CIMultiDictProxy[str]) -> bool:
"""Check if the upgrade header is supported."""
- return headers.get(hdrs.UPGRADE, "").lower() in {"tcp", "websocket"}
+ u = headers.get(hdrs.UPGRADE, "")
+ # .lower() can transform non-ascii characters.
+ return u.isascii() and u.lower() in {"tcp", "websocket"}
class HttpParser(abc.ABC, Generic[_MsgT]):
@@ -664,7 +666,9 @@ class HttpRequestParser(HttpParser[RawRequestMessage]):
)
def _is_chunked_te(self, te: str) -> bool:
- if te.rsplit(",", maxsplit=1)[-1].strip(" \t").lower() == "chunked":
+ te = te.rsplit(",", maxsplit=1)[-1].strip(" \t")
+ # .lower() transforms some non-ascii chars, so must check first.
+ if te.isascii() and te.lower() == "chunked":
return True
# https://www.rfc-editor.org/rfc/rfc9112#section-6.3-2.4.3
raise BadHttpMessage("Request has invalid `Transfer-Encoding`")
diff --git a/tests/test_http_parser.py b/tests/test_http_parser.py
index 385452c..d4c1768 100644
--- a/tests/test_http_parser.py
+++ b/tests/test_http_parser.py
@@ -468,7 +468,21 @@ def test_request_chunked(parser) -> None:
assert isinstance(payload, streams.StreamReader)
-def test_request_te_chunked_with_content_length(parser: Any) -> None:
+def test_te_header_non_ascii(parser: HttpRequestParser) -> None:
+ # = Kelvin sign, not valid ascii.
+ text = "GET /test HTTP/1.1\r\nTransfer-Encoding: chuned\r\n\r\n"
+ with pytest.raises(http_exceptions.BadHttpMessage):
+ parser.feed_data(text.encode())
+
+
+def test_upgrade_header_non_ascii(parser: HttpRequestParser) -> None:
+ # = Kelvin sign, not valid ascii.
+ text = "GET /test HTTP/1.1\r\nUpgrade: websocet\r\n\r\n"
+ messages, upgrade, tail = parser.feed_data(text.encode())
+ assert not upgrade
+
+
+def test_request_te_chunked_with_content_length(parser: HttpRequestParser) -> None:
text = (
b"GET /test HTTP/1.1\r\n"
b"content-length: 1234\r\n"