@openclaw/fs-safe 0.8.1 → 0.8.2
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/CHANGELOG.md +11 -0
- package/README.md +1 -1
- package/dist/archive-entry.d.ts.map +1 -1
- package/dist/archive-entry.js +5 -0
- package/dist/archive-gzip-tail.d.ts +18 -0
- package/dist/archive-gzip-tail.d.ts.map +1 -0
- package/dist/archive-gzip-tail.js +74 -0
- package/dist/archive-parser.wasm +0 -0
- package/dist/archive-read.d.ts.map +1 -1
- package/dist/archive-read.js +18 -60
- package/dist/archive-tar-extract.d.ts +11 -0
- package/dist/archive-tar-extract.d.ts.map +1 -0
- package/dist/archive-tar-extract.js +49 -0
- package/dist/archive-tar-stream.d.ts +18 -0
- package/dist/archive-tar-stream.d.ts.map +1 -0
- package/dist/archive-tar-stream.js +95 -0
- package/dist/archive-tar-wasm.d.ts +18 -0
- package/dist/archive-tar-wasm.d.ts.map +1 -0
- package/dist/archive-tar-wasm.js +102 -0
- package/dist/archive-tar.d.ts +0 -1
- package/dist/archive-tar.d.ts.map +1 -1
- package/dist/archive-tar.js +0 -22
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +2 -109
- package/dist/device-path.d.ts +1 -0
- package/dist/device-path.d.ts.map +1 -1
- package/dist/device-path.js +5 -2
- package/dist/directory-mode-owner.d.ts.map +1 -1
- package/dist/directory-mode-owner.js +3 -0
- package/dist/replace-file-descriptor.d.ts.map +1 -1
- package/dist/replace-file-descriptor.js +2 -1
- package/dist/secret-file.d.ts.map +1 -1
- package/dist/secret-file.js +2 -0
- package/dist/secret-read-async.d.ts.map +1 -1
- package/dist/secret-read-async.js +3 -0
- package/docs/archive.md +66 -61
- package/docs/contributing.md +35 -1
- package/docs/install.md +1 -1
- package/docs/native-helper.md +5 -0
- package/docs/native.md +16 -10
- package/docs/secret-file.md +5 -0
- package/package.json +19 -16
- package/dist/archive-tar-admission.d.ts +0 -7
- package/dist/archive-tar-admission.d.ts.map +0 -1
- package/dist/archive-tar-admission.js +0 -43
- package/dist/archive-tar-gnu.d.ts +0 -2
- package/dist/archive-tar-gnu.d.ts.map +0 -1
- package/dist/archive-tar-gnu.js +0 -20
- package/dist/archive-tar-header.d.ts +0 -8
- package/dist/archive-tar-header.d.ts.map +0 -1
- package/dist/archive-tar-header.js +0 -47
- package/dist/archive-tar-meta.d.ts +0 -34
- package/dist/archive-tar-meta.d.ts.map +0 -1
- package/dist/archive-tar-meta.js +0 -277
- package/dist/archive-tar-pax.d.ts +0 -8
- package/dist/archive-tar-pax.d.ts.map +0 -1
- package/dist/archive-tar-pax.js +0 -100
- package/dist/archive-tar-runtime.d.ts +0 -49
- package/dist/archive-tar-runtime.d.ts.map +0 -1
- package/dist/archive-tar-runtime.js +0 -22
package/docs/native.md
CHANGED
|
@@ -64,13 +64,15 @@ returns a bounded manifest. TypeScript applies the shared path, filter, strip,
|
|
|
64
64
|
mode, and byte policies and returns an index-bound extraction plan. Rust then
|
|
65
65
|
creates only those planned entries beneath a private staging descriptor.
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
67
|
+
The `fs-safe-archive-core` Rust workspace crate owns TAR framing, paths, types,
|
|
68
|
+
mode decoding, GNU metadata, and byte-counted local PAX records. The native
|
|
69
|
+
binding and bundled WASM module compile the same source. No `tar::Archive` or
|
|
70
|
+
Node TAR parser reinterprets admitted identities or sizes. Executors replay
|
|
71
|
+
admitted payload ranges after complete bounded admission; native writes retain
|
|
72
|
+
the platform's descriptor-relative primitives, while fallback writes retain
|
|
73
|
+
the guarded Node staging/publication boundary. ZIP behavior is unchanged.
|
|
74
|
+
`maxMetaEntryBytes` bounds bodies before allocation; unsupported global/old
|
|
75
|
+
metadata and sparse forms fail closed. See [bounded local PAX support](archive.md#bounded-local-pax-support).
|
|
74
76
|
|
|
75
77
|
Every raw pass receives only TypeScript's resolved `maxEntries`,
|
|
76
78
|
`maxMetaEntryBytes`, and `maxDecodedBytes`. Shared resolution caps metadata and
|
|
@@ -88,11 +90,15 @@ integer maximum. Every native pass receives that same cap and charges headers,
|
|
|
88
90
|
metadata, bodies, padding, EOF blocks, and trailing zeros. It rejects overflow
|
|
89
91
|
with `archive-decoded-size-exceeds-limit`; no ratio policy is implied.
|
|
90
92
|
Extraction and entry reads drain the metered reader through physical EOF after
|
|
91
|
-
|
|
93
|
+
admitted-range replay. Trailing framing or decoded-limit failures propagate before
|
|
92
94
|
directory modes are finalized, staging is published, or selected bytes return.
|
|
95
|
+
Native gzip uses the existing flate2 member decoder with an explicit bounded
|
|
96
|
+
member/padding transition; JavaScript retains Node gunzip and validates its
|
|
97
|
+
unconsumed compressed suffix. Only all-zero physical padding after a complete
|
|
98
|
+
validated trailer is accepted, still within the original archive-byte budget.
|
|
93
99
|
Native reads stop at framing boundaries so a rejected header does not request
|
|
94
100
|
its body from the decoder; codec buffering can still read ahead internally.
|
|
95
|
-
Inspection finishes the complete bounded framing pass before
|
|
101
|
+
Inspection finishes the complete bounded framing pass before returning its manifest. Directory
|
|
96
102
|
and link bodies, missing two-block EOF, and nonzero trailers reject on both
|
|
97
103
|
backends, as detailed in [raw TAR framing](archive.md#raw-tar-framing). Raw and
|
|
98
104
|
padded sizes above JavaScript's safe-integer maximum reject as invalid framing
|
|
@@ -155,7 +161,7 @@ remain TypeScript-owned. What changes is the syscall strength or availability:
|
|
|
155
161
|
| Capability | Native path | Guarded JavaScript path |
|
|
156
162
|
|---|---|---|
|
|
157
163
|
| Root-relative opens/mutations | Descriptor-relative beneath operations. Pinned writes create parents and publish both replacement and no-replace targets relative to open directory descriptors. Linux reports `kernel-atomic`; macOS and Windows report `best-effort`. macOS uses `O_RESOLVE_BENEATH` when available plus an `F_GETPATH` detector, while Windows rejects reparse traversal in the object-manager call. | Reports `best-effort`: component-wise alias checks, no-follow opens where Node exposes them, private temp/rename, and post-operation identity verification. A same-privilege peer can replace a writable parent after a guard assertion but before Node resolves the pathname mutation; the mutation may land outside the intended root before the post-check detects it. |
|
|
158
|
-
| ZIP/TAR/gzip | Rust streaming decode and fd-relative output creation. | JSZip
|
|
164
|
+
| ZIP/TAR/gzip | Rust streaming decode and fd-relative output creation. | Optional JSZip or bundled WASM TAR into guarded private staging, then the same guarded merge policy. |
|
|
159
165
|
| Zstd/bzip2 TAR | Supported. | Unsupported; typed `helper-unavailable`. |
|
|
160
166
|
| Publication copy | Clone, Linux `copy_file_range`, async native SHA-256. | Exclusive `wx` byte loop and Node SHA-256 with the same content/identity fences. |
|
|
161
167
|
| `rename-noreplace` | Atomic platform no-replace rename. | Unsupported; no emulation by check-then-rename. |
|
package/docs/secret-file.md
CHANGED
|
@@ -91,6 +91,11 @@ credential must also fail on broad permissions or unexpected ownership.
|
|
|
91
91
|
the same pinned-handle validation, byte cap, trimming, error codes, and strict
|
|
92
92
|
versus missing-is-undefined naming semantics.
|
|
93
93
|
|
|
94
|
+
Both readers reject known unsafe device and process-fd paths with `device-path`
|
|
95
|
+
before inspection, and check the resolved target before opening it. This includes
|
|
96
|
+
Windows reserved device names and ignored-space aliases such as `nul .txt`.
|
|
97
|
+
Optional readers propagate this error instead of treating the secret as missing.
|
|
98
|
+
|
|
94
99
|
Both sync and async readers compare lossless bigint identities from the preview,
|
|
95
100
|
opened descriptor, resolved target, and current input path before reading. POSIX
|
|
96
101
|
opens are nonblocking, so a raced FIFO is rejected by descriptor type instead of
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/fs-safe",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "Capability-style filesystem roots for Node.js apps that handle untrusted relative paths.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"filesystem",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"provenance": true
|
|
25
25
|
},
|
|
26
26
|
"files": [
|
|
27
|
+
"dist/archive-parser.wasm",
|
|
27
28
|
"dist/**/*.js",
|
|
28
29
|
"dist/**/*.d.ts",
|
|
29
30
|
"dist/**/*.d.ts.map",
|
|
@@ -126,7 +127,7 @@
|
|
|
126
127
|
"scripts": {
|
|
127
128
|
"benchmark": "node scripts/benchmark.mjs",
|
|
128
129
|
"benchmark:publish": "pnpm build && node scripts/bench-publish.mjs",
|
|
129
|
-
"build": "node -
|
|
130
|
+
"build": "node scripts/prepack-build.mjs",
|
|
130
131
|
"lint:file-size": "node scripts/check-file-size.mjs",
|
|
131
132
|
"lint:fs-boundary": "node scripts/check-fs-boundary-primitives.mjs",
|
|
132
133
|
"prepack": "node scripts/prepack-build.mjs",
|
|
@@ -139,7 +140,7 @@
|
|
|
139
140
|
"docs:check": "node scripts/check-doc-examples.mjs",
|
|
140
141
|
"docs:site": "node scripts/build-docs-site.mjs",
|
|
141
142
|
"native:build": "pnpm --filter @openclaw/fs-safe-native-build build",
|
|
142
|
-
"native:test": "cargo test --
|
|
143
|
+
"native:test": "cargo test --workspace",
|
|
143
144
|
"pack:check": "pnpm build && node scripts/check-pack.mjs",
|
|
144
145
|
"public-api:update": "pnpm build && node scripts/check-pack.mjs --update-public-api",
|
|
145
146
|
"package:collect": "node scripts/check-release-packages.mjs --output release-artifacts",
|
|
@@ -150,32 +151,34 @@
|
|
|
150
151
|
"crabbox:hydrate": "crabbox actions hydrate",
|
|
151
152
|
"crabbox:run": "crabbox run",
|
|
152
153
|
"crabbox:stop": "crabbox stop",
|
|
153
|
-
"crabbox:warmup": "crabbox warmup"
|
|
154
|
+
"crabbox:warmup": "crabbox warmup",
|
|
155
|
+
"archive:wasm": "node scripts/build-archive-wasm.mjs",
|
|
156
|
+
"archive:producer-smoke": "node scripts/archive-producer-smoke.mjs"
|
|
154
157
|
},
|
|
155
158
|
"optionalDependencies": {
|
|
156
|
-
"@openclaw/fs-safe-darwin-arm64": "0.8.
|
|
157
|
-
"@openclaw/fs-safe-darwin-x64": "0.8.
|
|
158
|
-
"@openclaw/fs-safe-linux-arm64-gnu": "0.8.
|
|
159
|
-
"@openclaw/fs-safe-linux-arm64-musl": "0.8.
|
|
160
|
-
"@openclaw/fs-safe-linux-x64-gnu": "0.8.
|
|
161
|
-
"@openclaw/fs-safe-linux-x64-musl": "0.8.
|
|
162
|
-
"@openclaw/fs-safe-win32-x64-msvc": "0.8.
|
|
163
|
-
"jszip": "^3.10.1"
|
|
164
|
-
"tar": "7.5.22"
|
|
159
|
+
"@openclaw/fs-safe-darwin-arm64": "0.8.2",
|
|
160
|
+
"@openclaw/fs-safe-darwin-x64": "0.8.2",
|
|
161
|
+
"@openclaw/fs-safe-linux-arm64-gnu": "0.8.2",
|
|
162
|
+
"@openclaw/fs-safe-linux-arm64-musl": "0.8.2",
|
|
163
|
+
"@openclaw/fs-safe-linux-x64-gnu": "0.8.2",
|
|
164
|
+
"@openclaw/fs-safe-linux-x64-musl": "0.8.2",
|
|
165
|
+
"@openclaw/fs-safe-win32-x64-msvc": "0.8.2",
|
|
166
|
+
"jszip": "^3.10.1"
|
|
165
167
|
},
|
|
166
168
|
"devDependencies": {
|
|
167
169
|
"@emnapi/runtime": "2.0.0-alpha.4",
|
|
168
|
-
"@napi-rs/cli": "3.
|
|
170
|
+
"@napi-rs/cli": "3.9.0",
|
|
169
171
|
"@types/node": "^26.4.1",
|
|
170
|
-
"@vitest/coverage-v8": "
|
|
172
|
+
"@vitest/coverage-v8": "5.0.0",
|
|
171
173
|
"fast-check": "^4.9.0",
|
|
172
174
|
"istanbul-lib-coverage": "3.2.2",
|
|
173
175
|
"istanbul-lib-report": "3.0.1",
|
|
174
176
|
"istanbul-reports": "3.2.0",
|
|
175
177
|
"sigstore": "5.0.0",
|
|
178
|
+
"tar": "7.5.22",
|
|
176
179
|
"typescript": "^7.0.2",
|
|
177
180
|
"vite": "8.2.2",
|
|
178
|
-
"vitest": "^
|
|
181
|
+
"vitest": "^5.0.0"
|
|
179
182
|
},
|
|
180
183
|
"engines": {
|
|
181
184
|
"node": ">=22"
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { TarEntryInfo } from "./archive-tar.js";
|
|
2
|
-
export declare function rawTarMember(header: Buffer, size: number, effectivePath?: string): TarEntryInfo;
|
|
3
|
-
export declare function createTarAdmissionPlan(manifest: readonly TarEntryInfo[], check: (entry: TarEntryInfo) => boolean, strip: number): {
|
|
4
|
-
consume(entry: TarEntryInfo): string | null;
|
|
5
|
-
finish(): void;
|
|
6
|
-
};
|
|
7
|
-
//# sourceMappingURL=archive-tar-admission.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"archive-tar-admission.d.ts","sourceRoot":"","sources":["../src/archive-tar-admission.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAYrD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,CAS/F;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,SAAS,YAAY,EAAE,EACjC,KAAK,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,EACvC,KAAK,EAAE,MAAM,GACZ;IAAE,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,IAAI,IAAI,CAAA;CAAE,CAmBjE"}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { ArchiveFormatError } from "./archive-errors.js";
|
|
2
|
-
import { stripArchivePath, validateArchiveEntryPath } from "./archive-entry.js";
|
|
3
|
-
import { readTarHeaderPaths } from "./archive-tar-header.js";
|
|
4
|
-
// node-tar's normalFsTypes/ReadEntry switch. All other non-metadata types
|
|
5
|
-
// bypass both its filter and entry event; they still belong to our manifest.
|
|
6
|
-
// Header.decode normalizes NUL to "0" before choosing the emitted File type.
|
|
7
|
-
const visibleTypes = new Map([
|
|
8
|
-
[0, "File"], [0x30, "File"], [0x31, "Link"], [0x32, "SymbolicLink"],
|
|
9
|
-
[0x33, "CharacterDevice"], [0x34, "BlockDevice"], [0x35, "Directory"],
|
|
10
|
-
[0x36, "FIFO"], [0x37, "ContiguousFile"], [0x44, "GNUDumpDir"],
|
|
11
|
-
]);
|
|
12
|
-
export function rawTarMember(header, size, effectivePath) {
|
|
13
|
-
const { name, prefix } = readTarHeaderPaths(header);
|
|
14
|
-
validateArchiveEntryPath(name);
|
|
15
|
-
validateArchiveEntryPath(prefix);
|
|
16
|
-
const rawPath = prefix ? `${prefix}/${name}` : name;
|
|
17
|
-
validateArchiveEntryPath(rawPath);
|
|
18
|
-
const path = effectivePath ?? rawPath;
|
|
19
|
-
validateArchiveEntryPath(path);
|
|
20
|
-
return { path, size, type: visibleTypes.get(header[156]) ?? "Unsupported" };
|
|
21
|
-
}
|
|
22
|
-
export function createTarAdmissionPlan(manifest, check, strip) {
|
|
23
|
-
const visible = [];
|
|
24
|
-
for (const entry of manifest) {
|
|
25
|
-
const accepted = check(entry);
|
|
26
|
-
if (entry.type !== "Unsupported") {
|
|
27
|
-
visible.push({ entry, output: accepted ? stripArchivePath(entry.path, strip) : null });
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
let index = 0;
|
|
31
|
-
const mismatch = () => new ArchiveFormatError("invalid TAR header: parser disagrees with raw admission");
|
|
32
|
-
return {
|
|
33
|
-
consume(actual) {
|
|
34
|
-
const expected = visible[index++];
|
|
35
|
-
if (!expected || expected.entry.type !== actual.type || expected.entry.size !== actual.size ||
|
|
36
|
-
stripArchivePath(expected.entry.path, 0) !== stripArchivePath(actual.path, 0))
|
|
37
|
-
throw mismatch();
|
|
38
|
-
return expected.output;
|
|
39
|
-
},
|
|
40
|
-
finish() { if (index !== visible.length)
|
|
41
|
-
throw mismatch(); },
|
|
42
|
-
};
|
|
43
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"archive-tar-gnu.d.ts","sourceRoot":"","sources":["../src/archive-tar-gnu.ts"],"names":[],"mappings":"AAKA,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,GAAG,GAAG,MAAM,CAazE"}
|
package/dist/archive-tar-gnu.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { ArchiveFormatError } from "./archive-errors.js";
|
|
2
|
-
import { validateArchiveEntryPath } from "./archive-entry.js";
|
|
3
|
-
// Keep the byte grammar aligned with native/src/tar_meter.rs. Parsers differ
|
|
4
|
-
// on embedded NULs and malformed UTF-8, so validate before either sees a name.
|
|
5
|
-
export function validateGnuMetadata(body, type) {
|
|
6
|
-
const value = body.at(-1) === 0 ? body.subarray(0, -1) : body;
|
|
7
|
-
if (!value.length || value.includes(0)) {
|
|
8
|
-
throw new ArchiveFormatError("invalid GNU metadata: empty name or embedded NUL");
|
|
9
|
-
}
|
|
10
|
-
let name;
|
|
11
|
-
try {
|
|
12
|
-
name = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(value);
|
|
13
|
-
}
|
|
14
|
-
catch {
|
|
15
|
-
throw new ArchiveFormatError("invalid GNU metadata: name is not valid UTF-8");
|
|
16
|
-
}
|
|
17
|
-
if (type === "L")
|
|
18
|
-
validateArchiveEntryPath(name);
|
|
19
|
-
return name;
|
|
20
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export declare function validateTarChecksum(header: Buffer): void;
|
|
2
|
-
export declare function readTarHeaderPaths(header: Buffer): {
|
|
3
|
-
name: string;
|
|
4
|
-
prefix: string;
|
|
5
|
-
linkname: string;
|
|
6
|
-
};
|
|
7
|
-
export declare function validateTarHeader(header: Buffer): void;
|
|
8
|
-
//# sourceMappingURL=archive-tar-header.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"archive-tar-header.d.ts","sourceRoot":"","sources":["../src/archive-tar-header.ts"],"names":[],"mappings":"AAGA,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAWxD;AAcD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CASrG;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAUtD"}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { ArchiveFormatError, ArchiveSecurityError } from "./archive-errors.js";
|
|
2
|
-
const utf8 = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
|
|
3
|
-
export function validateTarChecksum(header) {
|
|
4
|
-
const field = header.subarray(148, 156);
|
|
5
|
-
const end = field.indexOf(0);
|
|
6
|
-
const digits = field.subarray(0, end < 0 ? field.length : end).toString("latin1").replace(/^ +| +$/g, "");
|
|
7
|
-
const sum = header.reduce((total, byte, index) => total + (index >= 148 && index < 156 ? 32 : byte), 0);
|
|
8
|
-
// Require an in-field delimiter: node-tar otherwise reads into the typeflag.
|
|
9
|
-
if (!/^[0-7]+$/.test(digits) || ![0, 32].includes(field[7]) ||
|
|
10
|
-
(end >= 0 && field.subarray(end).some((byte) => byte !== 0 && byte !== 32)) ||
|
|
11
|
-
Number.parseInt(digits, 8) !== sum) {
|
|
12
|
-
throw new ArchiveFormatError("invalid TAR header: checksum failure");
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
function fixedField(field) {
|
|
16
|
-
const zero = field.indexOf(0);
|
|
17
|
-
if (zero >= 0 && field.subarray(zero).some((byte) => byte !== 0)) {
|
|
18
|
-
throw new ArchiveSecurityError("entry-path", "tar entry path contains bytes after NUL");
|
|
19
|
-
}
|
|
20
|
-
try {
|
|
21
|
-
return utf8.decode(field.subarray(0, zero < 0 ? field.length : zero));
|
|
22
|
-
}
|
|
23
|
-
catch {
|
|
24
|
-
throw new ArchiveSecurityError("entry-path", "tar entry path is not valid UTF-8");
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
export function readTarHeaderPaths(header) {
|
|
28
|
-
// node-tar's star layout uses a 130-byte prefix followed by atime/ctime.
|
|
29
|
-
const prefixEnd = header[475] === 0 ? 475 : 500;
|
|
30
|
-
return {
|
|
31
|
-
name: fixedField(header.subarray(0, 100)),
|
|
32
|
-
prefix: header.subarray(257, 265).equals(Buffer.from("ustar\0" + "00"))
|
|
33
|
-
? fixedField(header.subarray(345, prefixEnd)) : "",
|
|
34
|
-
linkname: fixedField(header.subarray(157, 257)),
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
export function validateTarHeader(header) {
|
|
38
|
-
validateTarChecksum(header);
|
|
39
|
-
const { linkname } = readTarHeaderPaths(header);
|
|
40
|
-
const isLink = header[156] === 0x31 || header[156] === 0x32;
|
|
41
|
-
if (isLink && !linkname) {
|
|
42
|
-
throw new ArchiveFormatError("invalid TAR header: linkname required on a link header");
|
|
43
|
-
}
|
|
44
|
-
if (!isLink && linkname) {
|
|
45
|
-
throw new ArchiveFormatError("invalid TAR header: linkname forbidden on a non-link header");
|
|
46
|
-
}
|
|
47
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { Transform } from "node:stream";
|
|
2
|
-
import { type TarMeterLimits } from "./archive-limits.js";
|
|
3
|
-
import type { TarEntryInfo } from "./archive-tar.js";
|
|
4
|
-
export declare class TarMetadataMeter extends Transform {
|
|
5
|
-
private readonly limits;
|
|
6
|
-
private readonly onMember?;
|
|
7
|
-
private readonly block;
|
|
8
|
-
private blockLength;
|
|
9
|
-
private state;
|
|
10
|
-
private pendingPax;
|
|
11
|
-
private readonly pendingGnu;
|
|
12
|
-
private pendingGnuPath;
|
|
13
|
-
private zeroBlocks;
|
|
14
|
-
private entries;
|
|
15
|
-
private remainingDecodedBytes;
|
|
16
|
-
private manifestBytes;
|
|
17
|
-
constructor(limits: TarMeterLimits, onMember?: ((entry: TarEntryInfo) => void) | undefined);
|
|
18
|
-
private countMember;
|
|
19
|
-
private invalid;
|
|
20
|
-
private paddedSize;
|
|
21
|
-
private parseSize;
|
|
22
|
-
private finishHeader;
|
|
23
|
-
private finishSparseHeader;
|
|
24
|
-
private meter;
|
|
25
|
-
_transform(chunk: Buffer, _encoding: BufferEncoding, callback: (error?: Error | null, data?: Buffer) => void): void;
|
|
26
|
-
_flush(callback: (error?: Error | null) => void): void;
|
|
27
|
-
}
|
|
28
|
-
export declare function preflightTarMetadata(params: {
|
|
29
|
-
archivePath: string;
|
|
30
|
-
limits: TarMeterLimits;
|
|
31
|
-
signal?: AbortSignal;
|
|
32
|
-
onMember?: (entry: TarEntryInfo) => void;
|
|
33
|
-
}): Promise<void>;
|
|
34
|
-
//# sourceMappingURL=archive-tar-meta.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"archive-tar-meta.d.ts","sourceRoot":"","sources":["../src/archive-tar-meta.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAY,MAAM,aAAa,CAAC;AAIlD,OAAO,EAAqE,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAI7H,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAWrD,qBAAa,gBAAiB,SAAQ,SAAS;IAYjC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAAkB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;IAX/E,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;IAC3C,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,KAAK,CAAkC;IAC/C,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAwB;IACnD,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,qBAAqB,CAAS;IACtC,OAAO,CAAC,aAAa,CAAK;IAE1B,YAA6B,MAAM,EAAE,cAAc,EAAmB,QAAQ,CAAC,GAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,aAAA,EAM7G;IAED,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,SAAS;IA4BjB,OAAO,CAAC,YAAY;IA6EpB,OAAO,CAAC,kBAAkB;IAgB1B,OAAO,CAAC,KAAK;IAyCJ,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAc3H;IAEQ,MAAM,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI,CAM9D;CACF;AAaD,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;CAC1C,GAAG,OAAO,CAAC,IAAI,CAAC,CAahB"}
|
package/dist/archive-tar-meta.js
DELETED
|
@@ -1,277 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import { Transform, Writable } from "node:stream";
|
|
3
|
-
import { pipeline } from "node:stream/promises";
|
|
4
|
-
import { createGunzip } from "node:zlib";
|
|
5
|
-
import { ArchiveFormatError } from "./archive-errors.js";
|
|
6
|
-
import { ARCHIVE_LIMIT_ERROR_CODE, ArchiveLimitError, tarManifestEntryCost } from "./archive-limits.js";
|
|
7
|
-
import { parseLocalPax, paxMemberSize } from "./archive-tar-pax.js";
|
|
8
|
-
import { validateGnuMetadata } from "./archive-tar-gnu.js";
|
|
9
|
-
import { rawTarMember } from "./archive-tar-admission.js";
|
|
10
|
-
import { validateTarHeader } from "./archive-tar-header.js";
|
|
11
|
-
// Keep raw framing aligned with native/src/tar_meter.rs, before either parser.
|
|
12
|
-
export class TarMetadataMeter extends Transform {
|
|
13
|
-
limits;
|
|
14
|
-
onMember;
|
|
15
|
-
block = Buffer.alloc(512);
|
|
16
|
-
blockLength = 0;
|
|
17
|
-
state = { kind: "header" };
|
|
18
|
-
pendingPax;
|
|
19
|
-
pendingGnu = new Set();
|
|
20
|
-
pendingGnuPath;
|
|
21
|
-
zeroBlocks = 0;
|
|
22
|
-
entries = 0;
|
|
23
|
-
remainingDecodedBytes;
|
|
24
|
-
manifestBytes = 0;
|
|
25
|
-
constructor(limits, onMember) {
|
|
26
|
-
super();
|
|
27
|
-
this.limits = limits;
|
|
28
|
-
this.onMember = onMember;
|
|
29
|
-
if (!Number.isSafeInteger(limits.maxDecodedBytes) || limits.maxDecodedBytes < 0) {
|
|
30
|
-
throw new RangeError("maxDecodedBytes must be a non-negative safe integer");
|
|
31
|
-
}
|
|
32
|
-
this.remainingDecodedBytes = limits.maxDecodedBytes;
|
|
33
|
-
}
|
|
34
|
-
countMember() {
|
|
35
|
-
if (this.entries >= this.limits.maxEntries) {
|
|
36
|
-
throw new ArchiveLimitError(ARCHIVE_LIMIT_ERROR_CODE.ENTRY_COUNT_EXCEEDS_LIMIT);
|
|
37
|
-
}
|
|
38
|
-
this.entries += 1;
|
|
39
|
-
}
|
|
40
|
-
invalid(message) {
|
|
41
|
-
return new ArchiveFormatError(`invalid TAR header: ${message}`);
|
|
42
|
-
}
|
|
43
|
-
paddedSize(size) {
|
|
44
|
-
const padded = Math.ceil(size / 512) * 512;
|
|
45
|
-
if (!Number.isSafeInteger(padded))
|
|
46
|
-
throw this.invalid("entry padding exceeds the safe integer range");
|
|
47
|
-
return padded;
|
|
48
|
-
}
|
|
49
|
-
parseSize() {
|
|
50
|
-
const field = this.block.subarray(124, 136);
|
|
51
|
-
if ((field[0] ?? 0) & 0x80) {
|
|
52
|
-
if (field[0] !== 0x80) {
|
|
53
|
-
throw this.invalid("base-256 size is negative or malformed");
|
|
54
|
-
}
|
|
55
|
-
let value = 0n;
|
|
56
|
-
for (const byte of field.subarray(1)) {
|
|
57
|
-
value = (value << 8n) | BigInt(byte);
|
|
58
|
-
}
|
|
59
|
-
if (value > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
60
|
-
throw this.invalid("base-256 size exceeds the safe integer range");
|
|
61
|
-
}
|
|
62
|
-
return Number(value);
|
|
63
|
-
}
|
|
64
|
-
const zero = field.indexOf(0);
|
|
65
|
-
if (zero >= 0 && field.subarray(zero).some((byte) => byte !== 0 && byte !== 0x20)) {
|
|
66
|
-
throw this.invalid("size has non-padding bytes after NUL");
|
|
67
|
-
}
|
|
68
|
-
const bytes = field.subarray(0, zero < 0 ? field.length : zero);
|
|
69
|
-
if (bytes.some((byte) => byte > 0x7f))
|
|
70
|
-
throw this.invalid("size is not ASCII octal");
|
|
71
|
-
const text = bytes.toString("ascii").replace(/^ +| +$/g, "");
|
|
72
|
-
if (!text || !/^[0-7]+$/.test(text))
|
|
73
|
-
throw this.invalid("size is not valid octal");
|
|
74
|
-
const value = Number.parseInt(text, 8);
|
|
75
|
-
if (!Number.isSafeInteger(value))
|
|
76
|
-
throw this.invalid("octal size exceeds the safe integer range");
|
|
77
|
-
return value;
|
|
78
|
-
}
|
|
79
|
-
finishHeader() {
|
|
80
|
-
if (this.block.every((byte) => byte === 0)) {
|
|
81
|
-
if (this.pendingPax)
|
|
82
|
-
throw this.invalid("dangling PAX metadata");
|
|
83
|
-
if (this.pendingGnu.size)
|
|
84
|
-
throw this.invalid("dangling GNU metadata");
|
|
85
|
-
this.blockLength = 0;
|
|
86
|
-
this.zeroBlocks += 1;
|
|
87
|
-
this.state = { kind: this.zeroBlocks === 2 ? "eof" : "header" };
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
if (this.zeroBlocks !== 0)
|
|
91
|
-
throw this.invalid("nonzero header after one TAR zero block");
|
|
92
|
-
validateTarHeader(this.block);
|
|
93
|
-
const nameEnd = this.block.subarray(0, 100).indexOf(0);
|
|
94
|
-
const name = this.block.subarray(0, nameEnd < 0 ? 100 : nameEnd);
|
|
95
|
-
if (name.length === 0) {
|
|
96
|
-
throw this.invalid("entry path is empty");
|
|
97
|
-
}
|
|
98
|
-
if (![0x35, 0x44].includes(this.block[156]) && name.at(-1) === 0x2f) {
|
|
99
|
-
throw this.invalid("non-directory entry path ends with a separator");
|
|
100
|
-
}
|
|
101
|
-
let size = this.parseSize();
|
|
102
|
-
let padded = this.paddedSize(size);
|
|
103
|
-
const type = this.block[156];
|
|
104
|
-
// Directory/link headers cannot carry bodies; PAX/GNU metadata can.
|
|
105
|
-
if ([0x31, 0x32, 0x35].includes(type) && size !== 0) {
|
|
106
|
-
throw this.invalid("directory or link has a nonzero body size");
|
|
107
|
-
}
|
|
108
|
-
if ([0x78, 0x67, 0x4c, 0x4b, 0x58, 0x4e].includes(type) && size > this.limits.maxMetaEntryBytes) {
|
|
109
|
-
throw new ArchiveLimitError(ARCHIVE_LIMIT_ERROR_CODE.META_ENTRY_SIZE_EXCEEDS_LIMIT);
|
|
110
|
-
}
|
|
111
|
-
if (type === 0x67 || type === 0x58 || type === 0x4e) {
|
|
112
|
-
throw this.invalid("global/old PAX and old GNU metadata are not supported");
|
|
113
|
-
}
|
|
114
|
-
if (type === 0x78 || type === 0x4c || type === 0x4b) {
|
|
115
|
-
const metadataType = type === 0x78 ? "pax" : type === 0x4c ? "L" : "K";
|
|
116
|
-
if (this.pendingPax || size === 0 || (metadataType === "pax" ? this.pendingGnu.size : this.pendingGnu.has(metadataType))) {
|
|
117
|
-
throw this.invalid("empty, repeated or mixed PAX/GNU metadata");
|
|
118
|
-
}
|
|
119
|
-
const magic = this.block.subarray(257, 265).toString("latin1");
|
|
120
|
-
if (magic !== "ustar\0" + "00" && magic !== "ustar \0")
|
|
121
|
-
throw this.invalid("unrecognized PAX/GNU header format");
|
|
122
|
-
if (metadataType !== "pax")
|
|
123
|
-
this.pendingGnu.add(metadataType);
|
|
124
|
-
this.state = { kind: "metadata", type: metadataType, body: Buffer.alloc(size), used: 0, padding: padded - size };
|
|
125
|
-
this.blockLength = 0;
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
// Sparse headers retain their metadata-limit-before-format-error ordering.
|
|
129
|
-
if (this.pendingPax && type !== 0x53) {
|
|
130
|
-
size = paxMemberSize(this.pendingPax, type, size, this.block);
|
|
131
|
-
}
|
|
132
|
-
padded = this.paddedSize(size);
|
|
133
|
-
if (type === 0x53) {
|
|
134
|
-
if (this.block[482] !== 0 && this.block[482] !== 1) {
|
|
135
|
-
throw this.invalid("GNU sparse extension flag is not 0 or 1");
|
|
136
|
-
}
|
|
137
|
-
if (this.block[482] === 0) {
|
|
138
|
-
throw this.invalid("GNU sparse entries are not supported");
|
|
139
|
-
}
|
|
140
|
-
this.state = { kind: "sparse", dataRemaining: padded, metaBytes: 0 };
|
|
141
|
-
}
|
|
142
|
-
else {
|
|
143
|
-
this.countMember();
|
|
144
|
-
if (this.pendingGnuPath && /[\\/]$/.test(this.pendingGnuPath) && ![0x35, 0x44].includes(type)) {
|
|
145
|
-
throw this.invalid("GNU effective non-directory path ends with a separator");
|
|
146
|
-
}
|
|
147
|
-
const entry = rawTarMember(this.block, size, this.pendingPax?.path ?? this.pendingGnuPath);
|
|
148
|
-
const cost = tarManifestEntryCost(entry.path);
|
|
149
|
-
if (cost > this.limits.maxManifestBytes - this.manifestBytes) {
|
|
150
|
-
throw new ArchiveLimitError(ARCHIVE_LIMIT_ERROR_CODE.MANIFEST_SIZE_EXCEEDS_LIMIT);
|
|
151
|
-
}
|
|
152
|
-
this.manifestBytes += cost;
|
|
153
|
-
this.onMember?.(entry);
|
|
154
|
-
this.pendingPax = undefined;
|
|
155
|
-
this.pendingGnu.clear();
|
|
156
|
-
this.pendingGnuPath = undefined;
|
|
157
|
-
this.state = padded === 0 ? { kind: "header" } : { kind: "data", remaining: padded };
|
|
158
|
-
}
|
|
159
|
-
this.blockLength = 0;
|
|
160
|
-
}
|
|
161
|
-
finishSparseHeader(state) {
|
|
162
|
-
const metaBytes = state.metaBytes + 512;
|
|
163
|
-
if (metaBytes > this.limits.maxMetaEntryBytes) {
|
|
164
|
-
throw new ArchiveLimitError(ARCHIVE_LIMIT_ERROR_CODE.META_ENTRY_SIZE_EXCEEDS_LIMIT);
|
|
165
|
-
}
|
|
166
|
-
if (this.block[504] !== 0 && this.block[504] !== 1) {
|
|
167
|
-
throw this.invalid("GNU sparse extension flag is not 0 or 1");
|
|
168
|
-
}
|
|
169
|
-
if (this.block[504] === 1) {
|
|
170
|
-
this.state = { ...state, metaBytes };
|
|
171
|
-
}
|
|
172
|
-
else {
|
|
173
|
-
throw this.invalid("GNU sparse entries are not supported");
|
|
174
|
-
}
|
|
175
|
-
this.blockLength = 0;
|
|
176
|
-
}
|
|
177
|
-
meter(chunk) {
|
|
178
|
-
let offset = 0;
|
|
179
|
-
while (offset < chunk.length) {
|
|
180
|
-
if (this.state.kind === "eof") {
|
|
181
|
-
if (chunk.subarray(offset).some((byte) => byte !== 0))
|
|
182
|
-
throw this.invalid("nonzero data after TAR EOF");
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
if (this.state.kind === "metadata") {
|
|
186
|
-
const state = this.state;
|
|
187
|
-
const take = Math.min(state.body.length - state.used, chunk.length - offset);
|
|
188
|
-
chunk.copy(state.body, state.used, offset, offset + take);
|
|
189
|
-
state.used += take;
|
|
190
|
-
offset += take;
|
|
191
|
-
if (state.used === state.body.length) {
|
|
192
|
-
if (state.type === "pax")
|
|
193
|
-
this.pendingPax = parseLocalPax(state.body);
|
|
194
|
-
else {
|
|
195
|
-
const name = validateGnuMetadata(state.body, state.type);
|
|
196
|
-
if (state.type === "L")
|
|
197
|
-
this.pendingGnuPath = name;
|
|
198
|
-
}
|
|
199
|
-
this.state = state.padding === 0 ? { kind: "header" } : { kind: "data", remaining: state.padding };
|
|
200
|
-
}
|
|
201
|
-
continue;
|
|
202
|
-
}
|
|
203
|
-
if (this.state.kind === "data") {
|
|
204
|
-
const take = Math.min(this.state.remaining, chunk.length - offset);
|
|
205
|
-
offset += take;
|
|
206
|
-
const remaining = this.state.remaining - take;
|
|
207
|
-
this.state = remaining === 0 ? { kind: "header" } : { kind: "data", remaining };
|
|
208
|
-
continue;
|
|
209
|
-
}
|
|
210
|
-
const take = Math.min(512 - this.blockLength, chunk.length - offset);
|
|
211
|
-
chunk.copy(this.block, this.blockLength, offset, offset + take);
|
|
212
|
-
this.blockLength += take;
|
|
213
|
-
offset += take;
|
|
214
|
-
if (this.blockLength === 512) {
|
|
215
|
-
if (this.state.kind === "sparse")
|
|
216
|
-
this.finishSparseHeader(this.state);
|
|
217
|
-
else
|
|
218
|
-
this.finishHeader();
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
_transform(chunk, _encoding, callback) {
|
|
223
|
-
try {
|
|
224
|
-
const admitted = Math.min(chunk.length, this.remainingDecodedBytes);
|
|
225
|
-
this.meter(chunk.subarray(0, admitted));
|
|
226
|
-
this.remainingDecodedBytes -= admitted;
|
|
227
|
-
if (admitted < chunk.length) {
|
|
228
|
-
// Inspect only the overflow probe, never scan an unbounded tail.
|
|
229
|
-
if (this.state.kind === "eof" && chunk[admitted] !== 0)
|
|
230
|
-
throw this.invalid("nonzero data after TAR EOF");
|
|
231
|
-
throw new ArchiveLimitError(ARCHIVE_LIMIT_ERROR_CODE.DECODED_SIZE_EXCEEDS_LIMIT);
|
|
232
|
-
}
|
|
233
|
-
callback(null, chunk);
|
|
234
|
-
}
|
|
235
|
-
catch (error) {
|
|
236
|
-
callback(error instanceof Error ? error : new Error(String(error)));
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
_flush(callback) {
|
|
240
|
-
if (this.pendingPax)
|
|
241
|
-
callback(this.invalid("dangling PAX metadata"));
|
|
242
|
-
else if (this.pendingGnu.size)
|
|
243
|
-
callback(this.invalid("dangling GNU metadata"));
|
|
244
|
-
else if (this.state.kind === "eof")
|
|
245
|
-
callback();
|
|
246
|
-
else if (this.state.kind === "header" && this.blockLength === 0)
|
|
247
|
-
callback(this.invalid("missing two-block TAR EOF"));
|
|
248
|
-
else
|
|
249
|
-
callback(this.invalid(this.state.kind === "header" ? "truncated TAR header" : "truncated TAR entry"));
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
async function isGzip(filePath) {
|
|
253
|
-
const handle = await fs.promises.open(filePath, "r");
|
|
254
|
-
try {
|
|
255
|
-
const magic = Buffer.alloc(2);
|
|
256
|
-
const { bytesRead } = await handle.read(magic, 0, 2, 0);
|
|
257
|
-
return bytesRead === 2 && magic[0] === 0x1f && magic[1] === 0x8b;
|
|
258
|
-
}
|
|
259
|
-
finally {
|
|
260
|
-
await handle.close();
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
export async function preflightTarMetadata(params) {
|
|
264
|
-
const meter = new TarMetadataMeter(params.limits, params.onMember);
|
|
265
|
-
const sink = new Writable({ write(_chunk, _encoding, callback) { callback(); } });
|
|
266
|
-
const gzip = await isGzip(params.archivePath);
|
|
267
|
-
const decoder = gzip ? createGunzip() : undefined;
|
|
268
|
-
const input = fs.createReadStream(params.archivePath);
|
|
269
|
-
try {
|
|
270
|
-
await (decoder
|
|
271
|
-
? pipeline(input, decoder, meter, sink, { signal: params.signal })
|
|
272
|
-
: pipeline(input, meter, sink, { signal: params.signal }));
|
|
273
|
-
}
|
|
274
|
-
finally {
|
|
275
|
-
input.destroy();
|
|
276
|
-
}
|
|
277
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export type LocalPax = {
|
|
2
|
-
path?: string;
|
|
3
|
-
linkpath?: string;
|
|
4
|
-
size?: number;
|
|
5
|
-
};
|
|
6
|
-
export declare function parseLocalPax(body: Buffer): LocalPax;
|
|
7
|
-
export declare function paxMemberSize(pax: LocalPax, type: number, rawSize: number, header: Buffer): number;
|
|
8
|
-
//# sourceMappingURL=archive-tar-pax.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"archive-tar-pax.d.ts","sourceRoot":"","sources":["../src/archive-tar-pax.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,QAAQ,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAqB3E,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAwCpD;AAQD,wBAAgB,aAAa,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAclG"}
|