Skip to content
Draft
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
150 changes: 150 additions & 0 deletions SPECS/python3/CVE-2026-4360.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
From 7c721a42009902c50b7707cb9f73aee88228f7f3 Mon Sep 17 00:00:00 2001
From: "Miss Islington (bot)"
<31488909+miss-islington@users.noreply.github.com>
Date: Tue, 4 Aug 2026 11:28:03 +0200
Subject: [PATCH] gh-151987: Pass filter_function to TarFile._extract_one()
during .extract() (GH-151988) (#152611)

gh-151987: Pass filter_function to TarFile._extract_one() during .extract() (GH-151988)

(cherry picked from commit 7ccdbaba2c54250a70d7f25632152df7655a5e0a)

Co-authored-by: Petr Viktorin <encukou@gmail.com>
Co-authored-by: Seth Michael Larson <seth@python.org>
Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
Upstream-reference: https://github.com/python/cpython/commit/0367912be336348b30572f8029cec4a282782d92.patch
---
Lib/tarfile.py | 3 +-
Lib/test/test_tarfile.py | 92 +++++++++++++++++++
...-06-23-14-19-30.gh-issue-151987.8mNIMf.rst | 2 +
3 files changed, 96 insertions(+), 1 deletion(-)
create mode 100644 Misc/NEWS.d/next/Security/2026-06-23-14-19-30.gh-issue-151987.8mNIMf.rst

diff --git a/Lib/tarfile.py b/Lib/tarfile.py
index 461ec16..d609c91 100755
--- a/Lib/tarfile.py
+++ b/Lib/tarfile.py
@@ -2408,7 +2408,8 @@ class TarFile(object):
tarinfo, unfiltered = self._get_extract_tarinfo(
member, filter_function, path)
if tarinfo is not None:
- self._extract_one(tarinfo, path, set_attrs, numeric_owner)
+ self._extract_one(tarinfo, path, set_attrs, numeric_owner,
+ filter_function=filter_function)

def _get_extract_tarinfo(self, member, filter_function, path):
"""Get (filtered, unfiltered) TarInfos from *member*
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
index 717d1c1..ecb0404 100644
--- a/Lib/test/test_tarfile.py
+++ b/Lib/test/test_tarfile.py
@@ -4234,6 +4234,98 @@ class TestExtractionFilters(unittest.TestCase):
st_mode = cc.outerdir.stat().st_mode
self.assertNotEqual(st_mode & 0o777, 0o777)

+ @symlink_test
+ @unittest.skipUnless(hasattr(os, 'chown'), "missing os.chown")
+ @unittest.skipUnless(hasattr(os, 'lchown'), "missing os.lchown")
+ @unittest.skipUnless(hasattr(os, 'geteuid'), "missing os.geteuid")
+ @support.subTests('link_type', (tarfile.SYMTYPE, tarfile.LNKTYPE))
+ def test_chown_links_on_extract(self, link_type):
+ with ArchiveMaker() as arc:
+ arc.add("test.txt",
+ uid=1337, gid=1337, uname="", gname="", mode='-rwxr-xr-x')
+ arc.add("link",
+ type=link_type,
+ linkname='test.txt',
+ uid=1337, gid=1337, uname="", gname="", mode='-rwxr-xr-x')
+
+ with (
+ os_helper.temp_dir() as tmpdir,
+ arc.open() as tar,
+ unittest.mock.patch("os.chown") as mock_chown,
+ unittest.mock.patch("os.lchown") as mock_lchown,
+ unittest.mock.patch("os.geteuid") as mock_geteuid,
+ ):
+ # Set UID to 0 so chown() is attempted.
+ mock_geteuid.return_value = 0
+ tar.extract("link", path=tmpdir, filter='data')
+ extract_path = os.path.join(tmpdir, "link")
+
+ if link_type == tarfile.SYMTYPE:
+ mock_chown.assert_not_called()
+ mock_lchown.assert_called_once_with(extract_path, -1, -1)
+ else:
+ mock_chown.assert_has_calls([
+ unittest.mock.call(extract_path, -1, -1),
+ unittest.mock.call(extract_path, -1, -1)
+ ])
+ mock_lchown.assert_not_called()
+
+ @symlink_test
+ @unittest.skipUnless(hasattr(os, 'chown'), "missing os.chown")
+ @unittest.skipUnless(hasattr(os, 'lchown'), "missing os.lchown")
+ @unittest.skipUnless(hasattr(os, 'geteuid'), "missing os.geteuid")
+ @support.subTests('link_type', (tarfile.SYMTYPE, tarfile.LNKTYPE))
+ def test_chown_links_on_extractall(self, link_type):
+ with ArchiveMaker() as arc:
+ arc.add("test.txt",
+ uid=1337, gid=1337, uname="", gname="", mode='-rwxr-xr-x')
+ arc.add("link",
+ type=link_type,
+ linkname='test.txt',
+ uid=1337, gid=1337, uname="", gname="", mode='-rwxr-xr-x')
+
+ with (
+ os_helper.temp_dir() as tmpdir,
+ arc.open() as tar,
+ unittest.mock.patch("os.chown") as mock_chown,
+ unittest.mock.patch("os.lchown") as mock_lchown,
+ unittest.mock.patch("os.geteuid") as mock_geteuid,
+ ):
+ # Set UID to 0 so chown() is attempted.
+ mock_geteuid.return_value = 0
+ tar.extractall(path=tmpdir, filter='data')
+ extract_link_path = os.path.join(tmpdir, "link")
+ extract_file_path = os.path.join(tmpdir, "test.txt")
+
+ if link_type == tarfile.SYMTYPE:
+ mock_chown.assert_called_once_with(extract_file_path, -1, -1)
+ mock_lchown.assert_called_once_with(extract_link_path, -1, -1)
+ else:
+ mock_chown.assert_has_calls([
+ unittest.mock.call(extract_file_path, -1, -1),
+ unittest.mock.call(extract_link_path, -1, -1)
+ ])
+ mock_lchown.assert_not_called()
+
+ def test_extract_filters_target(self):
+ # Test that when extract() falls back to extracting (rather than
+ # linking) a hardlink target, it filters the target.
+ with ArchiveMaker() as arc:
+ arc.add("target")
+ arc.add("link", hardlink_to="target")
+ def testing_filter(member, path):
+ if member.name == 'target':
+ # target: set read-only
+ return member.replace(mode=stat.S_IRUSR)
+ # link: don't overwrite the mode
+ return member.replace(mode=None)
+ tempdir = pathlib.Path(TEMPDIR) / 'extract'
+ with os_helper.temp_dir(tempdir), arc.open() as tar:
+ tar.extract("link", path=tempdir, filter=testing_filter)
+ path = tempdir / 'link'
+ if os_helper.can_chmod():
+ self.assertFalse(path.stat().st_mode & stat.S_IWUSR)
+
def test_link_fallback_normalizes(self):
# Make sure hardlink fallbacks work for non-normalized paths for all
# filters
diff --git a/Misc/NEWS.d/next/Security/2026-06-23-14-19-30.gh-issue-151987.8mNIMf.rst b/Misc/NEWS.d/next/Security/2026-06-23-14-19-30.gh-issue-151987.8mNIMf.rst
new file mode 100644
index 0000000..9eea7b3
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2026-06-23-14-19-30.gh-issue-151987.8mNIMf.rst
@@ -0,0 +1,2 @@
+The :meth:`tarfile.TarFile.extract` method now applies the given filter when
+it extracts a link target from the archive as a fallback.
--
2.45.4

6 changes: 5 additions & 1 deletion SPECS/python3/python3.spec
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
Summary: A high-level scripting language
Name: python3
Version: 3.12.9
Release: 13%{?dist}
Release: 14%{?dist}
License: PSF
Vendor: Microsoft Corporation
Distribution: Azure Linux
Expand Down Expand Up @@ -35,6 +35,7 @@ Patch15: CVE-2026-1502.patch
Patch16: CVE-2025-13462.patch
Patch17: CVE-2026-8328.patch
Patch18: CVE-2026-7774.patch
Patch19: CVE-2026-4360.patch

BuildRequires: bzip2-devel
BuildRequires: expat-devel >= 2.1.0
Expand Down Expand Up @@ -257,6 +258,9 @@ rm -rf %{buildroot}%{_bindir}/__pycache__
%{_libdir}/python%{majmin}/test/*

%changelog
* Wed Aug 05 2026 Azure Linux Security Servicing Account <azurelinux-security@microsoft.com> - 3.12.9-14
- Patch for CVE-2026-4360

* Wed Jun 10 2026 Azure Linux Security Servicing Account <azurelinux-security@microsoft.com> - 3.12.9-13
- Patch for CVE-2026-7774

Expand Down
6 changes: 3 additions & 3 deletions toolkit/resources/manifests/package/pkggen_core_aarch64.txt
Original file line number Diff line number Diff line change
Expand Up @@ -244,9 +244,9 @@ ca-certificates-base-3.0.0-15.azl3.noarch.rpm
ca-certificates-3.0.0-15.azl3.noarch.rpm
dwz-0.14-2.azl3.aarch64.rpm
unzip-6.0-22.azl3.aarch64.rpm
python3-3.12.9-13.azl3.aarch64.rpm
python3-devel-3.12.9-13.azl3.aarch64.rpm
python3-libs-3.12.9-13.azl3.aarch64.rpm
python3-3.12.9-14.azl3.aarch64.rpm
python3-devel-3.12.9-14.azl3.aarch64.rpm
python3-libs-3.12.9-14.azl3.aarch64.rpm
python3-setuptools-69.0.3-6.azl3.noarch.rpm
python3-pygments-2.7.4-2.azl3.noarch.rpm
which-2.21-8.azl3.aarch64.rpm
Expand Down
6 changes: 3 additions & 3 deletions toolkit/resources/manifests/package/pkggen_core_x86_64.txt
Original file line number Diff line number Diff line change
Expand Up @@ -244,9 +244,9 @@ ca-certificates-base-3.0.0-15.azl3.noarch.rpm
ca-certificates-3.0.0-15.azl3.noarch.rpm
dwz-0.14-2.azl3.x86_64.rpm
unzip-6.0-22.azl3.x86_64.rpm
python3-3.12.9-13.azl3.x86_64.rpm
python3-devel-3.12.9-13.azl3.x86_64.rpm
python3-libs-3.12.9-13.azl3.x86_64.rpm
python3-3.12.9-14.azl3.x86_64.rpm
python3-devel-3.12.9-14.azl3.x86_64.rpm
python3-libs-3.12.9-14.azl3.x86_64.rpm
python3-setuptools-69.0.3-6.azl3.noarch.rpm
python3-pygments-2.7.4-2.azl3.noarch.rpm
which-2.21-8.azl3.x86_64.rpm
Expand Down
14 changes: 7 additions & 7 deletions toolkit/resources/manifests/package/toolchain_aarch64.txt
Original file line number Diff line number Diff line change
Expand Up @@ -531,19 +531,19 @@ pyproject-rpm-macros-1.12.0-2.azl3.noarch.rpm
pyproject-srpm-macros-1.12.0-2.azl3.noarch.rpm
python-markupsafe-debuginfo-2.1.3-1.azl3.aarch64.rpm
python-wheel-wheel-0.43.0-2.azl3.noarch.rpm
python3-3.12.9-13.azl3.aarch64.rpm
python3-3.12.9-14.azl3.aarch64.rpm
python3-audit-3.1.2-1.azl3.aarch64.rpm
python3-cracklib-2.9.11-1.azl3.aarch64.rpm
python3-curses-3.12.9-13.azl3.aarch64.rpm
python3-curses-3.12.9-14.azl3.aarch64.rpm
python3-Cython-3.0.5-3.azl3.aarch64.rpm
python3-debuginfo-3.12.9-13.azl3.aarch64.rpm
python3-devel-3.12.9-13.azl3.aarch64.rpm
python3-debuginfo-3.12.9-14.azl3.aarch64.rpm
python3-devel-3.12.9-14.azl3.aarch64.rpm
python3-flit-core-3.9.0-1.azl3.noarch.rpm
python3-gpg-1.23.2-2.azl3.aarch64.rpm
python3-jinja2-3.1.2-3.azl3.noarch.rpm
python3-libcap-ng-0.8.4-1.azl3.aarch64.rpm
python3-libmount-2.40.2-5.azl3.aarch64.rpm
python3-libs-3.12.9-13.azl3.aarch64.rpm
python3-libs-3.12.9-14.azl3.aarch64.rpm
python3-libxml2-2.11.5-10.azl3.aarch64.rpm
python3-lxml-4.9.3-2.azl3.aarch64.rpm
python3-magic-5.45-1.azl3.noarch.rpm
Expand All @@ -555,8 +555,8 @@ python3-pygments-2.7.4-2.azl3.noarch.rpm
python3-rpm-4.18.2-1.azl3.aarch64.rpm
python3-rpm-generators-14-11.azl3.noarch.rpm
python3-setuptools-69.0.3-6.azl3.noarch.rpm
python3-test-3.12.9-13.azl3.aarch64.rpm
python3-tools-3.12.9-13.azl3.aarch64.rpm
python3-test-3.12.9-14.azl3.aarch64.rpm
python3-tools-3.12.9-14.azl3.aarch64.rpm
python3-wheel-0.43.0-2.azl3.noarch.rpm
readline-8.2-2.azl3.aarch64.rpm
readline-debuginfo-8.2-2.azl3.aarch64.rpm
Expand Down
14 changes: 7 additions & 7 deletions toolkit/resources/manifests/package/toolchain_x86_64.txt
Original file line number Diff line number Diff line change
Expand Up @@ -539,19 +539,19 @@ pyproject-rpm-macros-1.12.0-2.azl3.noarch.rpm
pyproject-srpm-macros-1.12.0-2.azl3.noarch.rpm
python-markupsafe-debuginfo-2.1.3-1.azl3.x86_64.rpm
python-wheel-wheel-0.43.0-2.azl3.noarch.rpm
python3-3.12.9-13.azl3.x86_64.rpm
python3-3.12.9-14.azl3.x86_64.rpm
python3-audit-3.1.2-1.azl3.x86_64.rpm
python3-cracklib-2.9.11-1.azl3.x86_64.rpm
python3-curses-3.12.9-13.azl3.x86_64.rpm
python3-curses-3.12.9-14.azl3.x86_64.rpm
python3-Cython-3.0.5-3.azl3.x86_64.rpm
python3-debuginfo-3.12.9-13.azl3.x86_64.rpm
python3-devel-3.12.9-13.azl3.x86_64.rpm
python3-debuginfo-3.12.9-14.azl3.x86_64.rpm
python3-devel-3.12.9-14.azl3.x86_64.rpm
python3-flit-core-3.9.0-1.azl3.noarch.rpm
python3-gpg-1.23.2-2.azl3.x86_64.rpm
python3-jinja2-3.1.2-3.azl3.noarch.rpm
python3-libcap-ng-0.8.4-1.azl3.x86_64.rpm
python3-libmount-2.40.2-5.azl3.x86_64.rpm
python3-libs-3.12.9-13.azl3.x86_64.rpm
python3-libs-3.12.9-14.azl3.x86_64.rpm
python3-libxml2-2.11.5-10.azl3.x86_64.rpm
python3-lxml-4.9.3-2.azl3.x86_64.rpm
python3-magic-5.45-1.azl3.noarch.rpm
Expand All @@ -563,8 +563,8 @@ python3-pygments-2.7.4-2.azl3.noarch.rpm
python3-rpm-4.18.2-1.azl3.x86_64.rpm
python3-rpm-generators-14-11.azl3.noarch.rpm
python3-setuptools-69.0.3-6.azl3.noarch.rpm
python3-test-3.12.9-13.azl3.x86_64.rpm
python3-tools-3.12.9-13.azl3.x86_64.rpm
python3-test-3.12.9-14.azl3.x86_64.rpm
python3-tools-3.12.9-14.azl3.x86_64.rpm
python3-wheel-0.43.0-2.azl3.noarch.rpm
readline-8.2-2.azl3.x86_64.rpm
readline-debuginfo-8.2-2.azl3.x86_64.rpm
Expand Down
Loading