Skip to main content
SafeZip Logo

Hardened ZIP extraction for Python - secure by default.

PyPI Version Supported Python versions Build Status Documentation Status llms.txt - documentation for LLMs Ask DeepWiki MIT Coverage

safezip is a zero-dependency, hardened wrapper around Python’s zipfile module that defends against the most common ZIP-based attacks: ZipSlip path traversal, ZIP bombs, and malformed/crafted archives.

Features

  • ZipSlip protection - relative traversal (..), absolute Unix and Windows paths, Windows UNC paths, and Windows drive-relative paths are all blocked. Filenames are Unicode NFC-normalised before path resolution, which defeats combining-character disguises of .. components. Null bytes in filenames cause Python’s zipfile layer to truncate the name before safezip sees it; the Sandbox also checks for null bytes as defence-in-depth.

  • ZIP bomb protection - per-member and cumulative decompression ratio limits abort extraction before runaway decompression can exhaust disk or memory.

  • File size limits - per-member size is checked against the declared header value at open time (Guard phase) and again against actual decompressed bytes during streaming (Streamer phase). Total extraction size is enforced cumulatively across all members at stream time.

  • ZIP64 consistency checks - crafted archives with inconsistent ZIP64 extra fields are rejected before decompression begins.

  • Symlink policy - configurable: REJECT (default), IGNORE, or RESOLVE_INTERNAL (symlink entries are extracted as regular files; no OS symlink is created on disk).

  • Atomic writes - every member is written to a temporary file first; the destination is only created after all checks pass. No partial files are left on disk after a security abort.

  • Secure by default - all limits are active without any configuration.

  • Zero dependencies - standard library only.

  • Environment variable overrides - all limits (including symlink_policy) can be set via SAFEZIP_* environment variables for containerised deployments.

Prerequisites

Python 3.10 or later. No additional packages required.

Installation

With uv:

uv pip install safezip

Or with pip:

pip install safezip

Quick start

Drop-in replacement for the common zipfile extraction pattern:

from safezip import safe_extract

safe_extract("path/to/file.zip", "/var/files/extracted/")

Or use the SafeZipFile context manager for more control:

from safezip import SafeZipFile

with SafeZipFile("path/to/file.zip") as zf:
    print(zf.namelist())
    zf.extractall("/var/files/extracted/")

Custom limits

See the Default limits for reference.

from safezip import SafeZipFile, SymlinkPolicy

with SafeZipFile(
    "path/to/file.zip",
    max_file_size=100 * 1024 * 1024,      # 100 MiB per member (default: 1 GiB)
    max_total_size=500 * 1024 * 1024,     # 500 MiB total (default: 5 GiB)
    max_files=1_000,                      # (default: 10 000)
    max_per_member_ratio=50.0,            # (default: 200)
    max_total_ratio=50.0,                 # (default: 200)
    max_nesting_depth=1,                  # (default: 3)
    symlink_policy=SymlinkPolicy.IGNORE,  # (default: SymlinkPolicy.REJECT)
) as zf:
    zf.extractall("/var/files/extracted/")

Recursive extraction

When an archive contains nested .zip files, set recursive=True to descend into them automatically. All safety limits apply at every level. Each nested archive is extracted into a directory named after it (without the extension). Nested archive detection is content-based (not extension-based): the member is streamed to a temporary file, inspected with zipfile.is_zipfile(), and either recursed into (temp file used as input, then deleted) or renamed to its final destination as a plain file.

from safezip import SafeZipFile

# archive.zip
#   readme.txt
#   data.zip          ← will be descended into, not extracted as a blob
#     report.csv

with SafeZipFile("path/to/archive.zip", recursive=True, max_nesting_depth=3) as zf:
    zf.extractall("/var/files/extracted/")

# Result on disk:
#   /var/files/extracted/readme.txt
#   /var/files/extracted/data/report.csv

With max_nesting_depth=0, opening any nested archive raises NestingDepthError before extracting a single byte from it:

import pytest
from safezip import SafeZipFile, NestingDepthError

# archive.zip
#   readme.txt
#   data.zip          ← depth 1 exceeds max_nesting_depth=0 → NestingDepthError
#     report.csv

with pytest.raises(NestingDepthError):
    with SafeZipFile(
        "path/to/archive.zip", recursive=True, max_nesting_depth=0
    ) as zf:
        zf.extractall("/var/files/extracted/")

Security event monitoring

from safezip import SafeZipFile, SecurityEvent

def my_monitor(event: SecurityEvent) -> None:
    print(f"[safezip] {event.event_type} archive={event.archive_hash}")

with SafeZipFile("path/to/file.zip", on_security_event=my_monitor) as zf:
    zf.extractall("/var/files/extracted/")

Environment variable overrides

See the Default limits for reference.

All limits can be overridden without changing code:

export SAFEZIP_MAX_FILE_SIZE=104857600    # 100 MiB (default: 1 GiB)
export SAFEZIP_MAX_TOTAL_SIZE=524288000   # 500 MiB (default: 5 GiB)
export SAFEZIP_MAX_FILES=1000             # (default: 10 000)
export SAFEZIP_MAX_PER_MEMBER_RATIO=50    # (default: 200)
export SAFEZIP_MAX_TOTAL_RATIO=50         # (default: 200)
export SAFEZIP_MAX_NESTING_DEPTH=1        # (default: 3)
export SAFEZIP_SYMLINK_POLICY=ignore      # reject | ignore | resolve_internal (default: reject)

Default limits

Parameter

Default

max_file_size

1 GiB

max_total_size

5 GiB

max_files

10 000

max_per_member_ratio

200

max_total_ratio

200

max_nesting_depth

3

symlink_policy

REJECT

recursive

False

Testing

All tests run inside Docker to prevent accidental pollution of the host system:

make test

To test a specific Python version:

make test-env ENV=py312

Writing documentation

Keep the following hierarchy:

=====
title
=====

header
======

sub-header
----------

sub-sub-header
~~~~~~~~~~~~~~

sub-sub-sub-header
^^^^^^^^^^^^^^^^^^

sub-sub-sub-sub-header
++++++++++++++++++++++

sub-sub-sub-sub-sub-header
**************************

License

MIT

Support

For security issues contact me at the e-mail given in the Author section.

For overall issues, go to GitHub.

Author

Artur Barseghyan <artur.barseghyan@gmail.com>

Release files for safezip 0.1.7

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for safezip 0.1.7
File Size Uploaded
safezip-0.1.7.tar.gz 46.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for safezip 0.1.7
File Interpreter ABI Platform
safezip-0.1.7-py3-none-any.whl Python 3 none any Details

Total release size: 94.1 kB

Release files / safezip-0.1.7.tar.gz

Download URL safezip-0.1.7.tar.gz
Size 46.0 kB
Tags Source
SHA-256 checksum
How to use checksums
3309172db34f472177da3d70beb8cd2ec128f3b721038a4b859f6d3a7164eeaa
BLAKE2b-256 checksum
How to use checksums
f04b0d5c7b03b868b756faa5d239308ce962772a31b7edd5172b5cc0af20bac6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.11

Release files / safezip-0.1.7-py3-none-any.whl

Download URL safezip-0.1.7-py3-none-any.whl
Size 48.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3a4532cd1e2fdd71ea292ab6133ff3dd9ec01d4649bf8ced0c9e62b440776485
BLAKE2b-256 checksum
How to use checksums
7469eecf637249035cf92988f7f23ff4a04d15272b1bff8192c54209b460dc9f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.11

Release history Release notifications | RSS feed

This release

0.1.7 This release

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page