mirror of
https://github.com/openembedded/meta-openembedded.git
synced 2026-11-13 15:47:23 +00:00
python3-aiohttp: fix CVE-2026-59881
This patch applies the upstream fix as referenced in [2], using the commit shown in [1]. The 3.13.5 backport passes the negotiated compression setting to the client WebSocket reader. [1] https://github.com/aio-libs/aiohttp/commit/47fb6ae354d4fa22048f4dbe7dbf82b625f0a2f6 [2] https://github.com/aio-libs/aiohttp/security/advisories/GHSA-mq44-7p77-q5h7 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
d193d78617
commit
70575863c3
2 files changed
+104
No files matched your search
@@ -0,0 +1,103 @@
|
||||
From b052f95658821f726388cb86cfefad1cec264842 Mon Sep 17 00:00:00 2001
|
||||
From: Sam Bull <git@sambull.org>
|
||||
Date: Thu, 1 Oct 2026 02:37:30 -0700
|
||||
Subject: [PATCH] Fix decompressing frames without negotiation (#12976)
|
||||
(#12978)
|
||||
|
||||
CVE: CVE-2026-59881
|
||||
Upstream-Status: Backport [https://github.com/aio-libs/aiohttp/commit/47fb6ae354d4fa22048f4dbe7dbf82b625f0a2f6]
|
||||
|
||||
Backport Changes:
|
||||
- Passed the negotiated compression flag to the 3.13.5 client reader.
|
||||
- Kept the reader's compress=True default: both production call
|
||||
sites now pass the negotiated setting (the server already did).
|
||||
- Adapted the upstream client regression test to the 3.13.5 API.
|
||||
- Omitted changes to newer WebSocket APIs and unrelated tests.
|
||||
|
||||
(cherry picked from commit 47fb6ae354d4fa22048f4dbe7dbf82b625f0a2f6)
|
||||
Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
|
||||
---
|
||||
aiohttp/client.py | 4 ++-
|
||||
tests/test_client_ws_functional.py | 40 +++++++++++++++++++++++++++++-
|
||||
2 files changed, 42 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/aiohttp/client.py b/aiohttp/client.py
|
||||
index 0637eff6b..8cd9add5f 100644
|
||||
--- a/aiohttp/client.py
|
||||
+++ b/aiohttp/client.py
|
||||
@@ -1190,7 +1190,9 @@ class ClientSession:
|
||||
transport = conn.transport
|
||||
assert transport is not None
|
||||
reader = WebSocketDataQueue(conn_proto, 2**16, loop=self._loop)
|
||||
- conn_proto.set_parser(WebSocketReader(reader, max_msg_size), reader)
|
||||
+ conn_proto.set_parser(
|
||||
+ WebSocketReader(reader, max_msg_size, compress=bool(compress)), reader
|
||||
+ )
|
||||
writer = WebSocketWriter(
|
||||
conn_proto,
|
||||
transport,
|
||||
diff --git a/tests/test_client_ws_functional.py b/tests/test_client_ws_functional.py
|
||||
index 7b6bd0322..a67769c1c 100644
|
||||
--- a/tests/test_client_ws_functional.py
|
||||
+++ b/tests/test_client_ws_functional.py
|
||||
@@ -1,5 +1,7 @@
|
||||
import asyncio
|
||||
+import struct
|
||||
import sys
|
||||
+import zlib
|
||||
from typing import Any, List, NoReturn, Optional
|
||||
from unittest import mock
|
||||
|
||||
@@ -15,8 +17,9 @@ from aiohttp import (
|
||||
web,
|
||||
)
|
||||
from aiohttp._websocket.reader import WebSocketDataQueue
|
||||
+from aiohttp._websocket.models import WS_DEFLATE_TRAILING
|
||||
from aiohttp.client_ws import ClientWSTimeout
|
||||
-from aiohttp.http import WSCloseCode
|
||||
+from aiohttp.http import WebSocketError, WSCloseCode
|
||||
from aiohttp.pytest_plugin import AiohttpClient
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
@@ -1276,3 +1279,38 @@ async def test_websocket_connection_cancellation(
|
||||
# Cleanup properly
|
||||
websocket._response = mock.Mock()
|
||||
await websocket.close()
|
||||
+
|
||||
+
|
||||
+async def test_client_rejects_compressed_frame_without_negotiation(
|
||||
+ aiohttp_client: AiohttpClient,
|
||||
+) -> None:
|
||||
+ """Reject RSV1 frames when permessage-deflate was not negotiated."""
|
||||
+ payload = b"this frame should never be decompressed by the client"
|
||||
+
|
||||
+ async def handler(request: web.Request) -> web.WebSocketResponse:
|
||||
+ ws = web.WebSocketResponse()
|
||||
+ await ws.prepare(request)
|
||||
+ transport = request.transport
|
||||
+ assert transport is not None
|
||||
+ compressor = zlib.compressobj(
|
||||
+ zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -zlib.MAX_WBITS
|
||||
+ )
|
||||
+ compressed = compressor.compress(payload)
|
||||
+ compressed += compressor.flush(zlib.Z_SYNC_FLUSH)
|
||||
+ compressed = compressed.removesuffix(WS_DEFLATE_TRAILING)
|
||||
+ frame = struct.pack("!BB", 0x80 | 0x40 | WSMsgType.TEXT.value,
|
||||
+ len(compressed)) + compressed
|
||||
+ transport.write(frame)
|
||||
+ await ws.receive()
|
||||
+ return ws
|
||||
+
|
||||
+ app = web.Application()
|
||||
+ app.router.add_get("/", handler)
|
||||
+ client = await aiohttp_client(app)
|
||||
+ async with client.ws_connect("/") as ws:
|
||||
+ assert ws.compress == 0
|
||||
+ msg = await ws.receive()
|
||||
+
|
||||
+ assert msg.type is WSMsgType.ERROR, msg
|
||||
+ assert isinstance(msg.data, WebSocketError)
|
||||
+ assert msg.data.code == WSCloseCode.PROTOCOL_ERROR
|
||||
--
|
||||
2.35.6
|
||||
|
||||
@@ -19,6 +19,7 @@ SRC_URI += " \
|
||||
file://CVE-2026-54280.patch \
|
||||
file://CVE-2026-54273_p1.patch \
|
||||
file://CVE-2026-54273_p2.patch \
|
||||
file://CVE-2026-59881.patch \
|
||||
"
|
||||
|
||||
CVE_PRODUCT = "aiohttp:aiohttp"
|
||||
|
||||
Reference in new issue
Block a user