new file mode 100644
@@ -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"