Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion SPECS/python-urllib3/CVE-2025-50181.patch
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ index 61715e9..ded7b38 100644
+ p = PoolManager(retries=retries)
merged = p._merge_pool_kwargs({"new_key": "value"})
- assert {"strict": True, "new_key": "value"} == merged
+ assert {"strict": retries, "new_key": "value"} == merged
+ assert {"retries": retries, "new_key": "value"} == merged

def test_merge_pool_kwargs_none(self):
"""Assert false-y values to _merge_pool_kwargs result in defaults"""
Expand Down
80 changes: 80 additions & 0 deletions SPECS/python-urllib3/CVE-2025-66418.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
From 4b4af90890a415a347e28cb21f20dbe5db243412 Mon Sep 17 00:00:00 2001
From: Illia Volochii <illia.volochii@gmail.com>
Date: Fri, 5 Dec 2025 16:41:33 +0200
Subject: [PATCH] Merge commit from fork

* Add a hard-coded limit for the decompression chain

* Reuse new list

Upstream Patch Reference: https://github.com/urllib3/urllib3/commit/24d7b67eac89f94e11003424bcf0d8f7b72222a8.patch

Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
Origin: https://github.com/urllib3/urllib3/commit/24d7b67eac89f94e11003424bcf0d8f7b72222a8.patch
Upstream-reference: https://raw.githubusercontent.com/azurelinux-security/azurelinux/22b34ffefbca93d9b67a608c9e9ea2c25ca89555/SPECS/python-urllib3/CVE-2025-66418.patch
---
changelog/GHSA-gm62-xv2j-4w53.security.rst | 4 ++++
src/urllib3/response.py | 13 ++++++++++++-
test/test_response.py | 10 ++++++++++
3 files changed, 26 insertions(+), 1 deletion(-)
create mode 100644 changelog/GHSA-gm62-xv2j-4w53.security.rst

diff --git a/changelog/GHSA-gm62-xv2j-4w53.security.rst b/changelog/GHSA-gm62-xv2j-4w53.security.rst
new file mode 100644
index 0000000..6646eaa
--- /dev/null
+++ b/changelog/GHSA-gm62-xv2j-4w53.security.rst
@@ -0,0 +1,4 @@
+Fixed a security issue where an attacker could compose an HTTP response with
+virtually unlimited links in the ``Content-Encoding`` header, potentially
+leading to a denial of service (DoS) attack by exhausting system resources
+during decoding. The number of allowed chained encodings is now limited to 5.
diff --git a/src/urllib3/response.py b/src/urllib3/response.py
index 0bd13d4..0f8adbd 100644
--- a/src/urllib3/response.py
+++ b/src/urllib3/response.py
@@ -135,8 +135,19 @@ class MultiDecoder(object):
they were applied.
"""

+
+ # Maximum allowed number of chained HTTP encodings in the
+ # Content-Encoding header.
+ max_decode_links = 5
+
def __init__(self, modes):
- self._decoders = [_get_decoder(m.strip()) for m in modes.split(",")]
+ encodings = [m.strip() for m in modes.split(",")]
+ if len(encodings) > self.max_decode_links:
+ raise DecodeError(
+ "Too many content encodings in the chain: "
+ f"{len(encodings)} > {self.max_decode_links}"
+ )
+ self._decoders = [_get_decoder(e) for e in encodings]

def flush(self):
return self._decoders[0].flush()
diff --git a/test/test_response.py b/test/test_response.py
index e09e385..4bfa8af 100644
--- a/test/test_response.py
+++ b/test/test_response.py
@@ -295,6 +295,16 @@ class TestResponse(object):

assert r.data == b"foo"

+ def test_read_multi_decoding_too_many_links(self) -> None:
+ fp = BytesIO(b"foo")
+ with pytest.raises(
+ DecodeError, match="Too many content encodings in the chain: 6 > 5"
+ ):
+ HTTPResponse(
+ fp,
+ headers={"content-encoding": "gzip, deflate, br, zstd, gzip, deflate"},
+ )
+
def test_body_blob(self):
resp = HTTPResponse(b"foo")
assert resp.data == b"foo"
--
2.45.4

86 changes: 86 additions & 0 deletions SPECS/python-urllib3/CVE-2026-21441.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
From 8864ac407bba8607950025e0979c4c69bc7abc7b Mon Sep 17 00:00:00 2001
From: Illia Volochii <illia.volochii@gmail.com>
Date: Wed, 7 Jan 2026 18:07:30 +0200
Subject: [PATCH] Merge commit from fork

* Stop decoding response content during redirects needlessly

* Rename the new query parameter

* Add a changelog entry

Upstream Patch reference: https://github.com/urllib3/urllib3/commit/8864ac407bba8607950025e0979c4c69bc7abc7b.patch
---
dummyserver/handlers.py | 9 ++++++++-
src/urllib3/response.py | 4 +++-
test/with_dummyserver/test_connectionpool.py | 19 +++++++++++++++++++
3 files changed, 30 insertions(+), 2 deletions(-)

diff --git a/dummyserver/handlers.py b/dummyserver/handlers.py
index acd181d..db38a39 100644
--- a/dummyserver/handlers.py
+++ b/dummyserver/handlers.py
@@ -190,7 +190,14 @@ class TestingApp(RequestHandler):
status = status.decode("latin-1")

headers = [("Location", target)]
- return Response(status=status, headers=headers)
+ compressed = request.params.get("compressed") == b"true"
+ if compressed:
+ headers.append(("Content-Encoding", "gzip"))
+ data = gzip.compress(b"foo")
+ else:
+ data = b""
+
+ return Response(data, status=status, headers=headers)

def not_found(self, request):
return Response("Not found", status="404 Not Found")
diff --git a/src/urllib3/response.py b/src/urllib3/response.py
index 0f8adbd..428b8ec 100644
--- a/src/urllib3/response.py
+++ b/src/urllib3/response.py
@@ -303,7 +303,9 @@ class HTTPResponse(io.IOBase):
Unread data in the HTTPResponse connection blocks the connection from being released back to the pool.
"""
try:
- self.read()
+ # Do not spend resources decoding the content unless decoding has already been initiated.
+ # In this backport we don't track that state, so we avoid decoding here.
+ self.read(decode_content=False)
except (HTTPError, SocketError, BaseSSLError, HTTPException):
pass

diff --git a/test/with_dummyserver/test_connectionpool.py b/test/with_dummyserver/test_connectionpool.py
index cde027b..c80a16d 100644
--- a/test/with_dummyserver/test_connectionpool.py
+++ b/test/with_dummyserver/test_connectionpool.py
@@ -464,6 +464,25 @@ class TestConnectionPool(HTTPDummyServerTestCase):
assert r.status == 200
assert r.data == b"Dummy server!"

+ @mock.patch("urllib3.response.GzipDecoder.decompress")
+ def test_no_decoding_with_redirect_when_preload_disabled(
+ self, gzip_decompress: mock.MagicMock
+ ) -> None:
+ """
+ Test that urllib3 does not attempt to decode a gzipped redirect
+ response when `preload_content` is set to `False`.
+ """
+ with HTTPConnectionPool(self.host, self.port) as pool:
+ # Three requests are expected: two redirects and one final / 200 OK.
+ response = pool.request(
+ "GET",
+ "/redirect",
+ fields={"target": "/redirect?compressed=true", "compressed": "true"},
+ preload_content=False,
+ )
+ assert response.status == 200
+ gzip_decompress.assert_not_called()
+
def test_303_redirect_makes_request_lose_body(self):
with HTTPConnectionPool(self.host, self.port) as pool:
response = pool.request(
--
2.43.0

28 changes: 23 additions & 5 deletions SPECS/python-urllib3/python-urllib3.spec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Summary: A powerful, sanity-friendly HTTP client for Python.
Name: python-urllib3
Version: 1.26.19
Release: 2%{?dist}
Release: 3%{?dist}
License: MIT
Vendor: Microsoft Corporation
Distribution: Mariner
Expand All @@ -22,10 +22,14 @@ BuildRequires: python3-setuptools
BuildRequires: python3-xml
%if %{with_check}
BuildRequires: python3-pip
BuildRequires: python3-pytest
BuildRequires: python3-mock
%endif
Requires: python3

Patch0: CVE-2025-50181.patch
Patch0: CVE-2025-50181.patch
Patch1: CVE-2025-66418.patch
Patch2: CVE-2026-21441.patch

%description -n python3-urllib3
urllib3 is a powerful, sanity-friendly HTTP client for Python. Much of the Python ecosystem already uses urllib3 and you should too.
Expand All @@ -43,16 +47,30 @@ rm -rf test/contrib/
%py3_install

%check
pip3 install --user --upgrade nox
PATH="$PATH:/root/.local/bin/"
nox --reuse-existing-virtualenvs --sessions test-%{python3_version}
# Install nox to handle test environment setup
pip3 install nox

# Patch dev-requirements.txt to use compatible versions with setuptools 69.x:
# - pytest 7.x+ is compatible with newer setuptools
# - flaky 3.8.0+ is compatible with pytest 7.x+
sed -i 's/pytest==4.6.9.*/pytest>=7.0.0/' dev-requirements.txt
sed -i 's/pytest==6.2.4.*/pytest>=7.0.0/' dev-requirements.txt
sed -i 's/flaky==3.7.0/flaky>=3.8.0/' dev-requirements.txt

# Run the test session for Python 3.9
# Skip test_recent_date which uses hardcoded date that is now in the past
# Note: test/with_dummyserver and test/contrib are removed in %prep
nox -s test-3.9 -- -k "not test_recent_date"

%files -n python3-urllib3
%defattr(-,root,root,-)
%license LICENSE.txt
%{python3_sitelib}/*

%changelog
* Fri Jan 09 2026 Azure Linux Security Servicing Account <azurelinux-security@microsoft.com> - 1.26.19-3
- Patch for CVE-2025-66418, CVE-2026-21441

* Thu Jun 26 2025 Durga Jagadeesh Palli <v-dpalli@microsoft.com> - 1.26.19-2
- Patch CVE-2025-50181

Expand Down
Loading