From d1199559acf4adf9cc7afaf19c0737f20d744604 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Thu, 22 Oct 2026 05:23:31 +0000 Subject: [PATCH] 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. --- Lib/test/test_zipfile/test_core.py | 14 ++++++++++++++ Lib/zipfile/__init__.py | 5 +++-- .../2026-10-22-05-21-00.gh-issue-161094.MDjCNm.rst | 4 ++++ 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-10-22-05-21-00.gh-issue-161094.MDjCNm.rst diff --git a/Lib/test/test_zipfile/test_core.py b/Lib/test/test_zipfile/test_core.py index ab1995e91286e6..84000f472c4df9 100644 --- a/Lib/test/test_zipfile/test_core.py +++ b/Lib/test/test_zipfile/test_core.py @@ -4300,6 +4300,20 @@ def test_is_zip_valid_file(self): self.assertTrue(zipfile.is_zipfile(fp)) self.assertEqual(fp.tell(), mid) + def test_is_zip_bad_central_directory_offset(self): + """Check that is_zipfile() rejects a central directory before the file.""" + fp = io.BytesIO() + with zipfile.ZipFile(fp, mode="w") as zipf: + zipf.writestr("foo.txt", b"O, for a Muse of Fire!") + data = bytearray(fp.getvalue()) + endrec = len(data) - zipfile.sizeEndCentDir + struct.pack_into("= sizeCentralDir: + start_dir = sum(_handle_prepended_data(endrec)) + if start_dir >= 0 and endrec[_ECD_SIZE] >= sizeCentralDir: + fp.seek(start_dir) data = fp.read(sizeCentralDir) # CD is where we expect it to be if len(data) == sizeCentralDir: centdir = struct.unpack(structCentralDir, data) # CD is the right size diff --git a/Misc/NEWS.d/next/Library/2026-10-22-05-21-00.gh-issue-161094.MDjCNm.rst b/Misc/NEWS.d/next/Library/2026-10-22-05-21-00.gh-issue-161094.MDjCNm.rst new file mode 100644 index 00000000000000..716da00504cc59 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-10-22-05-21-00.gh-issue-161094.MDjCNm.rst @@ -0,0 +1,4 @@ +Fix :func:`zipfile.is_zipfile` raising :exc:`ValueError` instead of returning +``False`` for a file-like object, such as :class:`io.BytesIO`, whose end of +central directory record places the central directory before the start of the +file.