Skip to content

Commit d119955

Browse files
committed
gh-161094: Fix zipfile.is_zipfile() ValueError on file-like objects
Return False instead of seeking to a negative offset when the end of central directory record places the central directory before the start of the file.
1 parent 9c0cfb1 commit d119955

3 files changed

Lines changed: 21 additions & 2 deletions

File tree

‎Lib/test/test_zipfile/test_core.py‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4300,6 +4300,20 @@ def test_is_zip_valid_file(self):
43004300
self.assertTrue(zipfile.is_zipfile(fp))
43014301
self.assertEqual(fp.tell(), mid)
43024302

4303+
def test_is_zip_bad_central_directory_offset(self):
4304+
"""Check that is_zipfile() rejects a central directory before the file."""
4305+
fp = io.BytesIO()
4306+
with zipfile.ZipFile(fp, mode="w") as zipf:
4307+
zipf.writestr("foo.txt", b"O, for a Muse of Fire!")
4308+
data = bytearray(fp.getvalue())
4309+
endrec = len(data) - zipfile.sizeEndCentDir
4310+
struct.pack_into("<L", data, endrec + 12, len(data))
4311+
fp = io.BytesIO(data)
4312+
pos = fp.seek(5)
4313+
self.assertFalse(zipfile.is_zipfile(fp))
4314+
self.assertEqual(fp.tell(), pos)
4315+
self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, io.BytesIO(data))
4316+
43034317
def test_non_existent_file_raises_OSError(self):
43044318
# make sure we don't raise an AttributeError when a partially-constructed
43054319
# ZipFile instance is finalized; this tests for regression on SF tracker

‎Lib/zipfile/__init__.py‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,8 +240,9 @@ def _check_zipfile(fp):
240240
return True # Empty zipfiles are still zipfiles
241241
elif endrec[_ECD_DISK_NUMBER] == endrec[_ECD_DISK_START]:
242242
# Central directory is on the same disk
243-
fp.seek(sum(_handle_prepended_data(endrec)))
244-
if endrec[_ECD_SIZE] >= sizeCentralDir:
243+
start_dir = sum(_handle_prepended_data(endrec))
244+
if start_dir >= 0 and endrec[_ECD_SIZE] >= sizeCentralDir:
245+
fp.seek(start_dir)
245246
data = fp.read(sizeCentralDir) # CD is where we expect it to be
246247
if len(data) == sizeCentralDir:
247248
centdir = struct.unpack(structCentralDir, data) # CD is the right size
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fix :func:`zipfile.is_zipfile` raising :exc:`ValueError` instead of returning
2+
``False`` for a file-like object, such as :class:`io.BytesIO`, whose end of
3+
central directory record places the central directory before the start of the
4+
file.

0 commit comments

Comments
 (0)