https://github.com/python/cpython/commit/7a562c474c6885b1d6f3ac74962bb920483bfbe1
commit: 7a562c474c6885b1d6f3ac74962bb920483bfbe1
branch: main
author: Xiao Yuan <[email protected]>
committer: gpshead <[email protected]>
date: 2026-09-08T06:15:28Z
summary:

gh-127636: Fix tarfile extracting trailing slash member names (GH-152984)

Fixes tarfile.TarFile.extract to accept archive member names with a
trailing forward slash, including those returned by tarfile.TarFile.getnames

very old style tar files may have these.  new archivers likely do not do this.

files:
A Misc/NEWS.d/next/Library/2026-07-04-00-19-23.gh-issue-127636.o_uD9U.rst
M Lib/tarfile.py
M Lib/test/test_tarfile.py

diff --git a/Lib/tarfile.py b/Lib/tarfile.py
index f46e938fd314dd..c4cbbdb980857d 100644
--- a/Lib/tarfile.py
+++ b/Lib/tarfile.py
@@ -2196,7 +2196,9 @@ def getmember(self, name):
            than once in the archive, its last occurrence is assumed to be the
            most up-to-date version.
         """
-        tarinfo = self._getmember(name.rstrip('/'))
+        tarinfo = self._getmember(name)
+        if tarinfo is None and name.endswith('/'):
+            tarinfo = self._getmember(name.rstrip('/'))
         if tarinfo is None:
             raise KeyError("filename %r not found" % name)
         return tarinfo
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
index 10106c3ada9ba5..62edb7115ed682 100644
--- a/Lib/test/test_tarfile.py
+++ b/Lib/test/test_tarfile.py
@@ -255,6 +255,19 @@ def test_add_dir_getmember(self):
         self.add_dir_and_getmember('bar')
         self.add_dir_and_getmember('a'*101)
 
+    def test_extract_name_with_trailing_slash(self):
+        # gh-127636: './mydir/' is deliberately a regular-file member
+        # (REGTYPE, not DIRTYPE) whose stored name ends in a slash.  It
+        # extracts as a file.  Do not "fix" this by setting DIRTYPE; the
+        # trailing-slash name on a non-directory is what is being tested.
+        with tarfile.open(tmpname, 'w') as tar:
+            tar.addfile(tarfile.TarInfo('./mydir/'))
+        with os_helper.temp_dir() as tmpdir, tarfile.open(tmpname) as tar:
+            names = tar.getnames()
+            self.assertEqual(names, ['./mydir/'])
+            tar.extract(names[0], tmpdir, filter='fully_trusted')
+            self.assertTrue(os.path.isfile(os.path.join(tmpdir, 'mydir')))
+
     @unittest.skipUnless(hasattr(os, "getuid") and hasattr(os, "getgid"),
                          "Missing getuid or getgid implementation")
     def add_dir_and_getmember(self, name):
diff --git 
a/Misc/NEWS.d/next/Library/2026-07-04-00-19-23.gh-issue-127636.o_uD9U.rst 
b/Misc/NEWS.d/next/Library/2026-07-04-00-19-23.gh-issue-127636.o_uD9U.rst
new file mode 100644
index 00000000000000..51957a73b5603b
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-07-04-00-19-23.gh-issue-127636.o_uD9U.rst
@@ -0,0 +1,3 @@
+Fix :meth:`tarfile.TarFile.extract` to accept archive member names with a
+trailing forward slash, including those returned by
+:meth:`tarfile.TarFile.getnames`. Contributed by Xiao Yuan.

_______________________________________________
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]

Reply via email to