diff --git a/src/inspect_ai/_util/async_zip.py b/src/inspect_ai/_util/async_zip.py index 40d700dd99..96a058870c 100644 --- a/src/inspect_ai/_util/async_zip.py +++ b/src/inspect_ai/_util/async_zip.py @@ -21,6 +21,10 @@ # Default chunk size for streaming compressed data (1MB) DEFAULT_CHUNK_SIZE = 1024 * 1024 +# Maximum archive comment length and minimum EOCD record size per the ZIP format +_MAX_ZIP_COMMENT_SIZE = (1 << 16) - 1 +_MIN_EOCD_SIZE = 22 + @dataclass class CentralDirectoryLocation: @@ -69,7 +73,9 @@ async def _find_central_directory( Raises: ValueError: If EOCD signature not found or ZIP64 structure is corrupt """ - suffix = await filesystem.read_file_suffix(filename, 65536) + suffix = await filesystem.read_file_suffix( + filename, _MAX_ZIP_COMMENT_SIZE + _MIN_EOCD_SIZE + ) tail = suffix.data tail_start = suffix.file_size - len(tail) diff --git a/tests/util/test_async_zip.py b/tests/util/test_async_zip.py index 6c0e785890..3658b2b3db 100644 --- a/tests/util/test_async_zip.py +++ b/tests/util/test_async_zip.py @@ -55,6 +55,25 @@ async def test_read_local_zip_member(test_zip_file: Path) -> None: assert parsed["message"] == "hello world" +async def test_read_zip_with_maximum_comment(tmp_path: Path) -> None: + """Test reading a ZIP file with the maximum-length archive comment.""" + zip_path = tmp_path / "max_comment.zip" + content = b"payload" + + with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf: + zf.writestr("member.txt", content) + zf.comment = b"x" * 65535 + + with zipfile.ZipFile(zip_path) as zf: + assert zf.read("member.txt") == content + + async with AsyncFilesystem() as fs: + reader = AsyncZipReader(fs, str(zip_path)) + entries = await reader.entries() + assert [entry.filename for entry in entries.entries] == ["member.txt"] + assert await reader.read_member_fully("member.txt") == content + + async def test_read_nested_member(test_zip_file: Path) -> None: """Test reading a nested member from a local ZIP file.""" zip_path = str(test_zip_file)