cpython: 483488a1dec5 (original) (raw)
--- a/Doc/library/zipfile.rst
+++ b/Doc/library/zipfile.rst
@@ -242,6 +242,16 @@ ZipFile Objects
to extract to. member can be a filename or a :class:ZipInfo
object.
pwd is the password used for encrypted files.
- .. note:: +
If a member filename is an absolute path, a drive/UNC sharepoint and[](#l1.9)
leading (back)slashes will be stripped, e.g.: ``///foo/bar`` becomes[](#l1.10)
``foo/bar`` on Unix, and ``ะก:\foo\bar`` becomes ``foo\bar`` on Windows.[](#l1.11)
And all ``".."`` components in a member filename will be removed, e.g.:[](#l1.12)
``../../foo../../ba..r`` becomes ``foo../ba..r``. On Windows illegal[](#l1.13)
characters (``:``, ``<``, ``>``, ``|``, ``"``, ``?``, and ``*``)[](#l1.14)
replaced by underscore (``_``).[](#l1.15)
+
.. method:: ZipFile.extractall(path=None, members=None, pwd=None)
@@ -250,12 +260,9 @@ ZipFile Objects
be a subset of the list returned by :meth:namelist
. pwd is the password
used for encrypted files.
Never extract archives from untrusted sources without prior inspection.[](#l1.27)
It is possible that files are created outside of *path*, e.g. members[](#l1.28)
that have absolute filenames starting with ``"/"`` or filenames with two[](#l1.29)
dots ``".."``.[](#l1.30)
See :meth:`extract` note.[](#l1.31)
.. method:: ZipFile.printdir()
--- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -24,7 +24,7 @@ DATAFILES_DIR = 'zipfile_datafiles' SMALL_TEST_DATA = [('_ziptest1', '1q2w3e4r5t'), ('ziptest2dir/_ziptest2', 'qawsedrftg'),
('/ziptest2dir/ziptest3dir/_ziptest3', 'azsxdcfvgb'),[](#l2.7)
('ziptest2dir/ziptest3dir/_ziptest3', 'azsxdcfvgb'),[](#l2.8) ('ziptest2dir/ziptest3dir/ziptest4dir/_ziptest3', '6y7u8i9o0p')][](#l2.9)
@@ -501,10 +501,7 @@ class TestsWithSourceFile(unittest.TestC writtenfile = zipfp.extract(fpath) # make sure it was written to the right place
if os.path.isabs(fpath):[](#l2.16)
correctfile = os.path.join(os.getcwd(), fpath[1:])[](#l2.17)
else:[](#l2.18)
correctfile = os.path.join(os.getcwd(), fpath)[](#l2.19)
correctfile = os.path.join(os.getcwd(), fpath)[](#l2.20) correctfile = os.path.normpath(correctfile)[](#l2.21)
self.assertEqual(writtenfile, correctfile) @@ -526,10 +523,7 @@ class TestsWithSourceFile(unittest.TestC with zipfile.ZipFile(TESTFN2, "r") as zipfp: zipfp.extractall() for fpath, fdata in SMALL_TEST_DATA:
if os.path.isabs(fpath):[](#l2.28)
outfile = os.path.join(os.getcwd(), fpath[1:])[](#l2.29)
else:[](#l2.30)
outfile = os.path.join(os.getcwd(), fpath)[](#l2.31)
outfile = os.path.join(os.getcwd(), fpath)[](#l2.32)
with open(outfile, "rb") as f: self.assertEqual(fdata.encode(), f.read()) @@ -539,6 +533,80 @@ class TestsWithSourceFile(unittest.TestC # remove the test file subdirectories shutil.rmtree(os.path.join(os.getcwd(), 'ziptest2dir'))
- def check_file(self, filename, content):
self.assertTrue(os.path.isfile(filename))[](#l2.41)
with open(filename, 'rb') as f:[](#l2.42)
self.assertEqual(f.read(), content)[](#l2.43)
- def test_extract_hackers_arcnames(self):
hacknames = [[](#l2.46)
('../foo/bar', 'foo/bar'),[](#l2.47)
('foo/../bar', 'foo/bar'),[](#l2.48)
('foo/../../bar', 'foo/bar'),[](#l2.49)
('foo/bar/..', 'foo/bar'),[](#l2.50)
('./../foo/bar', 'foo/bar'),[](#l2.51)
('/foo/bar', 'foo/bar'),[](#l2.52)
('/foo/../bar', 'foo/bar'),[](#l2.53)
('/foo/../../bar', 'foo/bar'),[](#l2.54)
('//foo/bar', 'foo/bar'),[](#l2.55)
('../../foo../../ba..r', 'foo../ba..r'),[](#l2.56)
][](#l2.57)
if os.path.sep == '\\': # Windows.[](#l2.58)
hacknames.extend([[](#l2.59)
(r'..\foo\bar', 'foo/bar'),[](#l2.60)
(r'..\/foo\/bar', 'foo/bar'),[](#l2.61)
(r'foo/\..\/bar', 'foo/bar'),[](#l2.62)
(r'foo\/../\bar', 'foo/bar'),[](#l2.63)
(r'C:foo/bar', 'foo/bar'),[](#l2.64)
(r'C:/foo/bar', 'foo/bar'),[](#l2.65)
(r'C://foo/bar', 'foo/bar'),[](#l2.66)
(r'C:\foo\bar', 'foo/bar'),[](#l2.67)
(r'//conky/mountpoint/foo/bar', 'foo/bar'),[](#l2.68)
(r'\\conky\mountpoint\foo\bar', 'foo/bar'),[](#l2.69)
(r'///conky/mountpoint/foo/bar', 'conky/mountpoint/foo/bar'),[](#l2.70)
(r'\\\conky\mountpoint\foo\bar', 'conky/mountpoint/foo/bar'),[](#l2.71)
(r'//conky//mountpoint/foo/bar', 'conky/mountpoint/foo/bar'),[](#l2.72)
(r'\\conky\\mountpoint\foo\bar', 'conky/mountpoint/foo/bar'),[](#l2.73)
(r'//?/C:/foo/bar', 'foo/bar'),[](#l2.74)
(r'\\?\C:\foo\bar', 'foo/bar'),[](#l2.75)
(r'C:/../C:/foo/bar', 'C_/foo/bar'),[](#l2.76)
(r'a:b\c<d>e|f"g?h*i', 'b/c_d_e_f_g_h_i'),[](#l2.77)
])[](#l2.78)
for arcname, fixedname in hacknames:[](#l2.80)
content = b'foobar' + arcname.encode()[](#l2.81)
with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipfp:[](#l2.82)
zipfp.writestr(arcname, content)[](#l2.83)
targetpath = os.path.join('target', 'subdir', 'subsub')[](#l2.85)
correctfile = os.path.join(targetpath, *fixedname.split('/'))[](#l2.86)
with zipfile.ZipFile(TESTFN2, 'r') as zipfp:[](#l2.88)
writtenfile = zipfp.extract(arcname, targetpath)[](#l2.89)
self.assertEqual(writtenfile, correctfile)[](#l2.90)
self.check_file(correctfile, content)[](#l2.91)
shutil.rmtree('target')[](#l2.92)
with zipfile.ZipFile(TESTFN2, 'r') as zipfp:[](#l2.94)
zipfp.extractall(targetpath)[](#l2.95)
self.check_file(correctfile, content)[](#l2.96)
shutil.rmtree('target')[](#l2.97)
correctfile = os.path.join(os.getcwd(), *fixedname.split('/'))[](#l2.99)
with zipfile.ZipFile(TESTFN2, 'r') as zipfp:[](#l2.101)
writtenfile = zipfp.extract(arcname)[](#l2.102)
self.assertEqual(writtenfile, correctfile)[](#l2.103)
self.check_file(correctfile, content)[](#l2.104)
shutil.rmtree(fixedname.split('/')[0])[](#l2.105)
with zipfile.ZipFile(TESTFN2, 'r') as zipfp:[](#l2.107)
zipfp.extractall()[](#l2.108)
self.check_file(correctfile, content)[](#l2.109)
shutil.rmtree(fixedname.split('/')[0])[](#l2.110)
os.remove(TESTFN2)[](#l2.112)
+ def test_writestr_compression_stored(self): zipfp = zipfile.ZipFile(TESTFN2, "w") zipfp.writestr("a.txt", "hello world", compress_type=zipfile.ZIP_STORED)
--- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -1229,17 +1229,22 @@ class ZipFile: """ # build the destination pathname, replacing # forward slashes to platform specific separators.
# Strip trailing path separator, unless it represents the root.[](#l3.7)
if (targetpath[-1:] in (os.path.sep, os.path.altsep)[](#l3.8)
and len(os.path.splitdrive(targetpath)[1]) > 1):[](#l3.9)
targetpath = targetpath[:-1][](#l3.10)
arcname = member.filename.replace('/', os.path.sep)[](#l3.11)
# don't include leading "/" from file name if present[](#l3.13)
if member.filename[0] == '/':[](#l3.14)
targetpath = os.path.join(targetpath, member.filename[1:])[](#l3.15)
else:[](#l3.16)
targetpath = os.path.join(targetpath, member.filename)[](#l3.17)
if os.path.altsep:[](#l3.18)
arcname = arcname.replace(os.path.altsep, os.path.sep)[](#l3.19)
# interpret absolute pathname as relative, remove drive letter or[](#l3.20)
# UNC path, redundant separators, "." and ".." components.[](#l3.21)
arcname = os.path.splitdrive(arcname)[1][](#l3.22)
arcname = os.path.sep.join(x for x in arcname.split(os.path.sep)[](#l3.23)
if x not in ('', os.path.curdir, os.path.pardir))[](#l3.24)
# filter illegal characters on Windows[](#l3.25)
if os.path.sep == '\\':[](#l3.26)
illegal = ':<>|"?*'[](#l3.27)
table = str.maketrans(illegal, '_' * len(illegal))[](#l3.28)
arcname = arcname.translate(table)[](#l3.29)
targetpath = os.path.join(targetpath, arcname)[](#l3.31) targetpath = os.path.normpath(targetpath)[](#l3.32)
# Create all upper directories if necessary.
--- a/Misc/NEWS +++ b/Misc/NEWS @@ -167,6 +167,9 @@ Core and Builtins Library ------- +- Issue #6972: The zipfile module no longer overwrites files outside of