Skip to content

Commit 7940084

Browse files
Preserve the current behavior whether it is possible.
1 parent 7c5ad11 commit 7940084

File tree

3 files changed

+56
-29
lines changed

3 files changed

+56
-29
lines changed

Lib/shutil.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1315,6 +1315,7 @@ def _unpack_zipfile(filename, extract_dir):
13151315
raise ReadError("%s is not a zip file" % filename)
13161316

13171317
with zipfile.ZipFile(filename) as zip:
1318+
zip._ignore_invalid_names = True
13181319
zip.extractall(extract_dir)
13191320

13201321
def _unpack_tarfile(filename, extract_dir, *, filter=None):

Lib/test/test_shutil.py

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2173,12 +2173,13 @@ def test_unpack_archive_zip_badpaths(self):
21732173
with zipfile.ZipFile(zipname, 'w') as zf:
21742174
zf.writestr(abspath, 'badfile')
21752175
zf.writestr(os.sep + abspath, 'badfile')
2176-
zf.writestr('/abspath2', 'badfile')
2177-
if os.name == 'nt':
2178-
zf.writestr('C:/abspath3', 'badfile')
2179-
zf.writestr('C:\\abspath4', 'badfile')
2180-
zf.writestr('C:abspath5', 'badfile')
2181-
zf.writestr('C:/C:/abspath6', 'badfile')
2176+
zf.writestr('/abspath', 'badfile')
2177+
zf.writestr('C:/abspath', 'badfile')
2178+
zf.writestr('D:\\abspath', 'badfile')
2179+
zf.writestr('E:abspath', 'badfile')
2180+
zf.writestr('F:/G:/abspath', 'badfile')
2181+
zf.writestr('//server/share/abspath', 'badfile')
2182+
zf.writestr('\\\\server2\\share\\abspath', 'badfile')
21822183
zf.writestr('../relpath', 'badfile')
21832184
zf.writestr(os.pardir + os.sep + 'relpath2', 'badfile')
21842185
zf.writestr('good/file', 'goodfile')
@@ -2189,16 +2190,23 @@ def test_unpack_archive_zip_badpaths(self):
21892190
self.assertTrue(os.path.isfile(os.path.join(dstdir, 'good', 'file')))
21902191
self.assertTrue(os.path.isfile(os.path.join(dstdir, 'good..file')))
21912192
self.assertFalse(os.path.exists(abspath))
2192-
self.assertTrue(os.path.exists(os.path.join(dstdir, 'abspath2')))
2193-
if os.name == 'nt':
2194-
self.assertTrue(os.path.exists(os.path.join(dstdir, 'abspath3')))
2195-
self.assertTrue(os.path.exists(os.path.join(dstdir, 'abspath4')))
2196-
self.assertTrue(os.path.exists(os.path.join(dstdir, 'abspath5')))
2197-
self.assertTrue(os.path.exists(os.path.join(dstdir, 'C_', 'abspath6')))
2198-
self.assertFalse(os.path.exists(os.path.join(dstdir, '..', 'relpath')))
2199-
self.assertTrue(os.path.exists(os.path.join(dstdir, 'relpath')))
2193+
self.assertFalse(os.path.exists(os.path.join(dstdir, 'abspath')))
2194+
self.assertFalse(os.path.exists(os.path.join(dstdir, 'G_')))
2195+
self.assertFalse(os.path.exists(os.path.join(dstdir, 'server')))
2196+
self.assertFalse(os.path.exists(os.path.join(dstdir, 'server2')))
2197+
if os.name != 'nt':
2198+
self.assertTrue(os.path.isfile(os.path.join(dstdir, 'C:', 'abspath')))
2199+
self.assertTrue(os.path.isfile(os.path.join(dstdir, 'D:\\abspath')))
2200+
self.assertTrue(os.path.isfile(os.path.join(dstdir, 'E:abspath')))
2201+
self.assertTrue(os.path.isfile(os.path.join(dstdir, 'F:', 'G:', 'abspath')))
2202+
self.assertTrue(os.path.isfile(os.path.join(dstdir, '\\\\server2\\share\\abspath')))
2203+
if os.pardir == '..':
2204+
self.assertFalse(os.path.exists(os.path.join(dstdir, '..', 'relpath')))
2205+
self.assertFalse(os.path.exists(os.path.join(dstdir, 'relpath')))
2206+
else:
2207+
self.assertTrue(os.path.isfile(os.path.join(dstdir, '..', 'relpath')))
22002208
self.assertFalse(os.path.exists(os.path.join(dstdir, os.pardir, 'relpath2')))
2201-
self.assertTrue(os.path.exists(os.path.join(dstdir, 'relpath2')))
2209+
self.assertFalse(os.path.exists(os.path.join(dstdir, 'relpath2')))
22022210

22032211
dstdir2 = os.path.join(self.mkdtemp(), 'dst')
22042212
os.mkdir(dstdir2)
@@ -2207,16 +2215,23 @@ def test_unpack_archive_zip_badpaths(self):
22072215
self.assertTrue(os.path.isfile(os.path.join('good', 'file')))
22082216
self.assertTrue(os.path.isfile('good..file'))
22092217
self.assertFalse(os.path.exists(abspath))
2210-
self.assertTrue(os.path.exists('abspath2'))
2211-
if os.name == 'nt':
2212-
self.assertTrue(os.path.exists('abspath3'))
2213-
self.assertTrue(os.path.exists('abspath4'))
2214-
self.assertTrue(os.path.exists('abspath5'))
2215-
self.assertTrue(os.path.exists(os.path.join('c_', 'abspath6')))
2216-
self.assertFalse(os.path.exists(os.path.join('..', 'relpath')))
2217-
self.assertTrue(os.path.exists('relpath'))
2218+
self.assertFalse(os.path.exists('abspath'))
2219+
self.assertFalse(os.path.exists('C_'))
2220+
self.assertFalse(os.path.exists('server'))
2221+
self.assertFalse(os.path.exists('server2'))
2222+
if os.name != 'nt':
2223+
self.assertTrue(os.path.isfile(os.path.join('C:', 'abspath')))
2224+
self.assertTrue(os.path.isfile('D:\\abspath'))
2225+
self.assertTrue(os.path.isfile('E:abspath'))
2226+
self.assertTrue(os.path.isfile(os.path.join('F:', 'G:', 'abspath')))
2227+
self.assertTrue(os.path.isfile('\\\\server2\\share\\abspath'))
2228+
if os.pardir == '..':
2229+
self.assertFalse(os.path.exists(os.path.join('..', 'relpath')))
2230+
self.assertFalse(os.path.exists('relpath'))
2231+
else:
2232+
self.assertTrue(os.path.isfile(os.path.join('..', 'relpath')))
22182233
self.assertFalse(os.path.exists(os.path.join(os.pardir, 'relpath2')))
2219-
self.assertTrue(os.path.exists('relpath2'))
2234+
self.assertFalse(os.path.exists('relpath2'))
22202235

22212236
def test_unpack_registry(self):
22222237

Lib/zipfile/__init__.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1410,6 +1410,7 @@ class ZipFile:
14101410

14111411
fp = None # Set here since __del__ checks it
14121412
_windows_illegal_name_trans_table = None
1413+
_ignore_invalid_names = False
14131414

14141415
def __init__(self, file, mode="r", compression=ZIP_STORED, allowZip64=True,
14151416
compresslevel=None, *, strict_timestamps=True, metadata_encoding=None):
@@ -1890,21 +1891,31 @@ def _extract_member(self, member, targetpath, pwd):
18901891

18911892
# build the destination pathname, replacing
18921893
# forward slashes to platform specific separators.
1893-
arcname = member.filename.replace('/', os.path.sep)
1894-
1895-
if os.path.altsep:
1894+
arcname = member.filename
1895+
if os.path.sep != '/':
1896+
arcname = arcname.replace('/', os.path.sep)
1897+
if os.path.altsep and os.path.altsep != '/':
18961898
arcname = arcname.replace(os.path.altsep, os.path.sep)
18971899
# interpret absolute pathname as relative, remove drive letter or
18981900
# UNC path, redundant separators, "." and ".." components.
1899-
arcname = os.path.splitdrive(arcname)[1]
1901+
drive, root, arcname = os.path.splitroot(arcname)
1902+
if self._ignore_invalid_names and (drive or root):
1903+
return None
1904+
if self._ignore_invalid_names and os.path.pardir in arcname.split(os.path.sep):
1905+
return None
19001906
invalid_path_parts = ('', os.path.curdir, os.path.pardir)
19011907
arcname = os.path.sep.join(x for x in arcname.split(os.path.sep)
19021908
if x not in invalid_path_parts)
19031909
if os.path.sep == '\\':
19041910
# filter illegal characters on Windows
1905-
arcname = self._sanitize_windows_name(arcname, os.path.sep)
1911+
arcname2 = self._sanitize_windows_name(arcname, os.path.sep)
1912+
if self._ignore_invalid_names and arcname2 != arcname:
1913+
return None
1914+
arcname = arcname2
19061915

19071916
if not arcname and not member.is_dir():
1917+
if self._ignore_invalid_names:
1918+
return None
19081919
raise ValueError("Empty filename.")
19091920

19101921
targetpath = os.path.join(targetpath, arcname)

0 commit comments

Comments
 (0)