{"id":36223733,"url":"https://github.com/bug-ops/exarch","last_synced_at":"2026-02-06T12:44:44.194Z","repository":{"id":331157897,"uuid":"1125505218","full_name":"bug-ops/exarch","owner":"bug-ops","description":"Secure archive library: TAR/ZIP/7z extraction \u0026 creation with CVE protection. Type-safe Rust core, Python/Node.js bindings, zero unsafe code.","archived":false,"fork":false,"pushed_at":"2026-01-03T18:50:49.000Z","size":561,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-03T20:26:28.620Z","etag":null,"topics":["7z","archive","cli","compression","cve-protection","extraction","library","memory-safe","napi-rs","nodejs","path-traversal","pyo3","python","rust","security","tar","zip","zip-bomb"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bug-ops.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-30T21:12:05.000Z","updated_at":"2026-01-03T18:50:52.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/bug-ops/exarch","commit_stats":null,"previous_names":["bug-ops/exarch"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/bug-ops/exarch","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bug-ops%2Fexarch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bug-ops%2Fexarch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bug-ops%2Fexarch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bug-ops%2Fexarch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bug-ops","download_url":"https://codeload.github.com/bug-ops/exarch/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bug-ops%2Fexarch/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28287012,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-11T04:44:51.577Z","status":"ssl_error","status_checked_at":"2026-01-11T04:44:44.232Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["7z","archive","cli","compression","cve-protection","extraction","library","memory-safe","napi-rs","nodejs","path-traversal","pyo3","python","rust","security","tar","zip","zip-bomb"],"created_at":"2026-01-11T05:02:35.516Z","updated_at":"2026-01-11T05:02:35.684Z","avatar_url":"https://github.com/bug-ops.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# exarch\n\n[![CI](https://img.shields.io/github/actions/workflow/status/bug-ops/exarch/ci.yml?branch=main)](https://github.com/bug-ops/exarch/actions)\n[![codecov](https://codecov.io/gh/bug-ops/exarch/graph/badge.svg?token=AKF1TLTVCA)](https://codecov.io/gh/bug-ops/exarch)\n[![crates.io](https://img.shields.io/crates/v/exarch-core)](https://crates.io/crates/exarch-core)\n[![docs.rs](https://img.shields.io/docsrs/exarch-core)](https://docs.rs/exarch-core)\n[![PyPI](https://img.shields.io/pypi/v/exarch)](https://pypi.org/project/exarch)\n[![npm](https://img.shields.io/npm/v/exarch-rs)](https://www.npmjs.com/package/exarch-rs)\n[![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue)](LICENSE-MIT)\n[![MSRV](https://img.shields.io/badge/MSRV-1.89.0-blue)](https://github.com/bug-ops/exarch)\n\nMemory-safe archive extraction and creation library with Python and Node.js bindings.\n\n\u003e [!IMPORTANT]\n\u003e **exarch** is designed as a secure replacement for vulnerable archive libraries like Python's `tarfile` and Node.js's `tar-fs`, which have known CVEs with CVSS scores up to 9.4.\n\n## Features\n\n- **Extract and create archives** — Full support for TAR and ZIP (extract and create), plus 7z extraction\n- **Security-first design** — Default-deny security model with protection against path traversal, symlink attacks, zip bombs, and more\n- **Type-driven safety** — Rust's type system ensures validated paths can only be constructed through security checks\n- **Multi-language support** — Native bindings for Python (PyO3) and Node.js (napi-rs)\n- **Zero unsafe code** — Core library contains no unsafe Rust code\n- **High performance** — Optimized I/O with reusable buffers and streaming operations\n\n## Installation\n\n### Rust\n\n```toml\n[dependencies]\nexarch-core = \"0.2\"\n```\n\n\u003e [!IMPORTANT]\n\u003e Requires Rust 1.89.0 or later (Edition 2024).\n\n### Python\n\n```bash\npip install exarch\n```\n\n\u003e [!NOTE]\n\u003e Requires Python 3.9 or later.\n\n### Node.js\n\n```bash\nnpm install exarch-rs\n```\n\n\u003e [!NOTE]\n\u003e Requires Node.js 18 or later.\n\n## Quick Start\n\n### Extraction\n\n#### Rust\n\n```rust\nuse exarch_core::{extract_archive, SecurityConfig};\n\nfn main() -\u003e Result\u003c(), exarch_core::ExtractionError\u003e {\n    let config = SecurityConfig::default();\n    let report = extract_archive(\"archive.tar.gz\", \"/output/path\", \u0026config)?;\n\n    println!(\"Extracted {} files ({} bytes)\",\n        report.files_extracted,\n        report.bytes_written);\n    Ok(())\n}\n```\n\n#### Python\n\n```python\nimport exarch\n\nresult = exarch.extract_archive(\"archive.tar.gz\", \"/output/path\")\nprint(f\"Extracted {result.files_extracted} files\")\n```\n\n#### Node.js\n\n```javascript\nconst { extractArchive } = require('exarch');\n\n// Async (recommended)\nconst result = await extractArchive('archive.tar.gz', '/output/path');\nconsole.log(`Extracted ${result.filesExtracted} files`);\n```\n\n### Creation\n\n#### Rust\n\n```rust\nuse exarch_core::{create_archive, creation::CreationConfig};\n\nfn main() -\u003e Result\u003c(), exarch_core::ExtractionError\u003e {\n    let config = CreationConfig::default();\n    let report = create_archive(\"output.tar.gz\", \u0026[\"src/\", \"Cargo.toml\"], \u0026config)?;\n\n    println!(\"Created archive with {} files ({} bytes)\",\n        report.files_added,\n        report.bytes_written);\n    Ok(())\n}\n```\n\n#### Python\n\n```python\nimport exarch\n\nresult = exarch.create_archive(\"output.tar.gz\", [\"src/\", \"Cargo.toml\"])\nprint(f\"Created archive with {result.files_added} files\")\n```\n\n#### Node.js\n\n```javascript\nconst { createArchive } = require('exarch');\n\n// Async (recommended)\nconst result = await createArchive('output.tar.gz', ['src/', 'package.json']);\nconsole.log(`Created archive with ${result.filesAdded} files`);\n```\n\n## Security\n\nexarch provides defense-in-depth protection against common archive vulnerabilities:\n\n| Protection | Description | Default |\n|------------|-------------|---------|\n| Path traversal | Blocks `../` and absolute paths | Enabled |\n| Symlink attacks | Prevents symlinks escaping extraction directory | Blocked |\n| Hardlink attacks | Validates hardlink targets within extraction directory | Blocked |\n| Zip bombs | Detects high compression ratios | Enabled (100x limit) |\n| Permission sanitization | Strips setuid/setgid bits | Enabled |\n| Size limits | Configurable file and total size limits | 50 MB / 10 GB |\n\n\u003e [!CAUTION]\n\u003e Enabling symlinks or hardlinks should only be done when you fully trust the archive source.\n\n### Security Configuration\n\n```rust\nuse exarch_core::SecurityConfig;\n\nlet config = SecurityConfig {\n    max_file_size: 100 * 1024 * 1024,   // 100 MB\n    max_total_size: 1024 * 1024 * 1024, // 1 GB\n    max_compression_ratio: 50.0,         // 50x compression limit\n    ..Default::default()\n};\n```\n\n## Supported Formats\n\n| Format | Extensions | Extract | Create | Compression |\n|--------|------------|:-------:|:------:|-------------|\n| TAR | `.tar` | ✅ | ✅ | None |\n| TAR+GZIP | `.tar.gz`, `.tgz` | ✅ | ✅ | gzip |\n| TAR+BZIP2 | `.tar.bz2`, `.tbz2` | ✅ | ✅ | bzip2 |\n| TAR+XZ | `.tar.xz`, `.txz` | ✅ | ✅ | xz/lzma |\n| TAR+ZSTD | `.tar.zst`, `.tzst` | ✅ | ✅ | zstandard |\n| ZIP | `.zip` | ✅ | ✅ | deflate, deflate64, bzip2, zstd |\n| 7z | `.7z` | ✅ | — | lzma, lzma2 |\n\n\u003e [!NOTE]\n\u003e 7z creation is not yet supported. Solid and encrypted 7z archives are rejected for security reasons.\n\n## Project Structure\n\n```\nexarch/\n├── crates/\n│   ├── exarch-core/     # Core Rust library\n│   ├── exarch-cli/      # Command-line utility\n│   ├── exarch-python/   # Python bindings (PyO3)\n│   └── exarch-node/     # Node.js bindings (napi-rs)\n├── benches/             # Criterion benchmarks\n├── examples/            # Usage examples\n└── tests/               # Integration tests\n```\n\n## Performance\n\nexarch uses optimized I/O with directory caching and atomic permission setting to outperform native archive libraries:\n\n| Comparison | Average Speedup | Max Speedup |\n|------------|-----------------|-------------|\n| vs Python tarfile/zipfile | **1.10x** faster | 1.43x |\n| vs Node.js tar/adm-zip | **1.75x** faster | 4.69x |\n\n### Throughput (100MB archives)\n\n| Format | Throughput | vs Target |\n|--------|------------|-----------|\n| TAR extraction | 2,136 MB/s | **4x** target (500 MB/s) |\n| ZIP extraction | 1,444 MB/s | **5x** target (300 MB/s) |\n| Path validation | ~85 ns | **12x** better than 1 µs target |\n\n\u003e [!TIP]\n\u003e Run `./benches/run_all.sh` to benchmark on your hardware. See [benches/README.md](benches/README.md) for details.\n\n## Development\n\n### Requirements\n\n- Rust 1.89.0 or later (Edition 2024)\n- Python 3.9+ (for Python bindings)\n- Node.js 18+ (for Node.js bindings)\n\n### Build\n\n```bash\ncargo build --workspace\n```\n\n### Test\n\n```bash\ncargo nextest run --workspace\n```\n\n### Pre-commit Checks\n\n```bash\ncargo +nightly fmt --all -- --check\ncargo clippy --workspace --all-targets -- -D warnings\nRUSTDOCFLAGS=\"-D warnings\" cargo doc --no-deps --workspace\ncargo deny check\n```\n\n\u003e [!TIP]\n\u003e Run all checks before committing to ensure CI passes.\n\n## Contributing\n\nContributions are welcome! Please read our contributing guidelines before submitting PRs.\n\n## License\n\nLicensed under either of:\n\n- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE))\n- MIT License ([LICENSE-MIT](LICENSE-MIT))\n\nat your option.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbug-ops%2Fexarch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbug-ops%2Fexarch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbug-ops%2Fexarch/lists"}