Security

zipwire fetches data from remote servers and parses binary ZIP structures. When processing archives from untrusted sources, keep the following risks in mind.

Zip bombs

A zip bomb is a malicious archive containing entries that decompress to a vastly larger size than their compressed representation, potentially exhausting memory or disk space.

Both read() and read_into() (and their async equivalents) accept a max_file_size keyword argument that limits the uncompressed size of a single entry:

from zipwire import SyncRemoteZip
from zipwire.backends import Urllib3Reader

reader = Urllib3Reader("https://example.com/untrusted.zip")
with SyncRemoteZip(reader) as rz:
    for info in rz.infolist():
        data = rz.read(info, max_file_size=50 * 1024 * 1024)  # 50 MiB

When the limit is exceeded, FileTooLarge is raised.

Two layers of protection:

  1. Pre-check – before decompression starts, the entry’s declared file_size from the central directory is compared against the limit. This catches honest zip bombs with no overhead.

  2. Streaming enforcement (read_into() only) – the streaming decompressor tracks the actual decompressed output and aborts if it exceeds the limit. This catches crafted archives that lie about their uncompressed size in the metadata.

read() performs only the pre-check (layer 1). Because it decompresses the entire entry into memory in one step, it cannot enforce the limit during decompression. Use read_into() when processing untrusted archives for defence in depth.

You can also inspect entries before extracting them:

MAX_SIZE = 100 * 1024 * 1024  # 100 MiB

with SyncRemoteZip(reader) as rz:
    for info in rz.infolist():
        if info.file_size > MAX_SIZE:
            print(f"Skipping {info.filename}: too large ({info.file_size} bytes)")
            continue
        data = rz.read(info)

Path traversal

ZIP archives can contain entries with absolute paths or ../ components (e.g. ../../etc/passwd). zipwire does not extract files to disk, so this is not a direct risk. However, if you use filenames from the archive to construct file paths, always sanitise them:

import os

with SyncRemoteZip(reader) as rz:
    for info in rz.infolist():
        # Reject absolute paths and path traversal
        if os.path.isabs(info.filename) or ".." in info.filename.split("/"):
            print(f"Skipping suspicious path: {info.filename}")
            continue
        dest = os.path.join(output_dir, info.filename)
        # Verify the resolved path is inside output_dir
        if not os.path.realpath(dest).startswith(os.path.realpath(output_dir)):
            print(f"Skipping path traversal: {info.filename}")
            continue

Server trust

zipwire sends HTTP requests to a URL you provide. The server controls what data is returned. A malicious server could return crafted responses designed to exploit ZIP parsing bugs, cause excessive memory use, or return different data for different range requests.

  • Only fetch archives from servers you trust.

  • Use HTTPS to prevent tampering in transit.

  • Consider setting max_file_size even for trusted servers as a safety net.