diff mbox series

[scarthgap] python-urllib3: Backport fix CVE-2026-21441

Message ID 20260127134721.76918-1-adarsh.jagadish.kamini@est.tech
State New
Delegated to: Yoann Congal
Headers show
Series [scarthgap] python-urllib3: Backport fix CVE-2026-21441 | expand

Commit Message

adarsh.jagadish.kamini@est.tech Jan. 27, 2026, 1:47 p.m. UTC
From: Adarsh Jagadish Kamini <adarsh.jagadish.kamini@est.tech>

Signed-off-by: Adarsh Jagadish Kamini <adarsh.jagadish.kamini@est.tech>
---
 .../python3-urllib3/CVE-2026-21441.patch      | 105 ++++++++++++++++++
 .../python/python3-urllib3_2.2.2.bb           |   1 +
 2 files changed, 106 insertions(+)
 create mode 100644 meta/recipes-devtools/python/python3-urllib3/CVE-2026-21441.patch

     file://CVE-2025-50181.patch \
     file://CVE-2025-66418.patch \
     file://CVE-2025-66471.patch \
+    file://CVE-2026-21441.patch \
 "
 
 RDEPENDS:${PN} += "\

Comments

patchtest@automation.yoctoproject.org Jan. 27, 2026, 2 p.m. UTC | #1
Thank you for your submission. Patchtest identified one
or more issues with the patch. Please see the log below for
more information:

---
Testing patch /home/patchtest/share/mboxes/scarthgap-python-urllib3-Backport-fix-CVE-2026-21441.patch

FAIL: test mbox format: Series has malformed diff lines. Create the series again using git-format-patch and ensure it applies using git am (test_mbox.TestMbox.test_mbox_format)

PASS: test Signed-off-by presence (test_mbox.TestMbox.test_signed_off_by_presence)
PASS: test author valid (test_mbox.TestMbox.test_author_valid)
PASS: test commit message presence (test_mbox.TestMbox.test_commit_message_presence)
PASS: test commit message user tags (test_mbox.TestMbox.test_commit_message_user_tags)
PASS: test non-AUH upgrade (test_mbox.TestMbox.test_non_auh_upgrade)
PASS: test shortlog format (test_mbox.TestMbox.test_shortlog_format)
PASS: test shortlog length (test_mbox.TestMbox.test_shortlog_length)
PASS: test target mailing list (test_mbox.TestMbox.test_target_mailing_list)

SKIP: pretest pylint: Python-unidiff parse error (test_python_pylint.PyLint.pretest_pylint)
SKIP: test CVE tag format: Parse error Hunk diff line expected: diff --git a/meta/recipes-devtools/python/python3-urllib3_2.2.2.bb b/meta/recipes-devtools/python/python3-urllib3_2.2.2.bb
SKIP: test Signed-off-by presence: Parse error Hunk diff line expected: diff --git a/meta/recipes-devtools/python/python3-urllib3_2.2.2.bb b/meta/recipes-devtools/python/python3-urllib3_2.2.2.bb
SKIP: test Upstream-Status presence: Parse error Hunk diff line expected: diff --git a/meta/recipes-devtools/python/python3-urllib3_2.2.2.bb b/meta/recipes-devtools/python/python3-urllib3_2.2.2.bb
SKIP: test bugzilla entry format: No bug ID found (test_mbox.TestMbox.test_bugzilla_entry_format)
SKIP: test pylint: Python-unidiff parse error (test_python_pylint.PyLint.test_pylint)
SKIP: test series merge on head: Merge test is disabled for now (test_mbox.TestMbox.test_series_merge_on_head)

---

Please address the issues identified and
submit a new revision of the patch, or alternatively, reply to this
email with an explanation of why the patch should be accepted. If you
believe these results are due to an error in patchtest, please submit a
bug at https://bugzilla.yoctoproject.org/ (use the 'Patchtest' category
under 'Yocto Project Subprojects'). For more information on specific
failures, see: https://wiki.yoctoproject.org/wiki/Patchtest. Thank
you!
Yoann Congal Jan. 27, 2026, 2:06 p.m. UTC | #2
Le mar. 27 janv. 2026 à 14:47, adarsh.jagadish.kamini via
lists.openembedded.org <adarsh.jagadish.kamini=
est.tech@lists.openembedded.org> a écrit :

> From: Adarsh Jagadish Kamini <adarsh.jagadish.kamini@est.tech>
>
> Signed-off-by: Adarsh Jagadish Kamini <adarsh.jagadish.kamini@est.tech>
> ---
>  .../python3-urllib3/CVE-2026-21441.patch      | 105 ++++++++++++++++++
>  .../python/python3-urllib3_2.2.2.bb           |   1 +
>  2 files changed, 106 insertions(+)
>

Hello,

Thank you for the patch.

Since this is the second version of a patch you sent previously, it should
have "[PATCH v2]" in the subject (the -v2 option of git-send-email works
well).

Also, for the next patches, please include a justification as to why you
think this patch does fix the CVE.
For example, in this case, "Include the patch linked in the NVD report :
https://nvd.nist.gov/vuln/detail/CVE-2026-21441" is enough.

I will take this patch into testing (with a commit message modification).

Regards,

 create mode 100644
> meta/recipes-devtools/python/python3-urllib3/CVE-2026-21441.patch
>
> diff --git
> a/meta/recipes-devtools/python/python3-urllib3/CVE-2026-21441.patch
> b/meta/recipes-devtools/python/python3-urllib3/CVE-2026-21441.patch
> new file mode 100644
> index 0000000000..16af67af31
> --- /dev/null
> +++ b/meta/recipes-devtools/python/python3-urllib3/CVE-2026-21441.patch
> @@ -0,0 +1,105 @@
> +From 686d2bdd4affd3c86e605f54a72afe53c920f72f Mon Sep 17 00:00:00 2001
> +From: Illia Volochii <illia.volochii@gmail.com>
> +Date: Wed, 7 Jan 2026 18:07:30 +0200
> +Subject: [OE-core][scarthgap][PATCH] python-urllib3: Backport fix
> CVE-2026-21441
> +
> +Bugfixes
> +--------
> +
> +- Fixed a high-severity security issue where decompression-bomb
> safeguards of
> +  the streaming API were bypassed when HTTP redirects were followed.
> +  (`GHSA-38jv-5279-wg99 <
> https://github.com/urllib3/urllib3/security/advisories/GHSA-38jv-5279-wg99
> >`__)
> +
> +* Stop decoding response content during redirects needlessly
> +
> +* Rename the new query parameter
> +
> +* Add a changelog entry
> +
> +CVE: CVE-2026-21441
> +
> +Upstream-Status: Backport [
> https://github.com/urllib3/urllib3/commit/8864ac407bba8607950025e0979c4c69bc7abc7b
> ]
> +
> +Signed-off-by: Adarsh Jagadish Kamini <adarsh.jagadish.kamini@est.tech>
> +---
> + dummyserver/app.py                           |  8 +++++++-
> + src/urllib3/response.py                      |  6 +++++-
> + test/with_dummyserver/test_connectionpool.py | 19 +++++++++++++++++++
> + 3 files changed, 31 insertions(+), 2 deletions(-)
> +
> +diff --git a/dummyserver/app.py b/dummyserver/app.py
> +index 9fc9d1b7..c4978152 100644
> +--- a/dummyserver/app.py
> ++++ b/dummyserver/app.py
> +@@ -233,10 +233,16 @@ async def redirect() -> ResponseReturnValue:
> +     values = await request.values
> +     target = values.get("target", "/")
> +     status = values.get("status", "303 See Other")
> ++    compressed = values.get("compressed") == "true"
> +     status_code = status.split(" ")[0]
> +
> +     headers = [("Location", target)]
> +-    return await make_response("", status_code, headers)
> ++    if compressed:
> ++        headers.append(("Content-Encoding", "gzip"))
> ++        data = gzip.compress(b"foo")
> ++    else:
> ++        data = b""
> ++    return await make_response(data, status_code, headers)
> +
> +
> + @hypercorn_app.route("/redirect_after")
> +diff --git a/src/urllib3/response.py b/src/urllib3/response.py
> +index a0273d65..909da62b 100644
> +--- a/src/urllib3/response.py
> ++++ b/src/urllib3/response.py
> +@@ -646,7 +646,11 @@ class HTTPResponse(BaseHTTPResponse):
> +         Unread data in the HTTPResponse connection blocks the connection
> from being released back to the pool.
> +         """
> +         try:
> +-            self.read()
> ++            self.read(
> ++                # Do not spend resources decoding the content unless
> ++                # decoding has already been initiated.
> ++                decode_content=self._has_decoded_content,
> ++            )
> +         except (HTTPError, OSError, BaseSSLError, HTTPException):
> +             pass
> +
> +diff --git a/test/with_dummyserver/test_connectionpool.py
> b/test/with_dummyserver/test_connectionpool.py
> +index 4fbe6a4f..ebcdf9bf 100644
> +--- a/test/with_dummyserver/test_connectionpool.py
> ++++ b/test/with_dummyserver/test_connectionpool.py
> +@@ -480,6 +480,25 @@ class
> TestConnectionPool(HypercornDummyServerTestCase):
> +             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) -> None:
> +         with HTTPConnectionPool(self.host, self.port) as pool:
> +             response = pool.request(
> +--
> +2.44.0
> +
> diff --git a/meta/recipes-devtools/python/python3-urllib3_2.2.2.bb
> b/meta/recipes-devtools/python/python3-urllib3_2.2.2.bb
> index 620927322a..f6ac8f89ca 100644
> --- a/meta/recipes-devtools/python/python3-urllib3_2.2.2.bb
> +++ b/meta/recipes-devtools/python/python3-urllib3_2.2.2.bb
> @@ -11,6 +11,7 @@ SRC_URI += " \
>      file://CVE-2025-50181.patch \
>      file://CVE-2025-66418.patch \
>      file://CVE-2025-66471.patch \
> +    file://CVE-2026-21441.patch \
>  "
>
>  RDEPENDS:${PN} += "\
> --
> 2.44.0
>
>
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#230043):
> https://lists.openembedded.org/g/openembedded-core/message/230043
> Mute This Topic: https://lists.openembedded.org/mt/117488019/4316185
> Group Owner: openembedded-core+owner@lists.openembedded.org
> Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [
> yoann.congal@smile.fr]
> -=-=-=-=-=-=-=-=-=-=-=-
>
>
diff mbox series

Patch

diff --git a/meta/recipes-devtools/python/python3-urllib3/CVE-2026-21441.patch b/meta/recipes-devtools/python/python3-urllib3/CVE-2026-21441.patch
new file mode 100644
index 0000000000..16af67af31
--- /dev/null
+++ b/meta/recipes-devtools/python/python3-urllib3/CVE-2026-21441.patch
@@ -0,0 +1,105 @@ 
+From 686d2bdd4affd3c86e605f54a72afe53c920f72f Mon Sep 17 00:00:00 2001
+From: Illia Volochii <illia.volochii@gmail.com>
+Date: Wed, 7 Jan 2026 18:07:30 +0200
+Subject: [OE-core][scarthgap][PATCH] python-urllib3: Backport fix CVE-2026-21441 
+
+Bugfixes
+--------
+
+- Fixed a high-severity security issue where decompression-bomb safeguards of
+  the streaming API were bypassed when HTTP redirects were followed.
+  (`GHSA-38jv-5279-wg99 <https://github.com/urllib3/urllib3/security/advisories/GHSA-38jv-5279-wg99>`__)
+
+* Stop decoding response content during redirects needlessly
+
+* Rename the new query parameter
+
+* Add a changelog entry
+
+CVE: CVE-2026-21441
+
+Upstream-Status: Backport [https://github.com/urllib3/urllib3/commit/8864ac407bba8607950025e0979c4c69bc7abc7b]
+
+Signed-off-by: Adarsh Jagadish Kamini <adarsh.jagadish.kamini@est.tech>
+---
+ dummyserver/app.py                           |  8 +++++++-
+ src/urllib3/response.py                      |  6 +++++-
+ test/with_dummyserver/test_connectionpool.py | 19 +++++++++++++++++++
+ 3 files changed, 31 insertions(+), 2 deletions(-)
+
+diff --git a/dummyserver/app.py b/dummyserver/app.py
+index 9fc9d1b7..c4978152 100644
+--- a/dummyserver/app.py
++++ b/dummyserver/app.py
+@@ -233,10 +233,16 @@ async def redirect() -> ResponseReturnValue:
+     values = await request.values
+     target = values.get("target", "/")
+     status = values.get("status", "303 See Other")
++    compressed = values.get("compressed") == "true"
+     status_code = status.split(" ")[0]
+ 
+     headers = [("Location", target)]
+-    return await make_response("", status_code, headers)
++    if compressed:
++        headers.append(("Content-Encoding", "gzip"))
++        data = gzip.compress(b"foo")
++    else:
++        data = b""
++    return await make_response(data, status_code, headers)
+ 
+ 
+ @hypercorn_app.route("/redirect_after")
+diff --git a/src/urllib3/response.py b/src/urllib3/response.py
+index a0273d65..909da62b 100644
+--- a/src/urllib3/response.py
++++ b/src/urllib3/response.py
+@@ -646,7 +646,11 @@ class HTTPResponse(BaseHTTPResponse):
+         Unread data in the HTTPResponse connection blocks the connection from being released back to the pool.
+         """
+         try:
+-            self.read()
++            self.read(
++                # Do not spend resources decoding the content unless
++                # decoding has already been initiated.
++                decode_content=self._has_decoded_content,
++            )
+         except (HTTPError, OSError, BaseSSLError, HTTPException):
+             pass
+ 
+diff --git a/test/with_dummyserver/test_connectionpool.py b/test/with_dummyserver/test_connectionpool.py
+index 4fbe6a4f..ebcdf9bf 100644
+--- a/test/with_dummyserver/test_connectionpool.py
++++ b/test/with_dummyserver/test_connectionpool.py
+@@ -480,6 +480,25 @@ class TestConnectionPool(HypercornDummyServerTestCase):
+             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) -> None:
+         with HTTPConnectionPool(self.host, self.port) as pool:
+             response = pool.request(
+-- 
+2.44.0
+
diff --git a/meta/recipes-devtools/python/python3-urllib3_2.2.2.bb b/meta/recipes-devtools/python/python3-urllib3_2.2.2.bb
index 620927322a..f6ac8f89ca 100644
--- a/meta/recipes-devtools/python/python3-urllib3_2.2.2.bb
+++ b/meta/recipes-devtools/python/python3-urllib3_2.2.2.bb
@@ -11,6 +11,7 @@  SRC_URI += " \