mirror of
https://github.com/openembedded/meta-openembedded.git
synced 2026-11-07 13:44:50 +00:00
python3-aiohttp: fix CVE-2026-34520
This patch applies the reviewed upstream fix shown in [1]. The CVE record is referenced in [2]. It also backports the C-parser null-byte validation and adapts its regression test from [3]. [1] https://github.com/aio-libs/aiohttp/commit/9370b9714a7a56003cacd31a9b4ae16eab109ba4 [2] https://nvd.nist.gov/vuln/detail/CVE-2026-34520 [3] https://github.com/aio-libs/aiohttp/commit/db560cfeab32aa35f3d06f7a24238bedc01ffe2b Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com> Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
This commit is contained in:
1 parent
627d177c3e
commit
807c758926
2 files changed
+122
No files matched your search
@@ -0,0 +1,121 @@
|
||||
From 661f91935d061a3d7d554f2c808c8a14f3122991 Mon Sep 17 00:00:00 2001
|
||||
From: Rodrigo Nogueira <rodrigo.b.nogueira@gmail.com>
|
||||
Date: Wed, 11 Mar 2026 20:38:17 -0300
|
||||
Subject: [PATCH] [PR #12231/7043bc56 backport][3.13] Adjust header value
|
||||
character checks to RFC 9110 (#12235)
|
||||
|
||||
CVE: CVE-2026-34520
|
||||
Upstream-Status: Backport [https://github.com/aio-libs/aiohttp/commit/9370b9714a7a56003cacd31a9b4ae16eab109ba4]
|
||||
|
||||
Backport Changes:
|
||||
- Included the C-parser null-byte validation from upstream prerequisite
|
||||
commit db560cfeab32aa35f3d06f7a24238bedc01ffe2b because the Scarthgap
|
||||
3.9.5 recipe patch stack does not contain that prerequisite.
|
||||
- Used the target's local `raw_value` bytes object when raising
|
||||
`InvalidHeader`, since aiohttp 3.9.5 converts
|
||||
`_raw_value` before checking.
|
||||
- Added a response-parser regression test for the C-parser validation.
|
||||
- Omitted the generated aiohttp/_http_parser.c changes because the
|
||||
Scarthgap recipe regenerates this file from _http_parser.pyx with
|
||||
Cython.
|
||||
|
||||
Co-authored-by: rodrigo.nogueira <rodrigo.nogueira@prf.gov.br>
|
||||
(cherry picked from commit 9370b9714a7a56003cacd31a9b4ae16eab109ba4)
|
||||
Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
|
||||
---
|
||||
CHANGES/12231.bugfix.rst | 2 ++
|
||||
aiohttp/_http_parser.pyx | 7 +++++++
|
||||
aiohttp/http_parser.py | 9 ++++++++-
|
||||
tests/test_http_parser.py | 17 +++++++++++++++++
|
||||
4 files changed, 34 insertions(+), 1 deletion(-)
|
||||
create mode 100644 CHANGES/12231.bugfix.rst
|
||||
|
||||
diff --git a/CHANGES/12231.bugfix.rst b/CHANGES/12231.bugfix.rst
|
||||
new file mode 100644
|
||||
index 000000000..cd74bd1e7
|
||||
--- /dev/null
|
||||
+++ b/CHANGES/12231.bugfix.rst
|
||||
@@ -0,0 +1,2 @@
|
||||
+Adjusted pure-Python request header value validation to align with RFC 9110 control-character handling, while preserving lax response parser behavior, and added regression tests for Host/header control-character cases.
|
||||
+-- by :user:`rodrigobnogueira`.
|
||||
diff --git a/aiohttp/_http_parser.pyx b/aiohttp/_http_parser.pyx
|
||||
index cc8b13cfb..496498d6f 100644
|
||||
--- a/aiohttp/_http_parser.pyx
|
||||
+++ b/aiohttp/_http_parser.pyx
|
||||
@@ -387,5 +387,12 @@ cdef class HttpParser:
|
||||
value = raw_value.decode('utf-8', 'surrogateescape')
|
||||
|
||||
+ # reject null bytes in header values - matches the Python parser
|
||||
+ # check at http_parser.py. llhttp in lenient mode doesn't reject
|
||||
+ # these itself, so we need to catch them here.
|
||||
+ # ref: RFC 9110 section 5.5 (CTL chars forbidden in field values)
|
||||
+ if "\x00" in value:
|
||||
+ raise InvalidHeader(raw_value)
|
||||
+
|
||||
self._headers.add(name, value)
|
||||
if len(self._headers) > self._max_headers:
|
||||
raise BadHttpMessage("Too many headers received")
|
||||
diff --git a/aiohttp/http_parser.py b/aiohttp/http_parser.py
|
||||
index 95ac28252..a7fec7f01 100644
|
||||
--- a/aiohttp/http_parser.py
|
||||
+++ b/aiohttp/http_parser.py
|
||||
@@ -73,6 +73,10 @@ ASCIISET: Final[Set[str]] = set(string.printable)
|
||||
# token = 1*tchar
|
||||
_TCHAR_SPECIALS: Final[str] = re.escape("!#$%&'*+-.^_`|~")
|
||||
TOKENRE: Final[Pattern[str]] = re.compile(f"[0-9A-Za-z{_TCHAR_SPECIALS}]+")
|
||||
+# https://www.rfc-editor.org/rfc/rfc9110#section-5.5-5
|
||||
+_FIELD_VALUE_FORBIDDEN_CTL_RE: Final[Pattern[str]] = re.compile(
|
||||
+ r"[\x00-\x08\x0a-\x1f\x7f]"
|
||||
+)
|
||||
VERSRE: Final[Pattern[str]] = re.compile(r"HTTP/(\d)\.(\d)", re.ASCII)
|
||||
DIGITS: Final[Pattern[str]] = re.compile(r"\d+", re.ASCII)
|
||||
HEXDIGITS: Final[Pattern[bytes]] = re.compile(rb"[0-9a-fA-F]+")
|
||||
@@ -200,7 +204,10 @@ class HeadersParser:
|
||||
value = bvalue.decode("utf-8", "surrogateescape")
|
||||
|
||||
# https://www.rfc-editor.org/rfc/rfc9110.html#section-5.5-5
|
||||
- if "\n" in value or "\r" in value or "\x00" in value:
|
||||
+ if self._lax:
|
||||
+ if "\n" in value or "\r" in value or "\x00" in value:
|
||||
+ raise InvalidHeader(bvalue)
|
||||
+ elif _FIELD_VALUE_FORBIDDEN_CTL_RE.search(value):
|
||||
raise InvalidHeader(bvalue)
|
||||
|
||||
headers.add(name, value)
|
||||
diff --git a/tests/test_http_parser.py b/tests/test_http_parser.py
|
||||
index cb562f598..be7446e0a 100644
|
||||
--- a/tests/test_http_parser.py
|
||||
+++ b/tests/test_http_parser.py
|
||||
@@ -210,6 +210,9 @@ def test_bad_header_name(parser: Any, rfc9110_5_6_2_token_delim: str) -> None:
|
||||
"Foo : bar", # https://www.rfc-editor.org/rfc/rfc9112.html#section-5.1-2
|
||||
"Foo\t: bar",
|
||||
"\xffoo: bar",
|
||||
+ "Foo: abc\x01def", # CTL bytes forbidden per RFC 9110 §5.5
|
||||
+ "Foo: abc\x7fdef", # DEL is also a CTL byte
|
||||
+ "Foo: abc\x1fdef",
|
||||
),
|
||||
)
|
||||
def test_bad_headers(parser: Any, hdr: str) -> None:
|
||||
@@ -218,6 +221,20 @@ def test_bad_headers(parser: Any, hdr: str) -> None:
|
||||
parser.feed_data(text)
|
||||
|
||||
|
||||
+def test_ctl_host_header_bad_characters(parser: HttpRequestParser) -> None:
|
||||
+ """CTL byte in Host header must be rejected."""
|
||||
+ text = b"GET /test HTTP/1.1\r\nHost: trusted.example\x01@bad.test\r\n\r\n"
|
||||
+ with pytest.raises(http_exceptions.BadHttpMessage):
|
||||
+ parser.feed_data(text)
|
||||
+
|
||||
+
|
||||
+def test_null_byte_in_response_header_value(
|
||||
+ response: HttpResponseParser,
|
||||
+) -> None:
|
||||
+ with pytest.raises(http_exceptions.InvalidHeader):
|
||||
+ response.feed_data(b"HTTP/1.1 200 OK\r\nFoo: abc\x00def\r\n\r\n")
|
||||
+
|
||||
+
|
||||
def test_unpaired_surrogate_in_header_py(loop: Any, protocol: Any) -> None:
|
||||
parser = HttpRequestParserPy(
|
||||
protocol,
|
||||
--
|
||||
2.35.6
|
||||
Reference in new issue
Block a user