Commit d2b1527f authored by Serhiy Storchaka's avatar Serhiy Storchaka

Issue #4844: ZipFile now raises BadZipFile when opens a ZIP file with an

incomplete "End of Central Directory" record.  Original patch by Guilherme
Polo and Alan McIntyre.
parent fc20d77b
...@@ -844,6 +844,20 @@ class OtherTests(unittest.TestCase): ...@@ -844,6 +844,20 @@ class OtherTests(unittest.TestCase):
chk = zipfile.is_zipfile(fp) chk = zipfile.is_zipfile(fp)
self.assertTrue(not chk) self.assertTrue(not chk)
def test_damaged_zipfile(self):
"""Check that zipfiles with missing bytes at the end raise BadZipFile."""
# - Create a valid zip file
fp = io.BytesIO()
with zipfile.ZipFile(fp, mode="w") as zipf:
zipf.writestr("foo.txt", b"O, for a Muse of Fire!")
zipfiledata = fp.getvalue()
# - Now create copies of it missing the last N bytes and make sure
# a BadZipFile exception is raised when we try to open it
for N in range(len(zipfiledata)):
fp = io.BytesIO(zipfiledata[:N])
self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, fp)
def test_is_zip_valid_file(self): def test_is_zip_valid_file(self):
"""Check that is_zipfile() correctly identifies zip files.""" """Check that is_zipfile() correctly identifies zip files."""
# - passing a filename # - passing a filename
......
...@@ -176,6 +176,8 @@ def _EndRecData64(fpin, offset, endrec): ...@@ -176,6 +176,8 @@ def _EndRecData64(fpin, offset, endrec):
return endrec return endrec
data = fpin.read(sizeEndCentDir64Locator) data = fpin.read(sizeEndCentDir64Locator)
if len(data) != sizeEndCentDir64Locator:
return endrec
sig, diskno, reloff, disks = struct.unpack(structEndArchive64Locator, data) sig, diskno, reloff, disks = struct.unpack(structEndArchive64Locator, data)
if sig != stringEndArchive64Locator: if sig != stringEndArchive64Locator:
return endrec return endrec
...@@ -186,6 +188,8 @@ def _EndRecData64(fpin, offset, endrec): ...@@ -186,6 +188,8 @@ def _EndRecData64(fpin, offset, endrec):
# Assume no 'zip64 extensible data' # Assume no 'zip64 extensible data'
fpin.seek(offset - sizeEndCentDir64Locator - sizeEndCentDir64, 2) fpin.seek(offset - sizeEndCentDir64Locator - sizeEndCentDir64, 2)
data = fpin.read(sizeEndCentDir64) data = fpin.read(sizeEndCentDir64)
if len(data) != sizeEndCentDir64:
return endrec
sig, sz, create_version, read_version, disk_num, disk_dir, \ sig, sz, create_version, read_version, disk_num, disk_dir, \
dircount, dircount2, dirsize, diroffset = \ dircount, dircount2, dirsize, diroffset = \
struct.unpack(structEndArchive64, data) struct.unpack(structEndArchive64, data)
...@@ -221,7 +225,9 @@ def _EndRecData(fpin): ...@@ -221,7 +225,9 @@ def _EndRecData(fpin):
except IOError: except IOError:
return None return None
data = fpin.read() data = fpin.read()
if data[0:4] == stringEndArchive and data[-2:] == b"\000\000": if (len(data) == sizeEndCentDir and
data[0:4] == stringEndArchive and
data[-2:] == b"\000\000"):
# the signature is correct and there's no comment, unpack structure # the signature is correct and there's no comment, unpack structure
endrec = struct.unpack(structEndArchive, data) endrec = struct.unpack(structEndArchive, data)
endrec=list(endrec) endrec=list(endrec)
...@@ -245,6 +251,9 @@ def _EndRecData(fpin): ...@@ -245,6 +251,9 @@ def _EndRecData(fpin):
if start >= 0: if start >= 0:
# found the magic number; attempt to unpack and interpret # found the magic number; attempt to unpack and interpret
recData = data[start:start+sizeEndCentDir] recData = data[start:start+sizeEndCentDir]
if len(recData) != sizeEndCentDir:
# Zip file is corrupted.
return None
endrec = list(struct.unpack(structEndArchive, recData)) endrec = list(struct.unpack(structEndArchive, recData))
commentSize = endrec[_ECD_COMMENT_SIZE] #as claimed by the zip file commentSize = endrec[_ECD_COMMENT_SIZE] #as claimed by the zip file
comment = data[start+sizeEndCentDir:start+sizeEndCentDir+commentSize] comment = data[start+sizeEndCentDir:start+sizeEndCentDir+commentSize]
...@@ -256,7 +265,7 @@ def _EndRecData(fpin): ...@@ -256,7 +265,7 @@ def _EndRecData(fpin):
endrec) endrec)
# Unable to find a valid end of central directory structure # Unable to find a valid end of central directory structure
return return None
class ZipInfo (object): class ZipInfo (object):
...@@ -819,9 +828,11 @@ class ZipFile: ...@@ -819,9 +828,11 @@ class ZipFile:
total = 0 total = 0
while total < size_cd: while total < size_cd:
centdir = fp.read(sizeCentralDir) centdir = fp.read(sizeCentralDir)
if centdir[0:4] != stringCentralDir: if len(centdir) != sizeCentralDir:
raise BadZipFile("Bad magic number for central directory") raise BadZipFile("Truncated central directory")
centdir = struct.unpack(structCentralDir, centdir) centdir = struct.unpack(structCentralDir, centdir)
if centdir[_CD_SIGNATURE] != stringCentralDir:
raise BadZipFile("Bad magic number for central directory")
if self.debug > 2: if self.debug > 2:
print(centdir) print(centdir)
filename = fp.read(centdir[_CD_FILENAME_LENGTH]) filename = fp.read(centdir[_CD_FILENAME_LENGTH])
...@@ -964,10 +975,12 @@ class ZipFile: ...@@ -964,10 +975,12 @@ class ZipFile:
# Skip the file header: # Skip the file header:
fheader = zef_file.read(sizeFileHeader) fheader = zef_file.read(sizeFileHeader)
if fheader[0:4] != stringFileHeader: if len(fheader) != sizeFileHeader:
raise BadZipFile("Truncated file header")
fheader = struct.unpack(structFileHeader, fheader)
if fheader[_FH_SIGNATURE] != stringFileHeader:
raise BadZipFile("Bad magic number for file header") raise BadZipFile("Bad magic number for file header")
fheader = struct.unpack(structFileHeader, fheader)
fname = zef_file.read(fheader[_FH_FILENAME_LENGTH]) fname = zef_file.read(fheader[_FH_FILENAME_LENGTH])
if fheader[_FH_EXTRA_FIELD_LENGTH]: if fheader[_FH_EXTRA_FIELD_LENGTH]:
zef_file.read(fheader[_FH_EXTRA_FIELD_LENGTH]) zef_file.read(fheader[_FH_EXTRA_FIELD_LENGTH])
......
...@@ -216,6 +216,10 @@ Core and Builtins ...@@ -216,6 +216,10 @@ Core and Builtins
Library Library
------- -------
- Issue #4844: ZipFile now raises BadZipFile when opens a ZIP file with an
incomplete "End of Central Directory" record. Original patch by Guilherme
Polo and Alan McIntyre.
- Issue #15505: `unittest.installHandler` no longer assumes SIGINT handler is - Issue #15505: `unittest.installHandler` no longer assumes SIGINT handler is
set to a callable object. set to a callable object.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment