Skip to content

Commit 583d7b6

Browse files
authored
[3.14] gh-161094: Fix zipfile.is_zipfile() raising ValueError on file-like objects (GH-161095) (#161121)
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. (cherry picked from commit 2789841)
1 parent 5f92e38 commit 583d7b6

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
@@ -2132,6 +2132,20 @@ def test_is_zip_valid_file(self):
21322132
self.assertTrue(zipfile.is_zipfile(fp))
21332133
self.assertEqual(fp.tell(), mid)
21342134

2135+
def test_is_zip_bad_central_directory_offset(self):
2136+
"""Check that is_zipfile() rejects a central directory before the file."""
2137+
fp = io.BytesIO()
2138+
with zipfile.ZipFile(fp, mode="w") as zipf:
2139+
zipf.writestr("foo.txt", b"O, for a Muse of Fire!")
2140+
data = bytearray(fp.getvalue())
2141+
endrec = len(data) - zipfile.sizeEndCentDir
2142+
struct.pack_into("<L", data, endrec + 12, len(data))
2143+
fp = io.BytesIO(data)
2144+
pos = fp.seek(5)
2145+
self.assertFalse(zipfile.is_zipfile(fp))
2146+
self.assertEqual(fp.tell(), pos)
2147+
self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, io.BytesIO(data))
2148+
21352149
def test_non_existent_file_raises_OSError(self):
21362150
# make sure we don't raise an AttributeError when a partially-constructed
21372151
# 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)