@openclaw/fs-safe 0.8.0 → 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.
Files changed (69) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +1 -1
  3. package/dist/archive-entry.d.ts.map +1 -1
  4. package/dist/archive-entry.js +5 -0
  5. package/dist/archive-gzip-tail.d.ts +18 -0
  6. package/dist/archive-gzip-tail.d.ts.map +1 -0
  7. package/dist/archive-gzip-tail.js +74 -0
  8. package/dist/archive-parser.wasm +0 -0
  9. package/dist/archive-read.d.ts.map +1 -1
  10. package/dist/archive-read.js +18 -60
  11. package/dist/archive-tar-extract.d.ts +11 -0
  12. package/dist/archive-tar-extract.d.ts.map +1 -0
  13. package/dist/archive-tar-extract.js +49 -0
  14. package/dist/archive-tar-stream.d.ts +18 -0
  15. package/dist/archive-tar-stream.d.ts.map +1 -0
  16. package/dist/archive-tar-stream.js +95 -0
  17. package/dist/archive-tar-wasm.d.ts +18 -0
  18. package/dist/archive-tar-wasm.d.ts.map +1 -0
  19. package/dist/archive-tar-wasm.js +102 -0
  20. package/dist/archive-tar.d.ts +0 -1
  21. package/dist/archive-tar.d.ts.map +1 -1
  22. package/dist/archive-tar.js +0 -22
  23. package/dist/archive.d.ts.map +1 -1
  24. package/dist/archive.js +2 -109
  25. package/dist/device-path.d.ts +1 -0
  26. package/dist/device-path.d.ts.map +1 -1
  27. package/dist/device-path.js +5 -2
  28. package/dist/directory-mode-owner.d.ts.map +1 -1
  29. package/dist/directory-mode-owner.js +3 -0
  30. package/dist/replace-file-descriptor.d.ts.map +1 -1
  31. package/dist/replace-file-descriptor.js +2 -1
  32. package/dist/secret-file.d.ts.map +1 -1
  33. package/dist/secret-file.js +2 -0
  34. package/dist/secret-read-async.d.ts.map +1 -1
  35. package/dist/secret-read-async.js +3 -0
  36. package/dist/sidecar-lock-acquire.d.ts +1 -0
  37. package/dist/sidecar-lock-acquire.d.ts.map +1 -1
  38. package/dist/sidecar-lock-acquire.js +6 -0
  39. package/dist/sidecar-lock-types.d.ts +7 -0
  40. package/dist/sidecar-lock-types.d.ts.map +1 -1
  41. package/dist/sidecar-lock.d.ts +2 -0
  42. package/dist/sidecar-lock.d.ts.map +1 -1
  43. package/dist/sidecar-lock.js +19 -4
  44. package/docs/archive.md +66 -61
  45. package/docs/contributing.md +35 -1
  46. package/docs/install.md +1 -1
  47. package/docs/native-helper.md +5 -0
  48. package/docs/native.md +16 -10
  49. package/docs/secret-file.md +5 -0
  50. package/docs/sidecar-lock.md +2 -1
  51. package/package.json +19 -16
  52. package/dist/archive-tar-admission.d.ts +0 -7
  53. package/dist/archive-tar-admission.d.ts.map +0 -1
  54. package/dist/archive-tar-admission.js +0 -43
  55. package/dist/archive-tar-gnu.d.ts +0 -2
  56. package/dist/archive-tar-gnu.d.ts.map +0 -1
  57. package/dist/archive-tar-gnu.js +0 -20
  58. package/dist/archive-tar-header.d.ts +0 -8
  59. package/dist/archive-tar-header.d.ts.map +0 -1
  60. package/dist/archive-tar-header.js +0 -47
  61. package/dist/archive-tar-meta.d.ts +0 -34
  62. package/dist/archive-tar-meta.d.ts.map +0 -1
  63. package/dist/archive-tar-meta.js +0 -277
  64. package/dist/archive-tar-pax.d.ts +0 -8
  65. package/dist/archive-tar-pax.d.ts.map +0 -1
  66. package/dist/archive-tar-pax.js +0 -100
  67. package/dist/archive-tar-runtime.d.ts +0 -49
  68. package/dist/archive-tar-runtime.d.ts.map +0 -1
  69. package/dist/archive-tar-runtime.js +0 -22
@@ -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"}
@@ -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"}
@@ -1,100 +0,0 @@
1
- import { ArchiveFormatError } from "./archive-errors.js";
2
- function invalid() {
3
- throw new ArchiveFormatError("invalid TAR header: unsupported or malformed PAX metadata");
4
- }
5
- function decimal(bytes) {
6
- const text = bytes.toString("latin1");
7
- if (!/^(0|[1-9][0-9]*)$/.test(text))
8
- invalid();
9
- const value = Number(text);
10
- if (!Number.isSafeInteger(value))
11
- invalid();
12
- return value;
13
- }
14
- function ascii(bytes) {
15
- if (bytes.length === 0 || bytes.some((byte) => byte < 0x20 || byte > 0x7e))
16
- invalid();
17
- return bytes.toString("ascii");
18
- }
19
- // Keep this grammar aligned with native/src/tar_pax.rs. Downstream parsers
20
- // disagree on duplicates, coercions and UTF-8 chunk boundaries.
21
- export function parseLocalPax(body) {
22
- if (body.length === 0)
23
- invalid();
24
- const result = {};
25
- const keys = new Set();
26
- let offset = 0;
27
- while (offset < body.length) {
28
- const space = body.indexOf(0x20, offset);
29
- if (space < 0)
30
- invalid();
31
- const length = decimal(body.subarray(offset, space));
32
- if (length > body.length - offset || length <= space - offset + 3)
33
- invalid();
34
- const end = offset + length;
35
- if (body[end - 1] !== 0x0a)
36
- invalid();
37
- const record = body.subarray(space + 1, end - 1);
38
- if (record.includes(0x0a))
39
- invalid();
40
- const equals = record.indexOf(0x3d);
41
- if (equals <= 0)
42
- invalid();
43
- const key = ascii(record.subarray(0, equals));
44
- if (!/^[A-Za-z0-9_.-]+$/.test(key) || keys.has(key))
45
- invalid();
46
- keys.add(key);
47
- const value = record.subarray(equals + 1);
48
- if (/^(LIBARCHIVE|SCHILY)\.xattr\..+$/.test(key)) {
49
- // Inert bytes, never restored. Newlines would corrupt Rust's subsequent
50
- // record lookup; NUL and non-UTF8 bytes in these values are harmless.
51
- }
52
- else if (key === "path" || key === "linkpath") {
53
- result[key] = ascii(value);
54
- }
55
- else if (key === "size") {
56
- result.size = decimal(value);
57
- }
58
- else if (key === "uid" || key === "gid") {
59
- decimal(value);
60
- }
61
- else if (key === "uname" || key === "gname") {
62
- ascii(value);
63
- }
64
- else if (key === "mtime" || key === "atime" || key === "ctime") {
65
- const text = ascii(value);
66
- if (!/^-?(0|[1-9][0-9]*)(\.[0-9]+)?$/.test(text) || Math.abs(Number(text)) > 8_640_000_000_000)
67
- invalid();
68
- }
69
- else {
70
- invalid();
71
- }
72
- offset = end;
73
- }
74
- return result;
75
- }
76
- function rawText(field) {
77
- const zero = field.indexOf(0);
78
- const value = field.subarray(0, zero < 0 ? field.length : zero);
79
- return value.length === 0 ? "" : ascii(value);
80
- }
81
- export function paxMemberSize(pax, type, rawSize, header) {
82
- if (![0, 0x30, 0x31, 0x32, 0x35, 0x37].includes(type))
83
- invalid();
84
- const rawName = rawText(header.subarray(0, 100));
85
- const rawLink = rawText(header.subarray(157, 257));
86
- if (header.subarray(257, 265).equals(Buffer.from("ustar\0" + "00"))) {
87
- rawText(header.subarray(345, 500));
88
- }
89
- const isLink = type === 0x31 || type === 0x32;
90
- if (isLink !== (rawLink.length > 0))
91
- invalid();
92
- const size = pax.size ?? rawSize;
93
- if ([0x31, 0x32, 0x35].includes(type) && (rawSize !== 0 || size !== 0))
94
- invalid();
95
- if (type !== 0x35 && (pax.path?.endsWith("/") || pax.path?.endsWith("\\") || rawName.endsWith("\\")))
96
- invalid();
97
- if (pax.linkpath !== undefined && !isLink)
98
- invalid();
99
- return size;
100
- }
@@ -1,49 +0,0 @@
1
- export type TarParserEntry = {
2
- meta?: boolean;
3
- size: number;
4
- type?: string;
5
- resume(): void;
6
- };
7
- export type TarParser = NodeJS.WritableStream & {
8
- abort(error: Error): void;
9
- on(event: "ignoredEntry", listener: (entry: TarParserEntry) => void): TarParser;
10
- on(event: "entry", listener: (entry: TarParserEntry) => void): TarParser;
11
- on(event: "meta", listener: (metadata: string) => void): TarParser;
12
- on(event: "error", listener: (error: Error) => void): TarParser;
13
- on(event: "end", listener: () => void): TarParser;
14
- };
15
- export declare const NODE_TAR_RATIO_DISABLED: number;
16
- export type TarModule = {
17
- Parser: new (options: {
18
- strict: true;
19
- maxMetaEntrySize: number;
20
- }) => TarParser;
21
- x(options: {
22
- cwd: string;
23
- strip: number;
24
- gzip?: boolean;
25
- signal?: AbortSignal;
26
- preservePaths: false;
27
- noChmod: true;
28
- preserveOwner: false;
29
- noMtime: true;
30
- dmode: number;
31
- strict: true;
32
- maxMetaEntrySize: number;
33
- maxDecompressionRatio: number;
34
- filter?(this: TarParser, entryPath: string, entry: unknown): boolean;
35
- onReadEntry(this: unknown, entry: unknown): void;
36
- }): TarParser;
37
- t(options: {
38
- file: string;
39
- strict: true;
40
- maxMetaEntrySize: number;
41
- maxDecompressionRatio: number;
42
- onReadEntry(entry: AsyncIterable<unknown> & {
43
- resume(): void;
44
- }): void;
45
- }): Promise<unknown>;
46
- };
47
- export declare function importOptionalTar(): Promise<TarModule>;
48
- export declare function normalizeTarParserError(error: unknown): unknown;
49
- //# sourceMappingURL=archive-tar-runtime.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"archive-tar-runtime.d.ts","sourceRoot":"","sources":["../src/archive-tar-runtime.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,IAAI,IAAI,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,cAAc,GAAG;IAC9C,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAC1B,EAAE,CAAC,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,GAAG,SAAS,CAAC;IAChF,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,GAAG,SAAS,CAAC;IACzE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,SAAS,CAAC;IACnE,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,SAAS,CAAC;IAChE,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,SAAS,CAAC;CACnD,CAAC;AAIF,eAAO,MAAM,uBAAuB,QAA2B,CAAC;AAEhE,MAAM,MAAM,SAAS,GAAG;IACtB,MAAM,EAAE,KAAK,OAAO,EAAE;QAAE,MAAM,EAAE,IAAI,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAE,KAAK,SAAS,CAAC;IAC/E,CAAC,CAAC,OAAO,EAAE;QACT,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,aAAa,EAAE,KAAK,CAAC;QACrB,OAAO,EAAE,IAAI,CAAC;QACd,aAAa,EAAE,KAAK,CAAC;QACrB,OAAO,EAAE,IAAI,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,IAAI,CAAC;QACb,gBAAgB,EAAE,MAAM,CAAC;QACzB,qBAAqB,EAAE,MAAM,CAAC;QAC9B,MAAM,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;QACrE,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;KAClD,GAAG,SAAS,CAAC;IACd,CAAC,CAAC,OAAO,EAAE;QACT,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,IAAI,CAAC;QACb,gBAAgB,EAAE,MAAM,CAAC;QACzB,qBAAqB,EAAE,MAAM,CAAC;QAC9B,WAAW,CAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,GAAG;YAAE,MAAM,IAAI,IAAI,CAAA;SAAE,GAAG,IAAI,CAAC;KACvE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACtB,CAAC;AAEF,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,SAAS,CAAC,CAS5D;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAS/D"}
@@ -1,22 +0,0 @@
1
- import { ArchiveFormatError } from "./archive-errors.js";
2
- // Both callers parse an immutable staged stream only after fs-safe's complete
3
- // decoded-byte admission, so node-tar must not add backend-specific policy.
4
- export const NODE_TAR_RATIO_DISABLED = Number.POSITIVE_INFINITY;
5
- export async function importOptionalTar() {
6
- try {
7
- return await import("tar");
8
- }
9
- catch (cause) {
10
- throw new Error('Optional archive dependency "tar" is not installed. Install it to use TAR archive helpers from @openclaw/fs-safe/archive.', { cause });
11
- }
12
- }
13
- export function normalizeTarParserError(error) {
14
- const code = error?.code;
15
- if (typeof code !== "string" || !code.startsWith("TAR_")) {
16
- return error;
17
- }
18
- const message = error instanceof Error ? error.message : String(error);
19
- return new ArchiveFormatError(`invalid TAR archive: ${message}`, {
20
- cause: error instanceof Error ? error : undefined,
21
- });
22
- }