exarch
Memory-safe archive extraction and creation library for Python.
Important: exarch is designed as a secure replacement for vulnerable archive libraries like Python's tarfile, which has known CVEs with CVSS scores up to 9.4.
This package provides Python bindings for exarch-core, a Rust library with built-in protection against common archive vulnerabilities.
Installation
pip install exarch
Tip: Use uv pip install exarch for faster installation.
Alternative Package Managers
# Poetry
poetry add exarch
# Pipenv
pipenv install exarch
Requirements
- Python >= 3.10
Quick Start
Extraction
import exarch
result = exarch.extract_archive("archive.tar.gz", "/output/path")
print(f"Extracted {result.files_extracted} files")
Creation
import exarch
result = exarch.create_archive("backup.tar.gz", ["src/", "Cargo.toml"])
print(f"Created archive with {result.files_added} files")
Usage
Basic Extraction
import exarch
result = exarch.extract_archive("archive.tar.gz", "/output/path")
print(f"Files extracted: {result.files_extracted}")
print(f"Bytes written: {result.bytes_written}")
print(f"Duration: {result.duration_ms}ms")
With pathlib.Path
from pathlib import Path
import exarch
archive = Path("archive.tar.gz")
output = Path("/output/path")
result = exarch.extract_archive(archive, output)
Custom Security Configuration
import exarch
config = exarch.SecurityConfig()
config = config.with_max_file_size(100 * 1024 * 1024) # 100 MB
result = exarch.extract_archive("archive.tar.gz", "/output", config)
Error Handling
import exarch
try:
result = exarch.extract_archive("archive.tar.gz", "/output")
print(f"Extracted {result.files_extracted} files")
except exarch.PathTraversalError as e:
print(f"Blocked path traversal: {e}")
except exarch.ZipBombError as e:
print(f"Zip bomb detected: {e}")
except exarch.SecurityViolationError as e:
print(f"Security violation: {e}")
except exarch.ArchiveError as e:
print(f"Extraction failed: {e}")
API Reference
extract_archive(archive_path, output_dir, config=None)
Extract an archive to the specified directory with security validation.
Parameters:
| Name | Type | Description |
|---|---|---|
archive_path |
str | Path |
Path to the archive file |
output_dir |
str | Path |
Directory where files will be extracted |
config |
SecurityConfig |
Optional security configuration |
Returns: ExtractionReport
| Attribute | Type | Description |
|---|---|---|
files_extracted |
int |
Number of files extracted |
directories_created |
int |
Number of directories created |
symlinks_created |
int |
Number of symlinks created |
bytes_written |
int |
Total bytes written |
duration_ms |
int |
Extraction duration in milliseconds |
files_skipped |
int |
Number of files skipped (e.g. duplicates) |
warnings |
list[str] |
Warning messages generated during extraction |
Raises:
| Exception | Description |
|---|---|
PathTraversalError |
Path traversal attempt detected |
SymlinkEscapeError |
Symlink points outside extraction directory |
HardlinkEscapeError |
Hardlink target outside extraction directory |
ZipBombError |
Potential zip bomb detected |
QuotaExceededError |
Resource quota exceeded |
SecurityViolationError |
Security policy violation |
UnsupportedFormatError |
Archive format not supported |
UnknownFormatError |
Archive format cannot be determined from path or magic bytes (subclass of UnsupportedFormatError) |
InvalidArchiveError |
Archive is corrupted |
IOError |
I/O operation failed |
Note: Since v0.4.0, create_archive raises FileNotFoundError for missing sources, FileExistsError when the output already exists without overwrite, and ValueError for invalid compression levels — matching standard Python conventions.
extract_archive_with_progress(archive_path, output_dir, config, progress)
Extract an archive with a progress callback. The GIL is held when a callback is provided and released otherwise.
Parameters:
| Name | Type | Description |
|---|---|---|
archive_path |
str | Path |
Path to the archive file |
output_dir |
str | Path |
Directory where files will be extracted |
config |
SecurityConfig | None |
Optional security configuration |
progress |
Callable[[str, int, int, int], None] | None |
Optional progress callback: (path, total_files, current_file, bytes_written) |
import exarch
def on_progress(path: str, total: int, current: int, bytes_written: int) -> None:
print(f"[{current}/{total}] {path} ({bytes_written} bytes)")
result = exarch.extract_archive_with_progress(
"archive.tar.gz", "/output", config=None, progress=on_progress
)
If progress raises: extraction is not aborted early — the progress-callback contract has no cancellation signal, so extraction always runs to completion first, and progress is not called again for the remaining entries once it has raised. If extraction otherwise succeeded, progress's own exception propagates unchanged, with files_extracted/bytes_written attributes describing what was written and a progress_callback_error = True marker attribute (check this marker before treating the presence of files_extracted as a partial-extraction signal, since a genuine partial-extraction failure carries the same two attribute names). If extraction also failed, the extraction error takes priority — a raising progress can never mask a security error such as SymlinkEscapeError — and progress's exception is attached as __cause__ instead of being dropped. create_archive_with_progress behaves the same way, using files_added in place of files_extracted.
SecurityConfig
Builder-style security configuration.
config = exarch.SecurityConfig()
config = config.with_max_file_size(100 * 1024 * 1024) # 100 MB per file
config = config.with_max_total_size(1024 * 1024 * 1024) # 1 GB total
config = config.with_max_file_count(10_000) # Max 10k files
config = config.with_max_compression_ratio(50.0) # Zip bomb threshold
config = config.add_allowed_extension(".txt") # Extension allowlist
config = config.add_allowed_extension(".md")
config = config.add_banned_component("__MACOSX") # Skip components
config = config.with_allow_solid_archives(True) # Allow solid 7z archives
Security Features
The library provides built-in protection against:
| Protection | Description |
|---|---|
| Path traversal | Blocks ../ and absolute paths |
| Symlink attacks | Prevents symlinks escaping extraction directory |
| Hardlink attacks | Validates hardlink targets |
| Zip bombs | Detects high compression ratios |
| TAR metadata bombs | Bounds GNU long-name/long-link and PAX header record reads |
| Permission sanitization | Strips setuid/setgid bits |
| Size limits | Enforces file and total size limits |
Caution: Unlike Python's standard tarfile module, exarch applies security validation by default.
Supported Formats
| Format | Extensions | Extract | Create | List | Verify |
|---|---|---|---|---|---|
| TAR | .tar |
✅ | ✅ | ✅ | ✅ |
| TAR+GZIP | .tar.gz, .tgz |
✅ | ✅ | ✅ | ✅ |
| TAR+BZIP2 | .tar.bz2, .tbz2 |
✅ | ✅ | ✅ | ✅ |
| TAR+XZ | .tar.xz, .txz |
✅ | ✅ | ✅ | ✅ |
| TAR+ZSTD | .tar.zst, .tzst |
✅ | ✅ | ✅ | ✅ |
| ZIP | .zip |
✅ | ✅ | ✅ | ✅ |
| ZIP-family | .jar, .war, .ear, .nar, .nbm, .apk, .aab, .ipa, .appx, .msix, .whl, .vsix, .xpi, .epub |
✅ | — | ✅ | ✅ |
| 7z | .7z |
✅ | — | ✅ | ✅ |
Note: ZIP-family formats share the ZIP container but add extra structure (signing, checksum manifests, ordering rules) that exarch doesn't produce, so creation is rejected for those extensions. 7z creation is not yet supported. Solid and encrypted 7z archives are rejected for security reasons. Unix symlinks inside 7z archives are reported as regular files (sevenz-rust2 API limitation).
Comparison with tarfile
# UNSAFE - tarfile has known vulnerabilities (CVE-2007-4559)
import tarfile
with tarfile.open("archive.tar.gz") as tar:
tar.extractall("/output") # May extract outside target directory!
# SAFE - exarch validates all paths
import exarch
exarch.extract_archive("archive.tar.gz", "/output") # Protected by default
Development
This package is built using PyO3 and maturin.
# Clone repository
git clone https://github.com/bug-ops/exarch
cd exarch/crates/exarch-python
# Build with maturin
pip install maturin
maturin develop
# Run tests
pytest tests/
Related Packages
- exarch-core — Core Rust library
- exarch (npm) — Node.js bindings
License
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT License (LICENSE-MIT)
at your option.
Metadata
Release files for exarch 0.6.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| exarch-0.6.1-cp39-abi3-win_amd64.whl | CPython 3.9 | abi3 | Windows x86-64 | Details |
| exarch-0.6.1-cp39-abi3-musllinux_1_2_x86_64.whl | CPython 3.9 | abi3 | Linux musl 1.2+ x86-64 | Details |
| exarch-0.6.1-cp39-abi3-musllinux_1_2_aarch64.whl | CPython 3.9 | abi3 | Linux musl 1.2+ ARM64 | Details |
| exarch-0.6.1-cp39-abi3-manylinux_2_34_x86_64.whl | CPython 3.9 | abi3 | Linux glibc 2.34+ x86-64 | Details |
| exarch-0.6.1-cp39-abi3-manylinux_2_34_aarch64.whl | CPython 3.9 | abi3 | Linux glibc 2.34+ ARM64 | Details |
| exarch-0.6.1-cp39-abi3-macosx_11_0_arm64.whl | CPython 3.9 | abi3 | macOS 11.0+ ARM64 | Details |
| exarch-0.6.1-cp39-abi3-macosx_10_12_x86_64.whl | CPython 3.9 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 10.0 MB
Release files / exarch-0.6.1-cp39-abi3-win_amd64.whl
| Download URL | exarch-0.6.1-cp39-abi3-win_amd64.whl |
|---|---|
| Size | 1.2 MB |
| Tags | CPython 3.9 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
0ba6a12ff8a3cf4a6edd2e6eddefd6993b06a570fc479da9a761c8d3ad5b15c1
|
|
BLAKE2b-256 checksum How to use checksums |
1a6a11c0d08b30fa3756947fb99afd173b738ff667758c21e160e041bea67a82
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 1, 2026.
Transparency logRelease files / exarch-0.6.1-cp39-abi3-musllinux_1_2_x86_64.whl
| Download URL | exarch-0.6.1-cp39-abi3-musllinux_1_2_x86_64.whl |
|---|---|
| Size | 1.7 MB |
| Tags | CPython 3.9 Linux musl 1.2+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
40cccffc6336b1ba492f0dedf0e6dce46e54410e0341456c99ba191d6021ee01
|
|
BLAKE2b-256 checksum How to use checksums |
3d2c9c594f7cf1cd737743756090ee78bf990f18c881e8892ed3cfc10c8f242b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 1, 2026.
Transparency logRelease files / exarch-0.6.1-cp39-abi3-musllinux_1_2_aarch64.whl
| Download URL | exarch-0.6.1-cp39-abi3-musllinux_1_2_aarch64.whl |
|---|---|
| Size | 1.6 MB |
| Tags | CPython 3.9 Linux musl 1.2+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
472eb4c5f89dc4695593de886314a01d47c54082b2661e1a46950b330ae7cfce
|
|
BLAKE2b-256 checksum How to use checksums |
6ae4bd79fd68e1d5af5376213386773bed8c39e80b5258d9f96af625ec7ecd09
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 1, 2026.
Transparency logRelease files / exarch-0.6.1-cp39-abi3-manylinux_2_34_x86_64.whl
| Download URL | exarch-0.6.1-cp39-abi3-manylinux_2_34_x86_64.whl |
|---|---|
| Size | 1.5 MB |
| Tags | CPython 3.9 Linux glibc 2.34+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
50d234f5ff6a15e744c139a4b2b93257f763734f17e72b0e33dd6cab4ab02dfe
|
|
BLAKE2b-256 checksum How to use checksums |
dba7f9f7e40c03666ba9b99af4cf71cf6dbc8935a7fae1e9e23292ef4bd00c62
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 1, 2026.
Transparency logRelease files / exarch-0.6.1-cp39-abi3-manylinux_2_34_aarch64.whl
| Download URL | exarch-0.6.1-cp39-abi3-manylinux_2_34_aarch64.whl |
|---|---|
| Size | 1.4 MB |
| Tags | CPython 3.9 Linux glibc 2.34+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
cb79da7d783d8f4decc636312923a12d9ee328f4e55608f7535528fb3e23e2c6
|
|
BLAKE2b-256 checksum How to use checksums |
103c943c559285c3d2b159a65ad05ee45d08f8101d405730cf5e71aa4e4ade81
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 1, 2026.
Transparency logRelease files / exarch-0.6.1-cp39-abi3-macosx_11_0_arm64.whl
| Download URL | exarch-0.6.1-cp39-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 1.2 MB |
| Tags | CPython 3.9 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
a4f0068e1ef0be21164ff288aaee587403536c0c136079482c33fd17f38d7804
|
|
BLAKE2b-256 checksum How to use checksums |
d594058faf4ee227eaee1a7dc86d2228fe5cbdc50c3d4b97656ecd0c66514196
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 1, 2026.
Transparency logRelease files / exarch-0.6.1-cp39-abi3-macosx_10_12_x86_64.whl
| Download URL | exarch-0.6.1-cp39-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 1.3 MB |
| Tags | CPython 3.9 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
43da71bf90d1dcc62e5fd3792cebb95715ae10c4b129242f2ca028deb23a0ce3
|
|
BLAKE2b-256 checksum How to use checksums |
1d48646e1b67c276ef4115a26b1178b963259693a205d3aa6b364fae8c0838df
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 1, 2026.
Transparency log