exarch-rs 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.toml +28 -0
- package/README.md +235 -0
- package/build.rs +5 -0
- package/index.d.ts +287 -0
- package/package.json +37 -0
- package/src/config.rs +927 -0
- package/src/error.rs +309 -0
- package/src/lib.rs +428 -0
- package/src/report.rs +176 -0
- package/src/utils.rs +84 -0
package/Cargo.toml
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "exarch-node"
|
|
3
|
+
description = "Node.js bindings for exarch-core"
|
|
4
|
+
version.workspace = true
|
|
5
|
+
authors.workspace = true
|
|
6
|
+
edition.workspace = true
|
|
7
|
+
rust-version.workspace = true
|
|
8
|
+
license.workspace = true
|
|
9
|
+
repository.workspace = true
|
|
10
|
+
homepage.workspace = true
|
|
11
|
+
|
|
12
|
+
[lib]
|
|
13
|
+
crate-type = ["cdylib"]
|
|
14
|
+
|
|
15
|
+
[dependencies]
|
|
16
|
+
exarch-core.workspace = true
|
|
17
|
+
napi = { workspace = true, features = ["async", "napi4", "error_anyhow", "tokio_rt"] }
|
|
18
|
+
napi-derive.workspace = true
|
|
19
|
+
tokio.workspace = true
|
|
20
|
+
|
|
21
|
+
[build-dependencies]
|
|
22
|
+
napi-build.workspace = true
|
|
23
|
+
|
|
24
|
+
[dev-dependencies]
|
|
25
|
+
tokio = { workspace = true, features = ["macros", "rt"] }
|
|
26
|
+
|
|
27
|
+
[lints]
|
|
28
|
+
workspace = true
|
package/README.md
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# exarch-rs
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/exarch-rs)
|
|
4
|
+
[](https://nodejs.org)
|
|
5
|
+
[](https://www.typescriptlang.org/)
|
|
6
|
+
[](https://github.com/bug-ops/exarch/actions)
|
|
7
|
+
[](../../LICENSE-MIT)
|
|
8
|
+
|
|
9
|
+
Memory-safe archive extraction library for Node.js.
|
|
10
|
+
|
|
11
|
+
> [!IMPORTANT]
|
|
12
|
+
> **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.
|
|
13
|
+
|
|
14
|
+
This package provides Node.js bindings for [exarch-core](../exarch-core), a Rust library with built-in protection against common archive vulnerabilities.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# npm
|
|
20
|
+
npm install exarch-rs
|
|
21
|
+
|
|
22
|
+
# yarn
|
|
23
|
+
yarn add exarch-rs
|
|
24
|
+
|
|
25
|
+
# pnpm
|
|
26
|
+
pnpm add exarch-rs
|
|
27
|
+
|
|
28
|
+
# bun
|
|
29
|
+
bun add exarch-rs
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
> [!NOTE]
|
|
33
|
+
> This package includes TypeScript definitions. No need for separate `@types` package.
|
|
34
|
+
|
|
35
|
+
## Requirements
|
|
36
|
+
|
|
37
|
+
- Node.js >= 14
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
```javascript
|
|
42
|
+
const { extractArchive } = require('exarch-rs');
|
|
43
|
+
|
|
44
|
+
// Async (recommended)
|
|
45
|
+
const result = await extractArchive('archive.tar.gz', '/output/path');
|
|
46
|
+
console.log(`Extracted ${result.filesExtracted} files`);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Usage
|
|
50
|
+
|
|
51
|
+
### Async API (Recommended)
|
|
52
|
+
|
|
53
|
+
```javascript
|
|
54
|
+
const { extractArchive } = require('exarch-rs');
|
|
55
|
+
|
|
56
|
+
const result = await extractArchive('archive.tar.gz', '/output/path');
|
|
57
|
+
|
|
58
|
+
console.log(`Files extracted: ${result.filesExtracted}`);
|
|
59
|
+
console.log(`Bytes written: ${result.bytesWritten}`);
|
|
60
|
+
console.log(`Duration: ${result.durationMs}ms`);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Sync API
|
|
64
|
+
|
|
65
|
+
```javascript
|
|
66
|
+
const { extractArchiveSync } = require('exarch-rs');
|
|
67
|
+
|
|
68
|
+
const result = extractArchiveSync('archive.tar.gz', '/output/path');
|
|
69
|
+
console.log(`Extracted ${result.filesExtracted} files`);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
> [!TIP]
|
|
73
|
+
> Prefer the async API to avoid blocking the event loop during extraction.
|
|
74
|
+
|
|
75
|
+
### ES Modules
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
import { extractArchive } from 'exarch-rs';
|
|
79
|
+
|
|
80
|
+
const result = await extractArchive('archive.tar.gz', '/output/path');
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### TypeScript
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
import { extractArchive, SecurityConfig, ExtractionReport } from 'exarch-rs';
|
|
87
|
+
|
|
88
|
+
const result: ExtractionReport = await extractArchive('archive.tar.gz', '/output/path');
|
|
89
|
+
console.log(`Extracted ${result.filesExtracted} files`);
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Custom Security Configuration
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
import { extractArchive, SecurityConfig } from 'exarch-rs';
|
|
96
|
+
|
|
97
|
+
const config = new SecurityConfig()
|
|
98
|
+
.maxFileSize(100 * 1024 * 1024) // 100 MB per file
|
|
99
|
+
.maxTotalSize(1024 * 1024 * 1024) // 1 GB total
|
|
100
|
+
.maxFileCount(10_000); // Max 10k files
|
|
101
|
+
|
|
102
|
+
const result = await extractArchive('archive.tar.gz', '/output', config);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Error Handling
|
|
106
|
+
|
|
107
|
+
```javascript
|
|
108
|
+
const { extractArchive } = require('exarch-rs');
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
const result = await extractArchive('archive.tar.gz', '/output');
|
|
112
|
+
console.log(`Success: ${result.filesExtracted} files`);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
// Error codes: PATH_TRAVERSAL, SYMLINK_ESCAPE, ZIP_BOMB, QUOTA_EXCEEDED, etc.
|
|
115
|
+
console.error(`Extraction failed: ${error.message}`);
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## API
|
|
120
|
+
|
|
121
|
+
### `extractArchive(archivePath, outputDir, config?)`
|
|
122
|
+
|
|
123
|
+
Extract an archive asynchronously with security validation.
|
|
124
|
+
|
|
125
|
+
**Parameters:**
|
|
126
|
+
|
|
127
|
+
| Name | Type | Description |
|
|
128
|
+
|------|------|-------------|
|
|
129
|
+
| `archivePath` | `string` | Path to the archive file |
|
|
130
|
+
| `outputDir` | `string` | Directory where files will be extracted |
|
|
131
|
+
| `config` | `SecurityConfig` | Optional security configuration |
|
|
132
|
+
|
|
133
|
+
**Returns:** `Promise<ExtractionReport>`
|
|
134
|
+
|
|
135
|
+
### `extractArchiveSync(archivePath, outputDir, config?)`
|
|
136
|
+
|
|
137
|
+
Synchronous version. Blocks the event loop until extraction completes.
|
|
138
|
+
|
|
139
|
+
**Returns:** `ExtractionReport`
|
|
140
|
+
|
|
141
|
+
### `ExtractionReport`
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
interface ExtractionReport {
|
|
145
|
+
filesExtracted: number; // Number of files extracted
|
|
146
|
+
bytesWritten: number; // Total bytes written
|
|
147
|
+
durationMs: number; // Extraction duration in milliseconds
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### `SecurityConfig`
|
|
152
|
+
|
|
153
|
+
Builder-style security configuration.
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
const config = new SecurityConfig()
|
|
157
|
+
.maxFileSize(bytes) // Max size per file
|
|
158
|
+
.maxTotalSize(bytes) // Max total extraction size
|
|
159
|
+
.maxFileCount(count) // Max number of files
|
|
160
|
+
.maxCompressionRatio(n); // Max compression ratio (zip bomb detection)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Security Features
|
|
164
|
+
|
|
165
|
+
The library provides built-in protection against:
|
|
166
|
+
|
|
167
|
+
| Protection | Description |
|
|
168
|
+
|------------|-------------|
|
|
169
|
+
| Path traversal | Blocks `../` and absolute paths |
|
|
170
|
+
| Symlink attacks | Prevents symlinks escaping extraction directory |
|
|
171
|
+
| Hardlink attacks | Validates hardlink targets |
|
|
172
|
+
| Zip bombs | Detects high compression ratios |
|
|
173
|
+
| Permission sanitization | Strips setuid/setgid bits |
|
|
174
|
+
| Size limits | Enforces file and total size limits |
|
|
175
|
+
|
|
176
|
+
> [!CAUTION]
|
|
177
|
+
> Unlike many Node.js archive libraries, exarch applies security validation by default.
|
|
178
|
+
|
|
179
|
+
## Supported Formats
|
|
180
|
+
|
|
181
|
+
| Format | Extensions |
|
|
182
|
+
|--------|------------|
|
|
183
|
+
| TAR | `.tar` |
|
|
184
|
+
| TAR+GZIP | `.tar.gz`, `.tgz` |
|
|
185
|
+
| TAR+BZIP2 | `.tar.bz2`, `.tbz2` |
|
|
186
|
+
| TAR+XZ | `.tar.xz`, `.txz` |
|
|
187
|
+
| TAR+ZSTD | `.tar.zst`, `.tzst` |
|
|
188
|
+
| ZIP | `.zip` |
|
|
189
|
+
|
|
190
|
+
## Comparison with tar-fs
|
|
191
|
+
|
|
192
|
+
```javascript
|
|
193
|
+
// UNSAFE - tar-fs has known vulnerabilities
|
|
194
|
+
const tar = require('tar-fs');
|
|
195
|
+
const fs = require('fs');
|
|
196
|
+
fs.createReadStream('archive.tar')
|
|
197
|
+
.pipe(tar.extract('/output')); // May extract outside target directory!
|
|
198
|
+
|
|
199
|
+
// SAFE - exarch-rs validates all paths
|
|
200
|
+
const { extractArchive } = require('exarch-rs');
|
|
201
|
+
await extractArchive('archive.tar', '/output'); // Protected by default
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Development
|
|
205
|
+
|
|
206
|
+
This package is built using [napi-rs](https://napi.rs/).
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
# Clone repository
|
|
210
|
+
git clone https://github.com/bug-ops/exarch
|
|
211
|
+
cd exarch/crates/exarch-node
|
|
212
|
+
|
|
213
|
+
# Install dependencies
|
|
214
|
+
npm install
|
|
215
|
+
|
|
216
|
+
# Build native module
|
|
217
|
+
npm run build
|
|
218
|
+
|
|
219
|
+
# Run tests
|
|
220
|
+
npm test
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Related Packages
|
|
224
|
+
|
|
225
|
+
- [exarch-core](../exarch-core) — Core Rust library
|
|
226
|
+
- [exarch (PyPI)](../exarch-python) — Python bindings
|
|
227
|
+
|
|
228
|
+
## License
|
|
229
|
+
|
|
230
|
+
Licensed under either of:
|
|
231
|
+
|
|
232
|
+
- Apache License, Version 2.0 ([LICENSE-APACHE](../../LICENSE-APACHE))
|
|
233
|
+
- MIT License ([LICENSE-MIT](../../LICENSE-MIT))
|
|
234
|
+
|
|
235
|
+
at your option.
|
package/build.rs
ADDED
package/index.d.ts
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* exarch - Memory-safe archive extraction library
|
|
3
|
+
*
|
|
4
|
+
* Provides secure archive extraction with built-in protection against:
|
|
5
|
+
* - Path traversal attacks
|
|
6
|
+
* - Symlink escape attacks
|
|
7
|
+
* - Hardlink escape attacks
|
|
8
|
+
* - Zip bomb attacks
|
|
9
|
+
* - Invalid file permissions
|
|
10
|
+
* - Resource quota violations
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Security configuration for archive extraction.
|
|
15
|
+
*
|
|
16
|
+
* All security features default to deny (secure-by-default policy).
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* // Use secure defaults
|
|
21
|
+
* const config = new SecurityConfig();
|
|
22
|
+
*
|
|
23
|
+
* // Customize with builder pattern
|
|
24
|
+
* const config = new SecurityConfig()
|
|
25
|
+
* .maxFileSize(100 * 1024 * 1024)
|
|
26
|
+
* .allowSymlinks(true);
|
|
27
|
+
*
|
|
28
|
+
* // Use permissive configuration for trusted archives
|
|
29
|
+
* const config = SecurityConfig.permissive();
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export class SecurityConfig {
|
|
33
|
+
/**
|
|
34
|
+
* Creates a new SecurityConfig with secure defaults.
|
|
35
|
+
*/
|
|
36
|
+
constructor();
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Creates a SecurityConfig with secure defaults.
|
|
40
|
+
* Equivalent to calling the constructor.
|
|
41
|
+
*/
|
|
42
|
+
static default(): SecurityConfig;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Creates a permissive configuration for trusted archives.
|
|
46
|
+
*
|
|
47
|
+
* Enables: symlinks, hardlinks, absolute paths, world-writable files.
|
|
48
|
+
* Use only for archives from trusted sources.
|
|
49
|
+
*/
|
|
50
|
+
static permissive(): SecurityConfig;
|
|
51
|
+
|
|
52
|
+
// Builder pattern methods (chainable)
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Sets the maximum file size in bytes.
|
|
56
|
+
* Default: 50 MB (52,428,800 bytes)
|
|
57
|
+
*/
|
|
58
|
+
maxFileSize(size: number): this;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Sets the maximum total size in bytes.
|
|
62
|
+
* Default: 500 MB (524,288,000 bytes)
|
|
63
|
+
*/
|
|
64
|
+
maxTotalSize(size: number): this;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Sets the maximum compression ratio.
|
|
68
|
+
* Default: 100.0
|
|
69
|
+
*
|
|
70
|
+
* @throws Error if ratio is not a positive finite number
|
|
71
|
+
*/
|
|
72
|
+
maxCompressionRatio(ratio: number): this;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Sets the maximum file count.
|
|
76
|
+
* Default: 10,000
|
|
77
|
+
*/
|
|
78
|
+
maxFileCount(count: number): this;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Sets the maximum path depth.
|
|
82
|
+
* Default: 32
|
|
83
|
+
*/
|
|
84
|
+
maxPathDepth(depth: number): this;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Allows or denies symlinks.
|
|
88
|
+
* Default: false (deny)
|
|
89
|
+
*/
|
|
90
|
+
allowSymlinks(allow?: boolean): this;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Allows or denies hardlinks.
|
|
94
|
+
* Default: false (deny)
|
|
95
|
+
*/
|
|
96
|
+
allowHardlinks(allow?: boolean): this;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Allows or denies absolute paths.
|
|
100
|
+
* Default: false (deny)
|
|
101
|
+
*/
|
|
102
|
+
allowAbsolutePaths(allow?: boolean): this;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Allows or denies world-writable files.
|
|
106
|
+
* Default: false (deny)
|
|
107
|
+
*/
|
|
108
|
+
allowWorldWritable(allow?: boolean): this;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Sets whether to preserve permissions from archive.
|
|
112
|
+
* Default: false
|
|
113
|
+
*/
|
|
114
|
+
preservePermissions(preserve?: boolean): this;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Adds an allowed file extension.
|
|
118
|
+
*
|
|
119
|
+
* @throws Error if extension exceeds maximum length or contains null bytes
|
|
120
|
+
*/
|
|
121
|
+
addAllowedExtension(ext: string): this;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Adds a banned path component.
|
|
125
|
+
*
|
|
126
|
+
* @throws Error if component exceeds maximum length or contains null bytes
|
|
127
|
+
*/
|
|
128
|
+
addBannedComponent(component: string): this;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Finalizes the configuration (for API consistency).
|
|
132
|
+
*
|
|
133
|
+
* This method is provided for builder pattern consistency but is optional.
|
|
134
|
+
* The configuration is always valid and can be used directly.
|
|
135
|
+
*/
|
|
136
|
+
build(): this;
|
|
137
|
+
|
|
138
|
+
// Validation methods
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Checks if a path component is allowed.
|
|
142
|
+
*/
|
|
143
|
+
isPathComponentAllowed(component: string): boolean;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Checks if a file extension is allowed.
|
|
147
|
+
*/
|
|
148
|
+
isExtensionAllowed(extension: string): boolean;
|
|
149
|
+
|
|
150
|
+
// Property getters
|
|
151
|
+
|
|
152
|
+
/** Maximum file size in bytes */
|
|
153
|
+
readonly maxFileSize: number;
|
|
154
|
+
|
|
155
|
+
/** Maximum total extraction size in bytes */
|
|
156
|
+
readonly maxTotalSize: number;
|
|
157
|
+
|
|
158
|
+
/** Maximum compression ratio */
|
|
159
|
+
readonly maxCompressionRatio: number;
|
|
160
|
+
|
|
161
|
+
/** Maximum number of files */
|
|
162
|
+
readonly maxFileCount: number;
|
|
163
|
+
|
|
164
|
+
/** Maximum path depth */
|
|
165
|
+
readonly maxPathDepth: number;
|
|
166
|
+
|
|
167
|
+
/** Whether file permissions are preserved from archive */
|
|
168
|
+
readonly preservePermissions: boolean;
|
|
169
|
+
|
|
170
|
+
/** Whether symlinks are allowed */
|
|
171
|
+
readonly allowSymlinks: boolean;
|
|
172
|
+
|
|
173
|
+
/** Whether hardlinks are allowed */
|
|
174
|
+
readonly allowHardlinks: boolean;
|
|
175
|
+
|
|
176
|
+
/** Whether absolute paths are allowed */
|
|
177
|
+
readonly allowAbsolutePaths: boolean;
|
|
178
|
+
|
|
179
|
+
/** Whether world-writable files are allowed */
|
|
180
|
+
readonly allowWorldWritable: boolean;
|
|
181
|
+
|
|
182
|
+
/** List of allowed file extensions */
|
|
183
|
+
readonly allowedExtensions: string[];
|
|
184
|
+
|
|
185
|
+
/** List of banned path components */
|
|
186
|
+
readonly bannedPathComponents: string[];
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Report of an archive extraction operation.
|
|
191
|
+
*
|
|
192
|
+
* Contains statistics and metadata about the extraction process.
|
|
193
|
+
*/
|
|
194
|
+
export interface ExtractionReport {
|
|
195
|
+
/** Number of files successfully extracted */
|
|
196
|
+
filesExtracted: number;
|
|
197
|
+
|
|
198
|
+
/** Number of directories created */
|
|
199
|
+
directoriesCreated: number;
|
|
200
|
+
|
|
201
|
+
/** Number of symlinks created */
|
|
202
|
+
symlinksCreated: number;
|
|
203
|
+
|
|
204
|
+
/** Total bytes written to disk */
|
|
205
|
+
bytesWritten: number;
|
|
206
|
+
|
|
207
|
+
/** Extraction duration in milliseconds */
|
|
208
|
+
durationMs: number;
|
|
209
|
+
|
|
210
|
+
/** Number of files skipped due to security checks */
|
|
211
|
+
filesSkipped: number;
|
|
212
|
+
|
|
213
|
+
/** List of warning messages */
|
|
214
|
+
warnings: string[];
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Extract an archive to the specified directory (async).
|
|
219
|
+
*
|
|
220
|
+
* This function provides secure archive extraction with configurable
|
|
221
|
+
* security policies. By default, it uses a restrictive security
|
|
222
|
+
* configuration that blocks symlinks, hardlinks, absolute paths, and
|
|
223
|
+
* enforces resource quotas.
|
|
224
|
+
*
|
|
225
|
+
* Error codes in exception messages:
|
|
226
|
+
* - `PATH_TRAVERSAL`: Path traversal attempt detected
|
|
227
|
+
* - `SYMLINK_ESCAPE`: Symlink points outside extraction directory
|
|
228
|
+
* - `HARDLINK_ESCAPE`: Hardlink target outside extraction directory
|
|
229
|
+
* - `ZIP_BOMB`: Potential zip bomb detected
|
|
230
|
+
* - `INVALID_PERMISSIONS`: File permissions are invalid or unsafe
|
|
231
|
+
* - `QUOTA_EXCEEDED`: Resource quota exceeded
|
|
232
|
+
* - `SECURITY_VIOLATION`: Security policy violation
|
|
233
|
+
* - `UNSUPPORTED_FORMAT`: Archive format not supported
|
|
234
|
+
* - `INVALID_ARCHIVE`: Archive is corrupted
|
|
235
|
+
* - `IO_ERROR`: I/O operation failed
|
|
236
|
+
*
|
|
237
|
+
* @param archivePath - Path to the archive file
|
|
238
|
+
* @param outputDir - Directory where files will be extracted
|
|
239
|
+
* @param config - Optional SecurityConfig (uses secure defaults if omitted)
|
|
240
|
+
* @returns Promise resolving to ExtractionReport with extraction statistics
|
|
241
|
+
* @throws Error for security violations or I/O errors
|
|
242
|
+
*
|
|
243
|
+
* @example
|
|
244
|
+
* ```typescript
|
|
245
|
+
* // Use secure defaults
|
|
246
|
+
* const report = await extractArchive('archive.tar.gz', '/tmp/output');
|
|
247
|
+
* console.log(`Extracted ${report.filesExtracted} files`);
|
|
248
|
+
*
|
|
249
|
+
* // Customize security settings
|
|
250
|
+
* const config = new SecurityConfig().maxFileSize(100 * 1024 * 1024);
|
|
251
|
+
* const report = await extractArchive('archive.tar.gz', '/tmp/output', config);
|
|
252
|
+
* ```
|
|
253
|
+
*/
|
|
254
|
+
export function extractArchive(
|
|
255
|
+
archivePath: string,
|
|
256
|
+
outputDir: string,
|
|
257
|
+
config?: SecurityConfig
|
|
258
|
+
): Promise<ExtractionReport>;
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Extract an archive to the specified directory (sync).
|
|
262
|
+
*
|
|
263
|
+
* Synchronous version of extractArchive. Blocks the event loop until
|
|
264
|
+
* extraction completes. Prefer the async version for most use cases.
|
|
265
|
+
*
|
|
266
|
+
* @param archivePath - Path to the archive file
|
|
267
|
+
* @param outputDir - Directory where files will be extracted
|
|
268
|
+
* @param config - Optional SecurityConfig (uses secure defaults if omitted)
|
|
269
|
+
* @returns ExtractionReport with extraction statistics
|
|
270
|
+
* @throws Error for security violations or I/O errors
|
|
271
|
+
*
|
|
272
|
+
* @example
|
|
273
|
+
* ```typescript
|
|
274
|
+
* // Use secure defaults
|
|
275
|
+
* const report = extractArchiveSync('archive.tar.gz', '/tmp/output');
|
|
276
|
+
* console.log(`Extracted ${report.filesExtracted} files`);
|
|
277
|
+
*
|
|
278
|
+
* // Customize security settings
|
|
279
|
+
* const config = new SecurityConfig().maxFileSize(100 * 1024 * 1024);
|
|
280
|
+
* const report = extractArchiveSync('archive.tar.gz', '/tmp/output', config);
|
|
281
|
+
* ```
|
|
282
|
+
*/
|
|
283
|
+
export function extractArchiveSync(
|
|
284
|
+
archivePath: string,
|
|
285
|
+
outputDir: string,
|
|
286
|
+
config?: SecurityConfig
|
|
287
|
+
): ExtractionReport;
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "exarch-rs",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Memory-safe archive extraction library",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"types": "index.d.ts",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"archive",
|
|
9
|
+
"extraction",
|
|
10
|
+
"security",
|
|
11
|
+
"tar",
|
|
12
|
+
"zip",
|
|
13
|
+
"napi-rs",
|
|
14
|
+
"rust"
|
|
15
|
+
],
|
|
16
|
+
"license": "MIT OR Apache-2.0",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/bug-ops/exarch"
|
|
20
|
+
},
|
|
21
|
+
"napi": {
|
|
22
|
+
"name": "exarch-rs",
|
|
23
|
+
"triples": {
|
|
24
|
+
"defaults": true
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "napi build --platform --release",
|
|
29
|
+
"build:debug": "napi build --platform"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@napi-rs/cli": "^3.0.0"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">= 14"
|
|
36
|
+
}
|
|
37
|
+
}
|