summaryrefslogtreecommitdiffstats
path: root/meta-python/recipes-devtools/python/python3-aiohttp
diff options
context:
space:
mode:
authorGyorgy Sarvari <skandigraun@gmail.com>2026-02-04 17:29:19 +0100
committerAnuj Mittal <anuj.mittal@oss.qualcomm.com>2026-02-05 06:59:40 +0530
commit0ea6c04dde4902016de86120de99c16ea7d91df6 (patch)
tree23f2a9b26a4de1e80e67a265093c4128a423222f /meta-python/recipes-devtools/python/python3-aiohttp
parent4ac10b5dbb564b6032ebfea1ec845809acc8b91a (diff)
downloadmeta-openembedded-0ea6c04dde4902016de86120de99c16ea7d91df6.tar.gz
python3-aiohttp: patch CVE-2025-69227
Details: https://nvd.nist.gov/vuln/detail/CVE-2025-69227 Backport the patch that is referenced by teh NVD advisory. Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com> Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
Diffstat (limited to 'meta-python/recipes-devtools/python/python3-aiohttp')
-rw-r--r--meta-python/recipes-devtools/python/python3-aiohttp/CVE-2025-69227.patch148
1 files changed, 148 insertions, 0 deletions
diff --git a/meta-python/recipes-devtools/python/python3-aiohttp/CVE-2025-69227.patch b/meta-python/recipes-devtools/python/python3-aiohttp/CVE-2025-69227.patch
new file mode 100644
index 0000000000..65dae1707b
--- /dev/null
+++ b/meta-python/recipes-devtools/python/python3-aiohttp/CVE-2025-69227.patch
@@ -0,0 +1,148 @@
1From 635c8c03b609b1099d93fb8ea8c8691624237b0f Mon Sep 17 00:00:00 2001
2From: Gyorgy Sarvari <skandigraun@gmail.com>
3Date: Sat, 3 Jan 2026 04:53:29 +0000
4Subject: [PATCH] Replace asserts with exceptions (#11897) (#11914)
5
6From: Sam Bull <git@sambull.org>
7
8(cherry picked from commit d5bf65f15c0c718b6b95e9bc9d0914a92c51e60f)
9
10Co-authored-by: J. Nick Koston <nick@home-assistant.io>
11
12CVE: CVE-2025-69227
13Upstream-Status: Backport [https://github.com/aio-libs/aiohttp/commit/bc1319ec3cbff9438a758951a30907b072561259]
14Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
15---
16 aiohttp/multipart.py | 10 ++++------
17 aiohttp/web_request.py | 8 +++-----
18 tests/test_multipart.py | 12 +++++++++++-
19 tests/test_web_request.py | 24 +++++++++++++++++++++++-
20 4 files changed, 41 insertions(+), 13 deletions(-)
21
22diff --git a/aiohttp/multipart.py b/aiohttp/multipart.py
23index 54dfd48..7783ac5 100644
24--- a/aiohttp/multipart.py
25+++ b/aiohttp/multipart.py
26@@ -357,11 +357,8 @@ class BodyPartReader:
27 self._read_bytes += len(chunk)
28 if self._read_bytes == self._length:
29 self._at_eof = True
30- if self._at_eof:
31- clrf = await self._content.readline()
32- assert (
33- b"\r\n" == clrf
34- ), "reader did not read all the data or it is malformed"
35+ if self._at_eof and await self._content.readline() != b"\r\n":
36+ raise ValueError("Reader did not read all the data or it is malformed")
37 return chunk
38
39 async def _read_chunk_from_length(self, size: int) -> bytes:
40@@ -390,7 +387,8 @@ class BodyPartReader:
41 while len(chunk) < self._boundary_len:
42 chunk += await self._content.read(size)
43 self._content_eof += int(self._content.at_eof())
44- assert self._content_eof < 3, "Reading after EOF"
45+ if self._content_eof > 2:
46+ raise ValueError("Reading after EOF")
47 if self._content_eof:
48 break
49 if len(chunk) > size:
50diff --git a/aiohttp/web_request.py b/aiohttp/web_request.py
51index 6e09027..96222b0 100644
52--- a/aiohttp/web_request.py
53+++ b/aiohttp/web_request.py
54@@ -721,13 +721,13 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
55 multipart = await self.multipart()
56 max_size = self._client_max_size
57
58- field = await multipart.next()
59- while field is not None:
60+ while (field := await multipart.next()) is not None:
61 size = 0
62 field_ct = field.headers.get(hdrs.CONTENT_TYPE)
63
64 if isinstance(field, BodyPartReader):
65- assert field.name is not None
66+ if field.name is None:
67+ raise ValueError("Multipart field missing name.")
68
69 # Note that according to RFC 7578, the Content-Type header
70 # is optional, even for files, so we can't assume it's
71@@ -779,8 +779,6 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
72 raise ValueError(
73 "To decode nested multipart you need to use custom reader",
74 )
75-
76- field = await multipart.next()
77 else:
78 data = await self.read()
79 if data:
80diff --git a/tests/test_multipart.py b/tests/test_multipart.py
81index 75b73a7..5351945 100644
82--- a/tests/test_multipart.py
83+++ b/tests/test_multipart.py
84@@ -221,11 +221,21 @@ class TestPartReader:
85 with Stream(data) as stream:
86 obj = aiohttp.BodyPartReader(BOUNDARY, {}, stream)
87 result = b""
88- with pytest.raises(AssertionError):
89+ with pytest.raises(ValueError):
90 for _ in range(4):
91 result += await obj.read_chunk(7)
92 assert data == result
93
94+ async def test_read_with_content_length_malformed_crlf(self) -> None:
95+ # Content-Length is correct but data after content is not \r\n
96+ content = b"Hello"
97+ h = CIMultiDictProxy(CIMultiDict({"CONTENT-LENGTH": str(len(content))}))
98+ # Malformed: "XX" instead of "\r\n" after content
99+ with Stream(content + b"XX--:--") as stream:
100+ obj = aiohttp.BodyPartReader(BOUNDARY, h, stream)
101+ with pytest.raises(ValueError, match="malformed"):
102+ await obj.read()
103+
104 async def test_read_boundary_with_incomplete_chunk(self) -> None:
105 with Stream(b"") as stream:
106
107diff --git a/tests/test_web_request.py b/tests/test_web_request.py
108index da80ca9..125b95e 100644
109--- a/tests/test_web_request.py
110+++ b/tests/test_web_request.py
111@@ -10,6 +10,7 @@ from multidict import CIMultiDict, CIMultiDictProxy, MultiDict
112 from yarl import URL
113
114 from aiohttp import HttpVersion
115+from aiohttp.base_protocol import BaseProtocol
116 from aiohttp.http_parser import RawRequestMessage
117 from aiohttp.streams import StreamReader
118 from aiohttp.test_utils import make_mocked_request
119@@ -815,7 +816,28 @@ async def test_multipart_formdata(protocol) -> None:
120 assert dict(result) == {"a": "b", "c": "d"}
121
122
123-async def test_multipart_formdata_file(protocol) -> None:
124+async def test_multipart_formdata_field_missing_name(protocol: BaseProtocol) -> None:
125+ # Ensure ValueError is raised when Content-Disposition has no name
126+ payload = StreamReader(protocol, 2**16, loop=asyncio.get_event_loop())
127+ payload.feed_data(
128+ b"-----------------------------326931944431359\r\n"
129+ b"Content-Disposition: form-data\r\n" # Missing name!
130+ b"\r\n"
131+ b"value\r\n"
132+ b"-----------------------------326931944431359--\r\n"
133+ )
134+ content_type = (
135+ "multipart/form-data; boundary=---------------------------326931944431359"
136+ )
137+ payload.feed_eof()
138+ req = make_mocked_request(
139+ "POST", "/", headers={"CONTENT-TYPE": content_type}, payload=payload
140+ )
141+ with pytest.raises(ValueError, match="Multipart field missing name"):
142+ await req.post()
143+
144+
145+async def test_multipart_formdata_file(protocol: BaseProtocol) -> None:
146 # Make sure file uploads work, even without a content type
147 payload = StreamReader(protocol, 2**16, loop=asyncio.get_event_loop())
148 payload.feed_data(