[oe][meta-python][wrynose][PATCH 10/10] python3-aiohttp: fix CVE-2026-54280

"Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)" <[email protected]>
Newsgroups org.openembedded.lists.openembedded-devel
Message-ID <[email protected]>
From: Darsh Kelaiya <[email protected]>

This patch applies the upstream fix as referenced in [2], using
the commit shown in [1].

[1] https://github.com/aio-libs/aiohttp/commit/a762eda5242f6490d6ba667533193f8b473ad587
[2] https://github.com/advisories/GHSA-9x8q-7h8h-wcw9

Signed-off-by: Darsh Kelaiya <[email protected]>
---
 .../python3-aiohttp/CVE-2026-54280.patch      | 138 ++++++++++++++++++
 .../python/python3-aiohttp_3.13.5.bb          |   1 +
 2 files changed, 139 insertions(+)
 create mode 100644 meta-python/recipes-devtools/python/python3-aiohttp/CVE-2026-54280.patch

diff --git a/meta-python/recipes-devtools/python/python3-aiohttp/CVE-2026-54280.patch b/meta-python/recipes-devtools/python/python3-aiohttp/CVE-2026-54280.patch
new file mode 100644
index 0000000000..c598a5194c
--- /dev/null
+++ b/meta-python/recipes-devtools/python/python3-aiohttp/CVE-2026-54280.patch
@@ -0,0 +1,138 @@
+From 724ae4d652417c4490a71ea0b16b8804dad3004f Mon Sep 17 00:00:00 2001
+From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com>
+Date: Sun, 7 Jun 2026 05:47:16 +0000
+Subject: [PATCH] [PR #12831/1ac92dae backport][3.14] Payload close on
+ disconnect (#12843)
+
+CVE: CVE-2026-54280
+Upstream-Status: Backport [https://github.com/aio-libs/aiohttp/commit/a762eda5242f6490d6ba667533193f8b473ad587]
+
+Co-authored-by: J. Nick Koston <[email protected]>
+(cherry picked from commit a762eda5242f6490d6ba667533193f8b473ad587)
+Signed-off-by: Darsh Kelaiya <[email protected]>
+---
+ CHANGES/12831.bugfix.rst   |  1 +
+ aiohttp/web_response.py    |  6 ++--
+ tests/test_web_response.py | 73 +++++++++++++++++++++++++++++++++++++-
+ 3 files changed, 77 insertions(+), 3 deletions(-)
+ create mode 100644 CHANGES/12831.bugfix.rst
+
+diff --git a/CHANGES/12831.bugfix.rst b/CHANGES/12831.bugfix.rst
+new file mode 100644
+index 000000000..bf460ffcc
+--- /dev/null
++++ b/CHANGES/12831.bugfix.rst
+@@ -0,0 +1 @@
++Fixed :meth:`aiohttp.web.Response.write_eof` skipping ``Payload.close()`` when the body write was interrupted by an error or cancellation, for example when a client disconnects mid-response; the payload close hook now runs in a ``finally`` so a :class:`~aiohttp.payload.Payload` body always releases its resources -- by :user:`bdraco`.
+diff --git a/aiohttp/web_response.py b/aiohttp/web_response.py
+index 364270e4d..cea5d4b45 100644
+--- a/aiohttp/web_response.py
++++ b/aiohttp/web_response.py
+@@ -779,8 +779,10 @@ class Response(StreamResponse):
+         if body is None or self._must_be_empty_body:
+             await super().write_eof()
+         elif isinstance(self._body, Payload):
+-            await self._body.write(self._payload_writer)
+-            await self._body.close()
++            try:
++                await self._body.write(self._payload_writer)
++            finally:
++                await self._body.close()
+             await super().write_eof()
+         else:
+             await super().write_eof(cast(bytes, body))
+diff --git a/tests/test_web_response.py b/tests/test_web_response.py
+index 5a4fb7e66..f094cd3d2 100644
+--- a/tests/test_web_response.py
++++ b/tests/test_web_response.py
+@@ -1,3 +1,4 @@
++import asyncio
+ import collections.abc
+ import datetime
+ import gzip
+@@ -18,7 +19,7 @@ from aiohttp.abc import AbstractStreamWriter
+ from aiohttp.helpers import ETag
+ from aiohttp.http_writer import StreamWriter, _serialize_headers
+ from aiohttp.multipart import BodyPartReader, MultipartWriter
+-from aiohttp.payload import BytesPayload, StringPayload
++from aiohttp.payload import BytesPayload, Payload, StringPayload
+ from aiohttp.test_utils import make_mocked_request
+ from aiohttp.web import ContentCoding, Response, StreamResponse, json_response
+ 
+@@ -1370,6 +1371,76 @@ async def test_consecutive_write_eof() -> None:
+     writer.write_eof.assert_called_once_with(data)
+ 
+ 
++class _ClosingPayload(Payload):
++    """Payload test double that records whether close() ran."""
++
++    def __init__(self) -> None:
++        super().__init__(None)
++        self.close_called = False
++        self.started = asyncio.Event()
++        self.release = asyncio.Event()
++        self.fail = False
++
++    async def write(self, writer: AbstractStreamWriter) -> None:
++        self.started.set()
++        if self.fail:
++            raise ConnectionResetError("client gone")
++        await self.release.wait()
++
++    async def close(self) -> None:
++        self.close_called = True
++        await super().close()
++
++    def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str:
++        assert False
++
++
++async def test_write_eof_closes_payload_on_success() -> None:
++    writer = mock.create_autospec(AbstractStreamWriter, spec_set=True, instance=True)
++    req = make_request("GET", "/", writer=writer)
++    payload = _ClosingPayload()
++    payload.release.set()
++    resp = web.Response(body=payload)
++
++    await resp.prepare(req)
++    await resp.write_eof()
++
++    assert payload.close_called
++    assert writer.write_eof.called
++
++
++async def test_write_eof_closes_payload_on_write_error() -> None:
++    writer = mock.create_autospec(AbstractStreamWriter, spec_set=True, instance=True)
++    req = make_request("GET", "/", writer=writer)
++    payload = _ClosingPayload()
++    payload.fail = True
++    resp = web.Response(body=payload)
++
++    await resp.prepare(req)
++    with pytest.raises(ConnectionResetError):
++        await resp.write_eof()
++
++    assert payload.close_called
++    assert not writer.write_eof.called
++
++
++async def test_write_eof_closes_payload_on_cancel() -> None:
++    writer = mock.create_autospec(AbstractStreamWriter, spec_set=True, instance=True)
++    req = make_request("GET", "/", writer=writer)
++    payload = _ClosingPayload()
++    resp = web.Response(body=payload)
++
++    await resp.prepare(req)
++    task = asyncio.ensure_future(resp.write_eof())
++    await payload.started.wait()
++    task.cancel()
++    with pytest.raises(asyncio.CancelledError):
++        await task
++
++    assert payload.close_called
++    assert not writer.write_eof.called
++
++
+ def test_set_text_with_content_type() -> None:
+     resp = Response()
+     resp.content_type = "text/html"
diff --git a/meta-python/recipes-devtools/python/python3-aiohttp_3.13.5.bb b/meta-python/recipes-devtools/python/python3-aiohttp_3.13.5.bb
index 9102175f24..b9d08ac97b 100644
--- a/meta-python/recipes-devtools/python/python3-aiohttp_3.13.5.bb
+++ b/meta-python/recipes-devtools/python/python3-aiohttp_3.13.5.bb
@@ -16,6 +16,7 @@ SRC_URI += " \
     file://CVE-2026-54277.patch \
     file://CVE-2026-54278.patch \
     file://CVE-2026-54279.patch \
+    file://CVE-2026-54280.patch \
 "
 
 CVE_PRODUCT = "aiohttp"
-- 
2.35.6
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.