]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.15] gh-151558: Fix symlink escape via `tarfile` hardlink-extraction fallback ...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Tue, 23 Jun 2026 13:58:57 +0000 (15:58 +0200)
committerGitHub <noreply@github.com>
Tue, 23 Jun 2026 13:58:57 +0000 (13:58 +0000)
(cherry picked from commit 27dd970bf6b17ebca7c8ed486a40ab043ed7af8f)

Co-authored-by: Stan Ulbrych <stan@python.org>
Lib/tarfile.py
Lib/test/test_tarfile.py
Misc/NEWS.d/next/Security/2026-06-10-13-08-19.gh-issue-151558.mL74i2.rst [new file with mode: 0644]

index 7660c1dbc44ba498437b40d2c601e429056e0f59..43d3db728cb76c86c108a70f8fc9b82505cb0e05 100644 (file)
@@ -2806,6 +2806,9 @@ class TarFile(object):
                     "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
index 04c4e990a69d73c3aee6b0d63e5254ae8a4299ab..447ccee6f6a51941ef4d1f598ce10ec8e7854f57 100644 (file)
@@ -4384,6 +4384,30 @@ class TestExtractionFilters(unittest.TestCase):
                     self.expect_file("boom", symlink_to='../../link_here')
                     self.expect_file("c", symlink_to='b')
 
+    @symlink_test
+    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'))
+
     @symlink_test
     def test_exfiltration_via_symlink(self):
         # (CVE-2025-4138)
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 (file)
index 0000000..74459d5
--- /dev/null
@@ -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`.