https://github.com/python/cpython/commit/0f852b3f07dd8e71e40326a51c02afbf16a42cc5
commit: 0f852b3f07dd8e71e40326a51c02afbf16a42cc5
branch: 3.10
author: Stan Ulbrych <[email protected]>
committer: pablogsal <[email protected]>
date: 2026-08-10T20:53:54+01:00
summary:
[3.10] gh-151558: Fix symlink escape via `tarfile` hardlink-extraction fallback
(GH-151559) (#152080)
gh-151558: Fix symlink escape via `tarfile` hardlink-extraction fallback
(GH-151559)
(cherry picked from commit 27dd970bf6b17ebca7c8ed486a40ab043ed7af8f)
files:
A Misc/NEWS.d/next/Security/2026-06-10-13-08-19.gh-issue-151558.mL74i2.rst
M Lib/tarfile.py
M Lib/test/test_tarfile.py
diff --git a/Lib/tarfile.py b/Lib/tarfile.py
index cdd97dbf69f7cd..af6066fa0f67d4 100755
--- a/Lib/tarfile.py
+++ b/Lib/tarfile.py
@@ -2670,6 +2670,9 @@ def makelink_with_filter(self, tarinfo, targetpath,
"makelink_with_filter: if filter_function is not None, "
+ "extraction_root must also not be None")
try:
+ filter_function(
+ unfiltered.replace(name=tarinfo.name, deep=False),
+ extraction_root)
filtered = filter_function(unfiltered, extraction_root)
except _FILTER_ERRORS as cause:
raise LinkFallbackError(tarinfo, unfiltered.name) from cause
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
index f9060aa6a94783..d114d652c950d0 100644
--- a/Lib/test/test_tarfile.py
+++ b/Lib/test/test_tarfile.py
@@ -3987,6 +3987,29 @@ def test_sneaky_hardlink_fallback(self):
self.expect_file("boom", symlink_to='../../link_here')
self.expect_file("c", symlink_to='b')
+ def test_sneaky_hardlink_fallback_deep(self):
+ # (CVE-2026-11940)
+ with ArchiveMaker() as arc:
+ arc.add("a/b/s", symlink_to=os.path.join("..", "escape"))
+ arc.add("s", hardlink_to=os.path.join("a", "b", "s"))
+
+ with self.check_context(arc.open(), 'data'):
+ e = self.expect_exception(
+ tarfile.LinkFallbackError,
+ "link 's' would be extracted as a copy of "
+ + "'a/b/s', which was rejected")
+ self.assertIsInstance(e.__cause__,
+ tarfile.LinkOutsideDestinationError)
+
+ for filter in 'tar', 'fully_trusted':
+ with self.subTest(filter), self.check_context(arc.open(), filter):
+ if not os_helper.can_symlink():
+ self.expect_file("a/")
+ self.expect_file("a/b/")
+ else:
+ self.expect_file("a/b/s", symlink_to=os.path.join('..',
'escape'))
+ self.expect_file("s", symlink_to=os.path.join('..',
'escape'))
+
def test_exfiltration_via_symlink(self):
# (CVE-2025-4138)
# Test changing symlinks that result in a symlink pointing outside
diff --git
a/Misc/NEWS.d/next/Security/2026-06-10-13-08-19.gh-issue-151558.mL74i2.rst
b/Misc/NEWS.d/next/Security/2026-06-10-13-08-19.gh-issue-151558.mL74i2.rst
new file mode 100644
index 00000000000000..575081600f8bdd
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2026-06-10-13-08-19.gh-issue-151558.mL74i2.rst
@@ -0,0 +1,3 @@
+Fixed an vulnerability in the :mod:`tarfile` ``data`` and ``tar`` extraction
+filters where crafted archives could create a symlink pointing outside the
+destination directory. This was a bypass of CVE-2025-4330.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]