Skip to content

Commit 5a5230a

Browse files
authored
Update shutil.py & zipfile to 3.14.5 (RustPython#7894)
* Update `shutil.py` to 3.14.5 * Update `zipfile` to 3.14.5
1 parent 8c98871 commit 5a5230a

6 files changed

Lines changed: 236 additions & 72 deletions

File tree

Lib/shutil.py

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,27 +1314,9 @@ def _unpack_zipfile(filename, extract_dir):
13141314
if not zipfile.is_zipfile(filename):
13151315
raise ReadError("%s is not a zip file" % filename)
13161316

1317-
zip = zipfile.ZipFile(filename)
1318-
try:
1319-
for info in zip.infolist():
1320-
name = info.filename
1321-
1322-
# don't extract absolute paths or ones with .. in them
1323-
if name.startswith('/') or '..' in name:
1324-
continue
1325-
1326-
targetpath = os.path.join(extract_dir, *name.split('/'))
1327-
if not targetpath:
1328-
continue
1329-
1330-
_ensure_directory(targetpath)
1331-
if not name.endswith('/'):
1332-
# file
1333-
with zip.open(name, 'r') as source, \
1334-
open(targetpath, 'wb') as target:
1335-
copyfileobj(source, target)
1336-
finally:
1337-
zip.close()
1317+
with zipfile.ZipFile(filename) as zip:
1318+
zip._ignore_invalid_names = True
1319+
zip.extractall(extract_dir)
13381320

13391321
def _unpack_tarfile(filename, extract_dir, *, filter=None):
13401322
"""Unpack tar/tar.gz/tar.bz2/tar.xz/tar.zst `filename` to `extract_dir`

Lib/test/test_shutil.py

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2110,8 +2110,6 @@ def test_make_zipfile_rootdir_nodir(self):
21102110
def check_unpack_archive(self, format, **kwargs):
21112111
self.check_unpack_archive_with_converter(
21122112
format, lambda path: path, **kwargs)
2113-
self.check_unpack_archive_with_converter(
2114-
format, FakePath, **kwargs)
21152113
self.check_unpack_archive_with_converter(format, FakePath, **kwargs)
21162114

21172115
def check_unpack_archive_with_converter(self, format, converter, **kwargs):
@@ -2168,6 +2166,71 @@ def test_unpack_archive_zip(self):
21682166
with self.assertRaises(TypeError):
21692167
self.check_unpack_archive('zip', filter='data')
21702168

2169+
def test_unpack_archive_zip_badpaths(self):
2170+
srcdir = self.mkdtemp()
2171+
zipname = os.path.join(srcdir, 'test.zip')
2172+
abspath = os.path.join(srcdir, 'abspath')
2173+
with zipfile.ZipFile(zipname, 'w') as zf:
2174+
zf.writestr(abspath, 'badfile')
2175+
zf.writestr(os.sep + abspath, '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')
2183+
zf.writestr('../relpath', 'badfile')
2184+
zf.writestr(os.pardir + os.sep + 'relpath2', 'badfile')
2185+
zf.writestr('good/file', 'goodfile')
2186+
zf.writestr('good..file', 'goodfile')
2187+
2188+
dstdir = os.path.join(self.mkdtemp(), 'dst')
2189+
unpack_archive(zipname, dstdir)
2190+
self.assertTrue(os.path.isfile(os.path.join(dstdir, 'good', 'file')))
2191+
self.assertTrue(os.path.isfile(os.path.join(dstdir, 'good..file')))
2192+
self.assertFalse(os.path.exists(abspath))
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+
if os.name != 'nt':
2197+
self.assertTrue(os.path.isfile(os.path.join(dstdir, 'C:', 'abspath')))
2198+
self.assertTrue(os.path.isfile(os.path.join(dstdir, 'D:\\abspath')))
2199+
self.assertTrue(os.path.isfile(os.path.join(dstdir, 'E:abspath')))
2200+
self.assertTrue(os.path.isfile(os.path.join(dstdir, 'F:', 'G:', 'abspath')))
2201+
self.assertTrue(os.path.isfile(os.path.join(dstdir, '\\\\server2\\share\\abspath')))
2202+
if os.pardir == '..':
2203+
self.assertFalse(os.path.exists(os.path.join(dstdir, '..', 'relpath')))
2204+
self.assertFalse(os.path.exists(os.path.join(dstdir, 'relpath')))
2205+
else:
2206+
self.assertTrue(os.path.isfile(os.path.join(dstdir, '..', 'relpath')))
2207+
self.assertFalse(os.path.exists(os.path.join(dstdir, os.pardir, 'relpath2')))
2208+
self.assertFalse(os.path.exists(os.path.join(dstdir, 'relpath2')))
2209+
2210+
dstdir2 = os.path.join(self.mkdtemp(), 'dst')
2211+
os.mkdir(dstdir2)
2212+
with os_helper.change_cwd(dstdir2):
2213+
unpack_archive(zipname, '')
2214+
self.assertTrue(os.path.isfile(os.path.join('good', 'file')))
2215+
self.assertTrue(os.path.isfile('good..file'))
2216+
self.assertFalse(os.path.exists(abspath))
2217+
self.assertFalse(os.path.exists('abspath'))
2218+
self.assertFalse(os.path.exists('C_'))
2219+
self.assertFalse(os.path.exists('server'))
2220+
if os.name != 'nt':
2221+
self.assertTrue(os.path.isfile(os.path.join('C:', 'abspath')))
2222+
self.assertTrue(os.path.isfile('D:\\abspath'))
2223+
self.assertTrue(os.path.isfile('E:abspath'))
2224+
self.assertTrue(os.path.isfile(os.path.join('F:', 'G:', 'abspath')))
2225+
self.assertTrue(os.path.isfile('\\\\server2\\share\\abspath'))
2226+
if os.pardir == '..':
2227+
self.assertFalse(os.path.exists(os.path.join('..', 'relpath')))
2228+
self.assertFalse(os.path.exists('relpath'))
2229+
else:
2230+
self.assertTrue(os.path.isfile(os.path.join('..', 'relpath')))
2231+
self.assertFalse(os.path.exists(os.path.join(os.pardir, 'relpath2')))
2232+
self.assertFalse(os.path.exists('relpath2'))
2233+
21712234
def test_unpack_registry(self):
21722235

21732236
formats = get_unpack_formats()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

Lib/test/test_zipfile/_path/test_path.py

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import pickle
66
import stat
77
import sys
8-
import time
98
import unittest
109
import zipfile
1110
import zipfile._path
@@ -649,7 +648,7 @@ def test_backslash_not_separator(self):
649648
"""
650649
data = io.BytesIO()
651650
zf = zipfile.ZipFile(data, "w")
652-
zf.writestr(DirtyZipInfo.for_name("foo\\bar", zf), b"content")
651+
zf.writestr(DirtyZipInfo("foo\\bar")._for_archive(zf), b"content")
653652
zf.filename = ''
654653
root = zipfile.Path(zf)
655654
(first,) = root.iterdir()
@@ -672,20 +671,3 @@ class DirtyZipInfo(zipfile.ZipInfo):
672671
def __init__(self, filename, *args, **kwargs):
673672
super().__init__(filename, *args, **kwargs)
674673
self.filename = filename
675-
676-
@classmethod
677-
def for_name(cls, name, archive):
678-
"""
679-
Construct the same way that ZipFile.writestr does.
680-
681-
TODO: extract this functionality and re-use
682-
"""
683-
self = cls(filename=name, date_time=time.localtime(time.time())[:6])
684-
self.compress_type = archive.compression
685-
self.compress_level = archive.compresslevel
686-
if self.filename.endswith('/'): # pragma: no cover
687-
self.external_attr = 0o40775 << 16 # drwxrwxr-x
688-
self.external_attr |= 0x10 # MS-DOS directory flag
689-
else:
690-
self.external_attr = 0o600 << 16 # ?rw-------
691-
return self

0 commit comments

Comments
 (0)