diff mbox series

[meta-python,scarthgap,09/13] python3-aiohttp: fix CVE-2026-54275

Message ID 20260928174323.1810308-10-dkelaiya@cisco.com
State New
Headers show
Series python3-aiohttp: fix multiple CVEs | expand

Commit Message

From: Darsh Kelaiya <dkelaiya@cisco.com>

This patch applies the reviewed upstream fix shown in [1]. The
advisory identifying the fix is referenced in [2].

[1] https://github.com/aio-libs/aiohttp/commit/0ca2b6c28a25726527a8b60f25960262a91ed0e0
[2] https://nvd.nist.gov/vuln/detail/CVE-2026-54275

Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
---
 .../python3-aiohttp/CVE-2026-54275.patch      | 124 ++++++++++++++++++
 .../python/python3-aiohttp_3.9.5.bb           |   1 +
 2 files changed, 125 insertions(+)
 create mode 100644 meta-python/recipes-devtools/python/python3-aiohttp/CVE-2026-54275.patch
diff mbox series

Patch

diff --git a/meta-python/recipes-devtools/python/python3-aiohttp/CVE-2026-54275.patch b/meta-python/recipes-devtools/python/python3-aiohttp/CVE-2026-54275.patch
new file mode 100644
index 0000000000..21528ccb07
--- /dev/null
+++ b/meta-python/recipes-devtools/python/python3-aiohttp/CVE-2026-54275.patch
@@ -0,0 +1,124 @@ 
+From 87daca6e11c2123c04d737bde7ddef35a154b272 Mon Sep 17 00:00:00 2001
+From: "J. Nick Koston" <nick@koston.org>
+Date: Sun, 7 Jun 2026 00:30:30 -0500
+Subject: [PATCH] [PR #12835/1e94b3e8 backport][3.14] Tls server hostname pool
+ key (#12847)
+
+CVE: CVE-2026-54275
+Upstream-Status: Backport [https://github.com/aio-libs/aiohttp/commit/0ca2b6c28a25726527a8b60f25960262a91ed0e0]
+
+Backport Changes:
+- Upstream uses a NamedTuple ConnectionKey with PEP 604 union
+  annotations; aiohttp 3.9.5 uses an attr.s class, so server_hostname
+  is added with the equivalent Optional[str] annotation and included
+  in its constructor.
+- aiohttp 3.9.5 does not define the newer AiohttpServer or
+  _RequestMaker test aliases, so their fixture annotations are
+  omitted. The request-key test is synchronous because this branch's
+  make_request fixture returns ClientRequest directly.
+
+(cherry picked from commit 0ca2b6c28a25726527a8b60f25960262a91ed0e0)
+Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
+---
+ CHANGES/12835.bugfix.rst        |  1 +
+ aiohttp/client_reqrep.py        |  2 ++
+ tests/test_client_functional.py | 29 +++++++++++++++++++++++++++++
+ tests/test_client_request.py    | 14 ++++++++++++++
+ 4 files changed, 46 insertions(+)
+ create mode 100644 CHANGES/12835.bugfix.rst
+
+diff --git a/CHANGES/12835.bugfix.rst b/CHANGES/12835.bugfix.rst
+new file mode 100644
+index 000000000..84a8ae006
+--- /dev/null
++++ b/CHANGES/12835.bugfix.rst
+@@ -0,0 +1 @@
++Included the per-request ``server_hostname`` override in the :class:`~aiohttp.TCPConnector` connection pool key, so a pooled TLS connection is no longer reused for a request that sets ``server_hostname`` to a different value -- by :user:`bdraco`.
+diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py
+index afe719da1..9abfc79de 100644
+--- a/aiohttp/client_reqrep.py
++++ b/aiohttp/client_reqrep.py
+@@ -220,6 +220,7 @@ class ConnectionKey:
+     proxy: Optional[URL]
+     proxy_auth: Optional[BasicAuth]
+     proxy_headers_hash: Optional[int]  # hash(CIMultiDict)
++    server_hostname: Optional[str] = None
+ 
+ 
+ def _is_expected_content_type(
+@@ -377,6 +378,7 @@ class ClientRequest:
+             self.proxy,
+             self.proxy_auth,
+             h,
++            self.server_hostname,
+         )
+ 
+     @property
+diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py
+index 2c531d7d3..40c551ffa 100644
+--- a/tests/test_client_functional.py
++++ b/tests/test_client_functional.py
+@@ -462,6 +462,35 @@ async def test_ssl_client(
+     assert txt == "Test message"
+ 
+ 
++async def test_server_hostname_override_not_reused(aiohttp_server) -> None:
++    """A pooled TLS connection must not be reused for a different server_hostname."""
++    trustme = pytest.importorskip("trustme")
++
++    ca = trustme.CA()
++    cert = ca.issue_cert("first.example")
++    server_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
++    cert.configure_cert(server_ctx)
++    client_ctx = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH)
++    ca.configure_trust(client_ctx)
++
++    async def handler(request: web.Request) -> web.Response:
++        return web.Response(text="ok")
++
++    app = web.Application()
++    app.router.add_route("GET", "/", handler)
++    server = await aiohttp_server(app, ssl=server_ctx)
++    url = server.make_url("/")
++
++    connector = aiohttp.TCPConnector(ssl=client_ctx, limit=1, limit_per_host=1)
++    async with aiohttp.ClientSession(connector=connector) as session:
++        async with session.get(url, server_hostname="first.example") as resp:
++            assert resp.status == 200
++            await resp.read()
++
++        with pytest.raises(aiohttp.ClientConnectorCertificateError):
++            await session.get(url, server_hostname="second.example")
++
++
+ async def test_tcp_connector_fingerprint_ok(
+     aiohttp_server,
+     aiohttp_client,
+diff --git a/tests/test_client_request.py b/tests/test_client_request.py
+index 6084f6854..0ef6e92d6 100644
+--- a/tests/test_client_request.py
++++ b/tests/test_client_request.py
+@@ -1326,6 +1326,20 @@ def test_insecure_fingerprint_sha1(loop) -> None:
+         Fingerprint(hashlib.sha1(b"foo").digest())
+ 
+ 
++def test_connection_key_includes_server_hostname(make_request) -> None:
++    """A server_hostname override must be part of the connection reuse key."""
++    url = URL("https://127.0.0.1:8443/")
++    none_req = make_request("GET", url)
++    first = make_request("GET", url, server_hostname="first.example")
++    first_again = make_request("GET", url, server_hostname="first.example")
++    second = make_request("GET", url, server_hostname="second.example")
++
++    assert first.connection_key.server_hostname == "first.example"
++    assert first.connection_key != none_req.connection_key
++    assert first.connection_key != second.connection_key
++    assert first.connection_key == first_again.connection_key
++
++
+ def test_loose_cookies_types(loop) -> None:
+     req = ClientRequest("get", URL("http://python.org"), loop=loop)
+     morsel = Morsel()
+-- 
+2.35.6
+
diff --git a/meta-python/recipes-devtools/python/python3-aiohttp_3.9.5.bb b/meta-python/recipes-devtools/python/python3-aiohttp_3.9.5.bb
index 5710e38943..42402f799b 100644
--- a/meta-python/recipes-devtools/python/python3-aiohttp_3.9.5.bb
+++ b/meta-python/recipes-devtools/python/python3-aiohttp_3.9.5.bb
@@ -30,6 +30,7 @@  SRC_URI += "file://CVE-2024-52304.patch \
            file://CVE-2026-47265.patch \
            file://CVE-2026-50269.patch \
            file://CVE-2026-54274.patch \
+           file://CVE-2026-54275.patch \
            "
 
 CVE_STATUS[CVE-2026-34515] = "not-applicable-platform: Vulnerability only affects applications running on Windows"