exarch-rs
v0.4.0
Published
Memory-safe archive extraction library with built-in security validation
Maintainers
Readme
exarch-rs
Memory-safe archive extraction and creation library for Node.js.
Important: exarch is designed as a secure replacement for vulnerable archive libraries like tar-fs, which has known CVEs with CVSS scores up to 9.4.
This package provides Node.js bindings for exarch-core, a Rust library with built-in protection against common archive vulnerabilities.
Installation
# npm
npm install exarch-rs
# yarn
yarn add exarch-rs
# pnpm
pnpm add exarch-rs
# bun
bun add exarch-rsNote: This package includes TypeScript definitions. No need for a separate @types package.
Requirements
- Node.js >= 18
Quick Start
Extraction
const { extractArchive } = require('exarch-rs');
// Async (recommended)
const result = await extractArchive('archive.tar.gz', '/output/path');
console.log(`Extracted ${result.filesExtracted} files`);Creation
const { createArchive } = require('exarch-rs');
// Async (recommended)
const result = await createArchive('backup.tar.gz', ['src/', 'package.json']);
console.log(`Created archive with ${result.filesAdded} files`);Usage
Async API (Recommended)
const { extractArchive } = require('exarch-rs');
const result = await extractArchive('archive.tar.gz', '/output/path');
console.log(`Files extracted: ${result.filesExtracted}`);
console.log(`Bytes written: ${result.bytesWritten}`);
console.log(`Duration: ${result.durationMs}ms`);Sync API
const { extractArchiveSync } = require('exarch-rs');
const result = extractArchiveSync('archive.tar.gz', '/output/path');
console.log(`Extracted ${result.filesExtracted} files`);Tip: Prefer the async API to avoid blocking the event loop during extraction.
ES Modules
import { extractArchive } from 'exarch-rs';
const result = await extractArchive('archive.tar.gz', '/output/path');TypeScript
import { extractArchive, SecurityConfig, ExtractionReport } from 'exarch-rs';
const result: ExtractionReport = await extractArchive('archive.tar.gz', '/output/path');
console.log(`Extracted ${result.filesExtracted} files`);Custom Security Configuration
import { extractArchive, SecurityConfig } from 'exarch-rs';
const config = new SecurityConfig()
.maxFileSize(100 * 1024 * 1024) // 100 MB per file
.maxTotalSize(1024 * 1024 * 1024) // 1 GB total
.maxFileCount(10_000); // Max 10k files
const result = await extractArchive('archive.tar.gz', '/output', config);Error Handling
const { extractArchive } = require('exarch-rs');
try {
const result = await extractArchive('archive.tar.gz', '/output');
console.log(`Success: ${result.filesExtracted} files`);
} catch (error) {
// Error codes: PATH_TRAVERSAL, SYMLINK_ESCAPE, ZIP_BOMB, QUOTA_EXCEEDED, etc.
console.error(`Extraction failed: ${error.message}`);
}API
extractArchive(archivePath, outputDir, config?)
Extract an archive asynchronously with security validation.
Parameters:
| Name | Type | Description |
|------|------|-------------|
| archivePath | string | Path to the archive file |
| outputDir | string | Directory where files will be extracted |
| config | SecurityConfig | Optional security configuration |
Returns: Promise<ExtractionReport>
extractArchiveSync(archivePath, outputDir, config?)
Synchronous version. Blocks the event loop until extraction completes.
Returns: ExtractionReport
ExtractionReport
interface ExtractionReport {
filesExtracted: number; // Number of files extracted
directoriesCreated: number; // Number of directories created
symlinksCreated: number; // Number of symlinks created
bytesWritten: number; // Total bytes written
durationMs: number; // Extraction duration in milliseconds
filesSkipped: number; // Files skipped (e.g. duplicates)
warnings: string[]; // Warning messages from extraction
}SecurityConfig
Builder-style security configuration.
const config = new SecurityConfig()
.maxFileSize(bytes) // Max size per file
.maxTotalSize(bytes) // Max total extraction size
.maxFileCount(count) // Max number of files
.maxCompressionRatio(n) // Max compression ratio (zip bomb detection)
.allowedExtensions([".txt", ".md"]) // Restrict to a set of extensions
.bannedPathComponents(["__MACOSX"]) // Skip these path components
.setAllowSolidArchives(true); // Allow solid 7z archives (default: false)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 |
| Permission sanitization | Strips setuid/setgid bits |
| Size limits | Enforces file and total size limits |
Caution: Unlike many Node.js archive libraries, 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 | ✅ | ✅ | ✅ | ✅ |
| 7z | .7z | ✅ | — | ✅ | ✅ |
Note: 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).
Note: Since v0.4.0, partial extraction failures return the ExtractionReport accumulated up to the failure point without the inner error text being duplicated, and the report is now correctly delivered across the FFI boundary instead of being dropped early.
Comparison with tar-fs
// UNSAFE - tar-fs has known vulnerabilities
const tar = require('tar-fs');
const fs = require('fs');
fs.createReadStream('archive.tar')
.pipe(tar.extract('/output')); // May extract outside target directory!
// SAFE - exarch-rs validates all paths
const { extractArchive } = require('exarch-rs');
await extractArchive('archive.tar', '/output'); // Protected by defaultDevelopment
This package is built using napi-rs.
# Clone repository
git clone https://github.com/bug-ops/exarch
cd exarch/crates/exarch-node
# Install dependencies
npm install
# Build native module
npm run build
# Run tests
npm testRelated Packages
- exarch-core — Core Rust library
- exarch (PyPI) — Python bindings
License
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT License (LICENSE-MIT)
at your option.
