-
Notifications
You must be signed in to change notification settings - Fork 619
[Medium] Patch python-virtualenv for CVE-2025-50181, CVE-2026-24049, CVE-2026-1703 #15951
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
56edd7f
5d3b47b
716896d
0b57e0c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| From f05b1329126d5be6de501f9d1e3e36738bc08857 Mon Sep 17 00:00:00 2001 | ||
| From: Illia Volochii <illia.volochii@gmail.com> | ||
| Date: Wed, 18 Jun 2025 16:25:01 +0300 | ||
| Subject: [PATCH] Merge commit from fork | ||
|
|
||
| * Apply Quentin's suggestion | ||
|
|
||
| Co-authored-by: Quentin Pradet <quentin.pradet@gmail.com> | ||
|
|
||
| * Add tests for disabled redirects in the pool manager | ||
|
|
||
| * Add a possible fix for the issue with not raised `MaxRetryError` | ||
|
|
||
| * Make urllib3 handle redirects instead of JS when JSPI is used | ||
|
|
||
| * Fix info in the new comment | ||
|
|
||
| * State that redirects with XHR are not controlled by urllib3 | ||
|
|
||
| * Remove excessive params from new test requests | ||
|
|
||
| * Add tests reaching max non-0 redirects | ||
|
|
||
| * Test redirects with Emscripten | ||
|
|
||
| * Fix `test_merge_pool_kwargs` | ||
|
|
||
| * Add a changelog entry | ||
|
|
||
| * Parametrize tests | ||
|
|
||
| * Drop a fix for Emscripten | ||
|
|
||
| * Apply Seth's suggestion to docs | ||
|
|
||
| Co-authored-by: Seth Michael Larson <sethmichaellarson@gmail.com> | ||
|
|
||
| * Use a minor release instead of the patch one | ||
|
|
||
| --------- | ||
|
|
||
| Co-authored-by: Quentin Pradet <quentin.pradet@gmail.com> | ||
| Co-authored-by: Seth Michael Larson <sethmichaellarson@gmail.com> | ||
| Upstream Patch Reference: https://github.com/urllib3/urllib3/commit/f05b1329126d5be6de501f9d1e3e36738bc08857.patch | ||
| --- | ||
| pip/_vendor/urllib3/poolmanager.py | 18 +++++++++++++++++- | ||
| 1 file changed, 17 insertions(+), 1 deletion(-) | ||
|
|
||
| diff --git a/pip/_vendor/urllib3/poolmanager.py b/pip/_vendor/urllib3/poolmanager.py | ||
| index fb51bf7..a8de7c6 100644 | ||
| --- a/pip/_vendor/urllib3/poolmanager.py | ||
| +++ b/pip/_vendor/urllib3/poolmanager.py | ||
| @@ -170,6 +170,22 @@ class PoolManager(RequestMethods): | ||
|
|
||
| def __init__(self, num_pools=10, headers=None, **connection_pool_kw): | ||
| RequestMethods.__init__(self, headers) | ||
| + if "retries" in connection_pool_kw: | ||
| + retries = connection_pool_kw["retries"] | ||
| + if not isinstance(retries, Retry): | ||
| + # When Retry is initialized, raise_on_redirect is based | ||
| + # on a redirect boolean value. | ||
| + # But requests made via a pool manager always set | ||
| + # redirect to False, and raise_on_redirect always ends | ||
| + # up being False consequently. | ||
| + # Here we fix the issue by setting raise_on_redirect to | ||
| + # a value needed by the pool manager without considering | ||
| + # the redirect boolean. | ||
| + raise_on_redirect = retries is not False | ||
| + retries = Retry.from_int(retries, redirect=False) | ||
| + retries.raise_on_redirect = raise_on_redirect | ||
| + connection_pool_kw = connection_pool_kw.copy() | ||
| + connection_pool_kw["retries"] = retries | ||
| self.connection_pool_kw = connection_pool_kw | ||
| self.pools = RecentlyUsedContainer(num_pools) | ||
|
|
||
| @@ -389,7 +405,7 @@ class PoolManager(RequestMethods): | ||
| kw["body"] = None | ||
| kw["headers"] = HTTPHeaderDict(kw["headers"])._prepare_for_method_change() | ||
|
|
||
| - retries = kw.get("retries") | ||
| + retries = kw.get("retries", response.retries) | ||
| if not isinstance(retries, Retry): | ||
| retries = Retry.from_int(retries, redirect=redirect) | ||
|
|
||
| -- | ||
| 2.45.4 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| From 4c651b70d60ed91b13663bcda9b3ed41748d0124 Mon Sep 17 00:00:00 2001 | ||
| From: Seth Michael Larson <seth@python.org> | ||
| Date: Fri, 30 Jan 2026 09:49:11 -0600 | ||
| Subject: [PATCH] Use os.path.commonpath() instead of commonprefix() | ||
|
|
||
| Upstream Patch Reference: https://github.com/pypa/pip/commit/8e227a9be4faa9594e05d02ca05a413a2a4e7735.patch | ||
| --- | ||
| news/+1ee322a1.bugfix.rst | 1 + | ||
| pip/_internal/utils/unpacking.py | 2 +- | ||
| 2 files changed, 2 insertions(+), 1 deletion(-) | ||
| create mode 100644 news/+1ee322a1.bugfix.rst | ||
|
|
||
| diff --git a/news/+1ee322a1.bugfix.rst b/news/+1ee322a1.bugfix.rst | ||
| new file mode 100644 | ||
| index 0000000..edb1b32 | ||
| --- /dev/null | ||
| +++ b/news/+1ee322a1.bugfix.rst | ||
| @@ -0,0 +1 @@ | ||
| +Use a path-segment prefix comparison, not char-by-char. | ||
| diff --git a/pip/_internal/utils/unpacking.py b/pip/_internal/utils/unpacking.py | ||
| index 87a6d19..b5c736d 100644 | ||
| --- a/pip/_internal/utils/unpacking.py | ||
| +++ b/pip/_internal/utils/unpacking.py | ||
| @@ -82,7 +82,7 @@ def is_within_directory(directory: str, target: str) -> bool: | ||
| abs_directory = os.path.abspath(directory) | ||
| abs_target = os.path.abspath(target) | ||
|
|
||
| - prefix = os.path.commonprefix([abs_directory, abs_target]) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Patch looks good w.r.t upstream |
||
| + prefix = os.path.commonpath([abs_directory, abs_target]) | ||
| return prefix == abs_directory | ||
|
|
||
|
|
||
| -- | ||
| 2.45.4 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| From 4c651b70d60ed91b13663bcda9b3ed41748d0124 Mon Sep 17 00:00:00 2001 | ||
| From: Seth Michael Larson <seth@python.org> | ||
| Date: Fri, 30 Jan 2026 09:49:11 -0600 | ||
| Subject: [PATCH] Use os.path.commonpath() instead of commonprefix() | ||
|
|
||
| Upstream Patch Reference: https://github.com/pypa/pip/commit/8e227a9be4faa9594e05d02ca05a413a2a4e7735.patch | ||
|
|
||
| --- | ||
| pip/_internal/utils/unpacking.py | 2 +- | ||
| 1 file changed, 1 insertion(+), 1 deletion(-) | ||
|
|
||
| diff --git a/pip/_internal/utils/unpacking.py b/pip/_internal/utils/unpacking.py | ||
| index bc950ac..b3f52e8 100644 | ||
| --- a/pip/_internal/utils/unpacking.py | ||
| +++ b/pip/_internal/utils/unpacking.py | ||
| @@ -83,7 +83,7 @@ def is_within_directory(directory: str, target: str) -> bool: | ||
| abs_directory = os.path.abspath(directory) | ||
| abs_target = os.path.abspath(target) | ||
|
|
||
| - prefix = os.path.commonprefix([abs_directory, abs_target]) | ||
| + prefix = os.path.commonpath([abs_directory, abs_target]) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Patch looks good w.r.t upstream |
||
| return prefix == abs_directory | ||
|
|
||
|
|
||
| -- | ||
| 2.45.4 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| From 7a7d2de96b22a9adf9208afcc9547e1001569fef Mon Sep 17 00:00:00 2001 | ||
| From: =?UTF-8?q?Alex=20Gr=C3=B6nholm?= <alex.gronholm@nextday.fi> | ||
| Date: Thu, 22 Jan 2026 01:41:14 +0200 | ||
| Subject: [PATCH] Fixed security issue around wheel unpack (#675) | ||
|
|
||
| A maliciously crafted wheel could cause the permissions of a file outside the unpack tree to be altered. | ||
|
|
||
| Fixes CVE-2026-24049. | ||
| Upstream Patch Reference: https://github.com/pypa/wheel/commit/7a7d2de96b22a9adf9208afcc9547e1001569fef.patch | ||
| --- | ||
| setuptools/_vendor/wheel/cli/unpack.py | 4 ++-- | ||
| 1 file changed, 2 insertions(+), 2 deletions(-) | ||
|
|
||
| diff --git a/setuptools/_vendor/wheel/cli/unpack.py b/setuptools/_vendor/wheel/cli/unpack.py | ||
| index d48840e..83dc742 100644 | ||
| --- a/setuptools/_vendor/wheel/cli/unpack.py | ||
| +++ b/setuptools/_vendor/wheel/cli/unpack.py | ||
| @@ -19,12 +19,12 @@ def unpack(path: str, dest: str = ".") -> None: | ||
| destination = Path(dest) / namever | ||
| print(f"Unpacking to: {destination}...", end="", flush=True) | ||
| for zinfo in wf.filelist: | ||
| - wf.extract(zinfo, destination) | ||
| + target_path = Path(wf.extract(zinfo, destination)) | ||
|
|
||
| # Set permissions to the same values as they were set in the archive | ||
| # We have to do this manually due to | ||
| # https://github.com/python/cpython/issues/59999 | ||
| permissions = zinfo.external_attr >> 16 & 0o777 | ||
| - destination.joinpath(zinfo.filename).chmod(permissions) | ||
| + target_path.chmod(permissions) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Patch looks good w.r.t upstream |
||
|
|
||
| print("OK") | ||
| -- | ||
| 2.45.4 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| From 7a7d2de96b22a9adf9208afcc9547e1001569fef Mon Sep 17 00:00:00 2001 | ||
| From: =?UTF-8?q?Alex=20Gr=C3=B6nholm?= <alex.gronholm@nextday.fi> | ||
| Date: Thu, 22 Jan 2026 01:41:14 +0200 | ||
| Subject: [PATCH] Fixed security issue around wheel unpack (#675) | ||
|
|
||
| A maliciously crafted wheel could cause the permissions of a file outside the unpack tree to be altered. | ||
|
|
||
| Fixes CVE-2026-24049. | ||
| Upstream Patch Reference: https://github.com/pypa/wheel/commit/7a7d2de96b22a9adf9208afcc9547e1001569fef.patch | ||
| --- | ||
| wheel/cli/unpack.py | 4 ++-- | ||
| 1 file changed, 2 insertions(+), 2 deletions(-) | ||
|
|
||
| diff --git a/wheel/cli/unpack.py b/wheel/cli/unpack.py | ||
| index d48840e..83dc742 100644 | ||
| --- a/wheel/cli/unpack.py | ||
| +++ b/wheel/cli/unpack.py | ||
| @@ -19,12 +19,12 @@ def unpack(path: str, dest: str = ".") -> None: | ||
| destination = Path(dest) / namever | ||
| print(f"Unpacking to: {destination}...", end="", flush=True) | ||
| for zinfo in wf.filelist: | ||
| - wf.extract(zinfo, destination) | ||
| + target_path = Path(wf.extract(zinfo, destination)) | ||
|
|
||
| # Set permissions to the same values as they were set in the archive | ||
| # We have to do this manually due to | ||
| # https://github.com/python/cpython/issues/59999 | ||
| permissions = zinfo.external_attr >> 16 & 0o777 | ||
| - destination.joinpath(zinfo.filename).chmod(permissions) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Patch looks good w.r.t upstream |
||
| + target_path.chmod(permissions) | ||
|
|
||
| print("OK") | ||
| -- | ||
| 2.45.4 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,14 +1,19 @@ | ||
| Summary: Virtual Python Environment builder | ||
| Name: python-virtualenv | ||
| Version: 20.36.1 | ||
| Release: 1%{?dist} | ||
| Release: 2%{?dist} | ||
| License: MIT | ||
| Vendor: Microsoft Corporation | ||
| Distribution: Azure Linux | ||
| Group: Development/Languages/Python | ||
| URL: https://pypi.python.org/pypi/virtualenv | ||
| Source0: https://files.pythonhosted.org/packages/aa/a3/4d310fa5f00863544e1d0f4de93bddec248499ccf97d4791bc3122c9d4f3/virtualenv-20.36.1.tar.gz | ||
| Patch0: 0001-replace-to-flit.patch | ||
| Patch1000: CVE-2025-50181.patch | ||
| Patch1001: CVE-2026-1703v0.patch | ||
| Patch1002: CVE-2026-1703v1.patch | ||
| Patch1003: CVE-2026-24049v0.patch | ||
| Patch1004: CVE-2026-24049v1.patch | ||
| BuildArch: noarch | ||
|
|
||
| %description | ||
|
|
@@ -20,6 +25,7 @@ BuildRequires: python3-devel | |
| BuildRequires: python3-setuptools_scm | ||
| BuildRequires: python3-xml | ||
| BuildRequires: python3-wheel | ||
| BuildRequires: zip | ||
|
|
||
| %if 0%{?with_check} | ||
| BuildRequires: python3-pip | ||
|
|
@@ -37,7 +43,77 @@ Provides: %{name}-doc = %{version}-%{release} | |
| virtualenv is a tool to create isolated Python environment. | ||
|
|
||
| %prep | ||
| %autosetup -p1 -n virtualenv-%{version} | ||
| # Adding -N to enable manual patching, needed for CVE-2025-50181 | ||
| %autosetup -p1 -n virtualenv-%{version} -N | ||
| %patch -P 0 -p1 | ||
|
|
||
| # Manual patching for CVE-2025-50181 and CVE-2026-1703v0 | ||
| # For CVE-2025-50181, poolmanager.py file is located in 2 different places and each is of different version so the same patch cannot be applied to all of them. | ||
| # For CVE-2026-1703, unpacking.py file is located in 2 different places and each is of different version so the same patch cannot be applied to all of them. | ||
| # Affected files are under src and archived inside a .whl file, so we need to unpack it, apply the patch, and then re-zip it. | ||
|
|
||
| echo "Manually Patching virtualenv-20.36.1/src/virtualenv/seed/wheels/embed/pip-25.0.1-py3-none-any.whl/pip/_vendor/urllib3/poolmanager.py" | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we please add a note above each of this to clarify what patch. Also a comment so that we can justify why we are duplicating code, as the two parts alsmost are similar.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Affected code is present in archive(.whl) files in source tarball and even though patch is same for diffrent archives, same patch is not applicable for affected archives due to different versions. So same procedure is repeated for different archives. |
||
| mkdir -p unpacked_pip-25.0.1-py3-none-any | ||
| unzip src/virtualenv/seed/wheels/embed/pip-25.0.1-py3-none-any.whl -d unpacked_pip-25.0.1-py3-none-any | ||
| patch -p1 -d unpacked_pip-25.0.1-py3-none-any < %{PATCH1000} | ||
| echo "Manually Patching virtualenv-20.36.1/src/virtualenv/seed/wheels/embed/pip-25.0.1-py3-none-any.whl/pip/_internal/utils/unpacking.py" | ||
| patch -p1 -d unpacked_pip-25.0.1-py3-none-any < %{PATCH1001} | ||
| # Remove the original file | ||
| rm -f src/virtualenv/seed/wheels/embed/pip-25.0.1-py3-none-any.whl | ||
| # After patching, re-zip the contents back into a .whl | ||
| pushd unpacked_pip-25.0.1-py3-none-any | ||
| zip -r ../src/virtualenv/seed/wheels/embed/pip-25.0.1-py3-none-any.whl * | ||
| popd | ||
| rm -rf unpacked_pip-25.0.1-py3-none-any | ||
|
|
||
| # Manual patching for CVE-2025-50181 and CVE-2026-1703v1 | ||
| echo "Manually Patching virtualenv-20.36.1/src/virtualenv/seed/wheels/embed/pip-25.3-py3-none-any.whl/pip/_vendor/urllib3/poolmanager.py" | ||
| mkdir -p unpacked_pip-25.3-py3-none-any | ||
| unzip src/virtualenv/seed/wheels/embed/pip-25.3-py3-none-any.whl -d unpacked_pip-25.3-py3-none-any | ||
| patch -p1 -d unpacked_pip-25.3-py3-none-any < %{PATCH1000} | ||
| echo "Manually Patching virtualenv-20.36.1/src/virtualenv/seed/wheels/embed/pip-25.3-py3-none-any.whl/pip/_internal/utils/unpacking.py" | ||
| patch -p1 -d unpacked_pip-25.3-py3-none-any < %{PATCH1002} | ||
| rm -f src/virtualenv/seed/wheels/embed/pip-25.3-py3-none-any.whl | ||
| pushd unpacked_pip-25.3-py3-none-any | ||
| zip -r ../src/virtualenv/seed/wheels/embed/pip-25.3-py3-none-any.whl * | ||
| popd | ||
| rm -rf unpacked_pip-25.3-py3-none-any | ||
|
|
||
| # Manual patching for CVE-2026-24049v0 | ||
| # For CVE-2026-24049, unpack.py file is located in 3 different places and each is of different version so the same patch cannot be applied to all of them. | ||
| # Affected files are under src and archived inside a .whl file, so we need to unpack it, apply the patch, and then re-zip it. | ||
| echo "Manually Patching virtualenv-20.36.1/src/virtualenv/seed/wheels/embed/setuptools-75.3.2-py3-none-any.whl/setuptools/_vendor/wheel/cli/unpack.py" | ||
| mkdir -p unpacked_setuptools-75.3.2-py3-none-any | ||
| unzip src/virtualenv/seed/wheels/embed/setuptools-75.3.2-py3-none-any.whl -d unpacked_setuptools-75.3.2-py3-none-any | ||
| patch -p1 -d unpacked_setuptools-75.3.2-py3-none-any < %{PATCH1003} | ||
| rm -f src/virtualenv/seed/wheels/embed/setuptools-75.3.2-py3-none-any.whl | ||
| pushd unpacked_setuptools-75.3.2-py3-none-any | ||
| zip -r ../src/virtualenv/seed/wheels/embed/setuptools-75.3.2-py3-none-any.whl * | ||
| popd | ||
| rm -rf unpacked_setuptools-75.3.2-py3-none-any | ||
|
|
||
| # Manual patching for CVE-2026-24049v0 | ||
| echo "Manually Patching virtualenv-20.36.1/src/virtualenv/seed/wheels/embed/setuptools-80.9.0-py3-none-any.whl/setuptools/_vendor/wheel/cli/unpack.py" | ||
| mkdir -p unpacked_setuptools-80.9.0-py3-none-any | ||
| unzip src/virtualenv/seed/wheels/embed/setuptools-80.9.0-py3-none-any.whl -d unpacked_setuptools-80.9.0-py3-none-any | ||
| patch -p1 -d unpacked_setuptools-80.9.0-py3-none-any < %{PATCH1003} | ||
| rm -f src/virtualenv/seed/wheels/embed/setuptools-80.9.0-py3-none-any.whl | ||
| pushd unpacked_setuptools-80.9.0-py3-none-any | ||
| zip -r ../src/virtualenv/seed/wheels/embed/setuptools-80.9.0-py3-none-any.whl * | ||
| popd | ||
| rm -rf unpacked_setuptools-80.9.0-py3-none-any | ||
|
|
||
| # Manual patching for CVE-2026-24049v1 | ||
| echo "Manually Patching virtualenv-20.36.1/src/virtualenv/seed/wheels/embed/unpacked_wheel-0.45.1-py3-none-any.whl/wheel/cli/unpack.py" | ||
| mkdir -p unpacked_wheel-0.45.1-py3-none-any | ||
| unzip src/virtualenv/seed/wheels/embed/wheel-0.45.1-py3-none-any.whl -d unpacked_wheel-0.45.1-py3-none-any | ||
| patch -p1 -d unpacked_wheel-0.45.1-py3-none-any < %{PATCH1004} | ||
| rm -f src/virtualenv/seed/wheels/embed/wheel-0.45.1-py3-none-any.whl | ||
| pushd unpacked_wheel-0.45.1-py3-none-any | ||
| zip -r ../src/virtualenv/seed/wheels/embed/unpacked_wheel-0.45.1-py3-none-any.whl * | ||
| popd | ||
| rm -rf unpacked_wheel-0.45.1-py3-none-any | ||
|
|
||
|
|
||
| %generate_buildrequires | ||
|
|
||
|
|
@@ -60,6 +136,9 @@ tox -e py | |
| %{_bindir}/virtualenv | ||
|
|
||
| %changelog | ||
| * Mon Feb 23 2026 BinduSri Adabala <v-badabala@microsoft.com> - 20.36.1-2 | ||
| - Patch for CVE-2025-50181, CVE-2026-24049 and CVE-2026-1703 | ||
|
|
||
| * Wed Jan 14 2026 Archana Shettigar <v-shettigara@microsoft.com> - 20.36.1-1 | ||
| - Upgrade to 20.36.1 for CVE-2026-22702 | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Patch looks good w.r.t upstream