@openclaw/fs-safe 0.4.7 → 0.5.1
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 +58 -0
- package/README.md +82 -43
- package/dist/archive-entry.d.ts.map +1 -1
- package/dist/archive-entry.js +5 -4
- package/dist/archive-errors.d.ts +11 -0
- package/dist/archive-errors.d.ts.map +1 -0
- package/dist/archive-errors.js +16 -0
- package/dist/archive-input.d.ts +12 -0
- package/dist/archive-input.d.ts.map +1 -0
- package/dist/archive-input.js +72 -0
- package/dist/archive-kind.d.ts +1 -1
- package/dist/archive-kind.d.ts.map +1 -1
- package/dist/archive-kind.js +18 -0
- package/dist/archive-limits.d.ts +10 -0
- package/dist/archive-limits.d.ts.map +1 -1
- package/dist/archive-limits.js +18 -0
- package/dist/archive-native.d.ts +18 -0
- package/dist/archive-native.d.ts.map +1 -0
- package/dist/archive-native.js +115 -0
- package/dist/archive-options.d.ts +21 -0
- package/dist/archive-options.d.ts.map +1 -0
- package/dist/archive-options.js +1 -0
- package/dist/archive-policy.d.ts +20 -0
- package/dist/archive-policy.d.ts.map +1 -0
- package/dist/archive-policy.js +29 -0
- package/dist/archive-read.d.ts +6 -0
- package/dist/archive-read.d.ts.map +1 -0
- package/dist/archive-read.js +200 -0
- package/dist/archive-staging.d.ts +2 -5
- package/dist/archive-staging.d.ts.map +1 -1
- package/dist/archive-staging.js +19 -10
- package/dist/archive-tar-meta.d.ts +6 -0
- package/dist/archive-tar-meta.d.ts.map +1 -0
- package/dist/archive-tar-meta.js +143 -0
- package/dist/archive-tar-runtime.d.ts +43 -0
- package/dist/archive-tar-runtime.d.ts.map +1 -0
- package/dist/archive-tar-runtime.js +8 -0
- package/dist/archive-tar.d.ts +5 -1
- package/dist/archive-tar.d.ts.map +1 -1
- package/dist/archive-tar.js +24 -6
- package/dist/archive-zip-entry.d.ts +15 -0
- package/dist/archive-zip-entry.d.ts.map +1 -0
- package/dist/archive-zip-entry.js +17 -0
- package/dist/archive.d.ts +7 -17
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +111 -108
- package/dist/atomic.d.ts +1 -1
- package/dist/atomic.d.ts.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -1
- package/dist/containment.d.ts +2 -0
- package/dist/containment.d.ts.map +1 -0
- package/dist/containment.js +1 -0
- package/dist/device-path.d.ts.map +1 -1
- package/dist/device-path.js +24 -2
- package/dist/durability.d.ts +2 -0
- package/dist/durability.d.ts.map +1 -1
- package/dist/durability.js +2 -0
- package/dist/errors.d.ts +4 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +2 -0
- package/dist/file-hash.d.ts +10 -0
- package/dist/file-hash.d.ts.map +1 -0
- package/dist/file-hash.js +73 -0
- package/dist/file-lock-sync.d.ts +35 -0
- package/dist/file-lock-sync.d.ts.map +1 -0
- package/dist/file-lock-sync.js +233 -0
- package/dist/file-lock.d.ts +2 -0
- package/dist/file-lock.d.ts.map +1 -1
- package/dist/file-lock.js +1 -0
- package/dist/filename.d.ts.map +1 -1
- package/dist/filename.js +4 -1
- package/dist/guarded-mkdir.d.ts +7 -1
- package/dist/guarded-mkdir.d.ts.map +1 -1
- package/dist/guarded-mkdir.js +45 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/json-document-store.d.ts.map +1 -1
- package/dist/json-document-store.js +30 -15
- package/dist/move-path.d.ts.map +1 -1
- package/dist/move-path.js +101 -17
- package/dist/native/darwin-arm64/fs-safe-native.node +0 -0
- package/dist/native/darwin-x64/fs-safe-native.node +0 -0
- package/dist/native/linux-arm64-gnu/fs-safe-native.node +0 -0
- package/dist/native/linux-arm64-musl/fs-safe-native.node +0 -0
- package/dist/native/linux-x64-gnu/fs-safe-native.node +0 -0
- package/dist/native/linux-x64-musl/fs-safe-native.node +0 -0
- package/dist/native/win32-x64-msvc/fs-safe-native.node +0 -0
- package/dist/native-binding.d.ts +79 -0
- package/dist/native-binding.d.ts.map +1 -0
- package/dist/native-binding.js +1 -0
- package/dist/native-config.d.ts +17 -0
- package/dist/native-config.d.ts.map +1 -0
- package/dist/native-config.js +71 -0
- package/dist/native-operations.d.ts +21 -0
- package/dist/native-operations.d.ts.map +1 -0
- package/dist/native-operations.js +115 -0
- package/dist/native-pinned-write.d.ts +5 -0
- package/dist/native-pinned-write.d.ts.map +1 -0
- package/dist/native-pinned-write.js +115 -0
- package/dist/native.d.ts +14 -0
- package/dist/native.d.ts.map +1 -0
- package/dist/native.js +204 -0
- package/dist/output-sibling.d.ts +8 -0
- package/dist/output-sibling.d.ts.map +1 -0
- package/dist/output-sibling.js +122 -0
- package/dist/output.d.ts +2 -0
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +32 -5
- package/dist/owner-dacl.d.ts +31 -0
- package/dist/owner-dacl.d.ts.map +1 -0
- package/dist/owner-dacl.js +27 -0
- package/dist/path-policy.js +1 -1
- package/dist/path.d.ts.map +1 -1
- package/dist/path.js +3 -2
- package/dist/permissions-public.d.ts +2 -0
- package/dist/permissions-public.d.ts.map +1 -1
- package/dist/permissions-public.js +2 -0
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +6 -0
- package/dist/pinned-operation.d.ts +2 -0
- package/dist/pinned-operation.d.ts.map +1 -0
- package/dist/pinned-operation.js +35 -0
- package/dist/pinned-write.d.ts +3 -16
- package/dist/pinned-write.d.ts.map +1 -1
- package/dist/pinned-write.js +20 -85
- package/dist/private-directory.d.ts +5 -0
- package/dist/private-directory.d.ts.map +1 -0
- package/dist/private-directory.js +13 -0
- package/dist/private-temp-workspace.d.ts +6 -2
- package/dist/private-temp-workspace.d.ts.map +1 -1
- package/dist/private-temp-workspace.js +40 -13
- package/dist/publish-file-failure.d.ts +27 -0
- package/dist/publish-file-failure.d.ts.map +1 -0
- package/dist/publish-file-failure.js +21 -0
- package/dist/publish-file.d.ts +21 -0
- package/dist/publish-file.d.ts.map +1 -0
- package/dist/publish-file.js +351 -0
- package/dist/read-opened-file.d.ts +3 -0
- package/dist/read-opened-file.d.ts.map +1 -1
- package/dist/read-opened-file.js +1 -0
- package/dist/replace-file-copy-fallback.d.ts +34 -0
- package/dist/replace-file-copy-fallback.d.ts.map +1 -0
- package/dist/replace-file-copy-fallback.js +339 -0
- package/dist/replace-file.d.ts +6 -2
- package/dist/replace-file.d.ts.map +1 -1
- package/dist/replace-file.js +39 -90
- package/dist/root-context.d.ts +4 -1
- package/dist/root-context.d.ts.map +1 -1
- package/dist/root-context.js +20 -1
- package/dist/root-impl.d.ts +5 -0
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +128 -225
- package/dist/root-path-existing.d.ts +3 -0
- package/dist/root-path-existing.d.ts.map +1 -0
- package/dist/root-path-existing.js +69 -0
- package/dist/root-path.d.ts +1 -2
- package/dist/root-path.d.ts.map +1 -1
- package/dist/root-path.js +67 -81
- package/dist/root-walk.d.ts +41 -0
- package/dist/root-walk.d.ts.map +1 -0
- package/dist/root-walk.js +114 -0
- package/dist/root.d.ts +2 -0
- package/dist/root.d.ts.map +1 -1
- package/dist/safe-path-segment.d.ts.map +1 -1
- package/dist/safe-path-segment.js +16 -4
- package/dist/secret-file.d.ts +5 -2
- package/dist/secret-file.d.ts.map +1 -1
- package/dist/secret-file.js +20 -2
- package/dist/secret-read-async.d.ts +4 -0
- package/dist/secret-read-async.d.ts.map +1 -0
- package/dist/secret-read-async.js +95 -0
- package/dist/secret.d.ts +2 -1
- package/dist/secret.d.ts.map +1 -1
- package/dist/secret.js +2 -1
- package/dist/sibling-temp.d.ts.map +1 -1
- package/dist/sibling-temp.js +1 -0
- package/dist/sidecar-lock-handle.d.ts +20 -0
- package/dist/sidecar-lock-handle.d.ts.map +1 -0
- package/dist/sidecar-lock-handle.js +28 -0
- package/dist/sidecar-lock-policy.d.ts +10 -0
- package/dist/sidecar-lock-policy.d.ts.map +1 -0
- package/dist/sidecar-lock-policy.js +29 -0
- package/dist/sidecar-lock-reclaim.d.ts +22 -6
- package/dist/sidecar-lock-reclaim.d.ts.map +1 -1
- package/dist/sidecar-lock-reclaim.js +106 -20
- package/dist/sidecar-lock-types.d.ts +56 -0
- package/dist/sidecar-lock-types.d.ts.map +1 -0
- package/dist/sidecar-lock-types.js +1 -0
- package/dist/sidecar-lock.d.ts +2 -45
- package/dist/sidecar-lock.d.ts.map +1 -1
- package/dist/sidecar-lock.js +94 -55
- package/dist/temp-cleanup.d.ts +7 -1
- package/dist/temp-cleanup.d.ts.map +1 -1
- package/dist/temp-cleanup.js +35 -5
- package/dist/temp-target.d.ts.map +1 -1
- package/dist/temp-target.js +51 -3
- package/dist/temp.d.ts +2 -1
- package/dist/temp.d.ts.map +1 -1
- package/dist/test-hooks.d.ts +3 -0
- package/dist/test-hooks.d.ts.map +1 -1
- package/dist/windows-permissions-native.d.ts +9 -0
- package/dist/windows-permissions-native.d.ts.map +1 -0
- package/dist/windows-permissions-native.js +31 -0
- package/docs/archive.md +131 -13
- package/docs/atomic.md +30 -1
- package/docs/config.md +51 -21
- package/docs/durability.md +211 -1
- package/docs/errors.md +36 -6
- package/docs/filename.md +0 -0
- package/docs/index.md +11 -8
- package/docs/install.md +27 -26
- package/docs/json-store.md +18 -3
- package/docs/migrating-to-0.5.md +196 -0
- package/docs/native-helper.md +85 -0
- package/docs/native.md +134 -0
- package/docs/output.md +37 -11
- package/docs/path.md +1 -1
- package/docs/permissions.md +84 -1
- package/docs/quickstart.md +5 -2
- package/docs/reading.md +4 -4
- package/docs/root.md +31 -15
- package/docs/secret-file.md +50 -1
- package/docs/security-model.md +29 -8
- package/docs/sidecar-lock.md +92 -7
- package/docs/temp.md +24 -1
- package/docs/test-hooks.md +22 -2
- package/docs/testing.md +18 -7
- package/docs/types.md +3 -1
- package/docs/walk.md +55 -0
- package/docs/writing.md +3 -3
- package/package.json +10 -4
- package/dist/pinned-helper.d.ts +0 -9
- package/dist/pinned-helper.d.ts.map +0 -1
- package/dist/pinned-helper.js +0 -19
- package/dist/pinned-path.d.ts +0 -7
- package/dist/pinned-path.d.ts.map +0 -1
- package/dist/pinned-path.js +0 -21
- package/dist/pinned-python-config.d.ts +0 -9
- package/dist/pinned-python-config.d.ts.map +0 -1
- package/dist/pinned-python-config.js +0 -38
- package/dist/pinned-python.d.ts +0 -12
- package/dist/pinned-python.d.ts.map +0 -1
- package/dist/pinned-python.js +0 -688
- package/docs/python-helper.md +0 -103
|
@@ -0,0 +1,143 @@
|
|
|
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 } from "./archive-limits.js";
|
|
7
|
+
class TarMetadataMeter extends Transform {
|
|
8
|
+
maxMetaEntryBytes;
|
|
9
|
+
block = Buffer.alloc(512);
|
|
10
|
+
blockLength = 0;
|
|
11
|
+
state = { kind: "header" };
|
|
12
|
+
constructor(maxMetaEntryBytes) {
|
|
13
|
+
super();
|
|
14
|
+
this.maxMetaEntryBytes = maxMetaEntryBytes;
|
|
15
|
+
}
|
|
16
|
+
invalid(message) {
|
|
17
|
+
return new ArchiveFormatError(`invalid TAR header: ${message}`);
|
|
18
|
+
}
|
|
19
|
+
parseSize() {
|
|
20
|
+
const field = this.block.subarray(124, 136);
|
|
21
|
+
if ((field[0] ?? 0) & 0x80) {
|
|
22
|
+
const value = field.readBigUInt64BE(4);
|
|
23
|
+
if (value > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
24
|
+
throw this.invalid("base-256 size exceeds the safe integer range");
|
|
25
|
+
}
|
|
26
|
+
return Number(value);
|
|
27
|
+
}
|
|
28
|
+
const zero = field.indexOf(0);
|
|
29
|
+
const text = field.subarray(0, zero < 0 ? field.length : zero).toString("ascii").trim();
|
|
30
|
+
if (!text || !/^[0-7]+$/.test(text))
|
|
31
|
+
throw this.invalid("size is not valid octal");
|
|
32
|
+
const value = Number.parseInt(text, 8);
|
|
33
|
+
if (!Number.isSafeInteger(value))
|
|
34
|
+
throw this.invalid("octal size exceeds the safe integer range");
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
finishHeader() {
|
|
38
|
+
if (this.block.every((byte) => byte === 0)) {
|
|
39
|
+
this.blockLength = 0;
|
|
40
|
+
this.state = { kind: "header" };
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const size = this.parseSize();
|
|
44
|
+
const type = this.block[156];
|
|
45
|
+
if ([0x78, 0x67, 0x4c, 0x4b, 0x58].includes(type ?? -1) && size > this.maxMetaEntryBytes) {
|
|
46
|
+
throw new ArchiveLimitError(ARCHIVE_LIMIT_ERROR_CODE.META_ENTRY_SIZE_EXCEEDS_LIMIT);
|
|
47
|
+
}
|
|
48
|
+
if (type === 0x78 || type === 0x67 || type === 0x58) {
|
|
49
|
+
throw this.invalid("PAX metadata is unmeterable without interpreting content");
|
|
50
|
+
}
|
|
51
|
+
const padded = Math.ceil(size / 512) * 512;
|
|
52
|
+
if (!Number.isSafeInteger(padded))
|
|
53
|
+
throw this.invalid("entry padding exceeds the safe integer range");
|
|
54
|
+
if (type === 0x53) {
|
|
55
|
+
if (this.block[482] !== 0 && this.block[482] !== 1) {
|
|
56
|
+
throw this.invalid("GNU sparse extension flag is not 0 or 1");
|
|
57
|
+
}
|
|
58
|
+
if (this.block[482] === 0) {
|
|
59
|
+
throw this.invalid("GNU sparse entries are not supported");
|
|
60
|
+
}
|
|
61
|
+
this.state = { kind: "sparse", dataRemaining: padded, metaBytes: 0 };
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
this.state = padded === 0 ? { kind: "header" } : { kind: "data", remaining: padded };
|
|
65
|
+
}
|
|
66
|
+
this.blockLength = 0;
|
|
67
|
+
}
|
|
68
|
+
finishSparseHeader(state) {
|
|
69
|
+
const metaBytes = state.metaBytes + 512;
|
|
70
|
+
if (metaBytes > this.maxMetaEntryBytes) {
|
|
71
|
+
throw new ArchiveLimitError(ARCHIVE_LIMIT_ERROR_CODE.META_ENTRY_SIZE_EXCEEDS_LIMIT);
|
|
72
|
+
}
|
|
73
|
+
if (this.block[504] !== 0 && this.block[504] !== 1) {
|
|
74
|
+
throw this.invalid("GNU sparse extension flag is not 0 or 1");
|
|
75
|
+
}
|
|
76
|
+
if (this.block[504] === 1) {
|
|
77
|
+
this.state = { ...state, metaBytes };
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
throw this.invalid("GNU sparse entries are not supported");
|
|
81
|
+
}
|
|
82
|
+
this.blockLength = 0;
|
|
83
|
+
}
|
|
84
|
+
meter(chunk) {
|
|
85
|
+
let offset = 0;
|
|
86
|
+
while (offset < chunk.length) {
|
|
87
|
+
if (this.state.kind === "data") {
|
|
88
|
+
const take = Math.min(this.state.remaining, chunk.length - offset);
|
|
89
|
+
offset += take;
|
|
90
|
+
const remaining = this.state.remaining - take;
|
|
91
|
+
this.state = remaining === 0 ? { kind: "header" } : { kind: "data", remaining };
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const take = Math.min(512 - this.blockLength, chunk.length - offset);
|
|
95
|
+
chunk.copy(this.block, this.blockLength, offset, offset + take);
|
|
96
|
+
this.blockLength += take;
|
|
97
|
+
offset += take;
|
|
98
|
+
if (this.blockLength === 512) {
|
|
99
|
+
if (this.state.kind === "sparse")
|
|
100
|
+
this.finishSparseHeader(this.state);
|
|
101
|
+
else
|
|
102
|
+
this.finishHeader();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
_transform(chunk, _encoding, callback) {
|
|
107
|
+
try {
|
|
108
|
+
this.meter(chunk);
|
|
109
|
+
callback(null, chunk);
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
callback(error instanceof Error ? error : new Error(String(error)));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
_flush(callback) {
|
|
116
|
+
if (this.state.kind === "header" && this.blockLength === 0)
|
|
117
|
+
callback();
|
|
118
|
+
else
|
|
119
|
+
callback(this.invalid(this.state.kind === "header" ? "truncated TAR header" : "truncated TAR entry"));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
async function isGzip(filePath) {
|
|
123
|
+
const handle = await fs.promises.open(filePath, "r");
|
|
124
|
+
try {
|
|
125
|
+
const magic = Buffer.alloc(2);
|
|
126
|
+
const { bytesRead } = await handle.read(magic, 0, 2, 0);
|
|
127
|
+
return bytesRead === 2 && magic[0] === 0x1f && magic[1] === 0x8b;
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
await handle.close();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
export async function preflightTarMetadata(params) {
|
|
134
|
+
const input = fs.createReadStream(params.archivePath);
|
|
135
|
+
const meter = new TarMetadataMeter(params.maxMetaEntryBytes);
|
|
136
|
+
const sink = new Writable({ write(_chunk, _encoding, callback) { callback(); } });
|
|
137
|
+
if (await isGzip(params.archivePath)) {
|
|
138
|
+
await pipeline(input, createGunzip(), meter, sink, { signal: params.signal });
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
await pipeline(input, meter, sink, { signal: params.signal });
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
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 type TarModule = {
|
|
16
|
+
Parser: new (options: {
|
|
17
|
+
strict: true;
|
|
18
|
+
maxMetaEntrySize: number;
|
|
19
|
+
}) => TarParser;
|
|
20
|
+
x(options: {
|
|
21
|
+
cwd: string;
|
|
22
|
+
strip: number;
|
|
23
|
+
gzip?: boolean;
|
|
24
|
+
signal?: AbortSignal;
|
|
25
|
+
preservePaths: false;
|
|
26
|
+
noChmod: true;
|
|
27
|
+
preserveOwner: false;
|
|
28
|
+
strict: true;
|
|
29
|
+
maxMetaEntrySize: number;
|
|
30
|
+
filter?(this: TarParser, entryPath: string, entry: unknown): boolean;
|
|
31
|
+
onReadEntry(this: unknown, entry: unknown): void;
|
|
32
|
+
}): TarParser;
|
|
33
|
+
t(options: {
|
|
34
|
+
file: string;
|
|
35
|
+
strict: true;
|
|
36
|
+
maxMetaEntrySize: number;
|
|
37
|
+
onReadEntry(entry: AsyncIterable<unknown> & {
|
|
38
|
+
resume(): void;
|
|
39
|
+
}): void;
|
|
40
|
+
}): Promise<unknown>;
|
|
41
|
+
};
|
|
42
|
+
export declare function importOptionalTar(): Promise<TarModule>;
|
|
43
|
+
//# sourceMappingURL=archive-tar-runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archive-tar-runtime.d.ts","sourceRoot":"","sources":["../src/archive-tar-runtime.ts"],"names":[],"mappings":"AAAA,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;AAEF,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,MAAM,EAAE,IAAI,CAAC;QACb,gBAAgB,EAAE,MAAM,CAAC;QACzB,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,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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export async function importOptionalTar() {
|
|
2
|
+
try {
|
|
3
|
+
return await import("tar");
|
|
4
|
+
}
|
|
5
|
+
catch (cause) {
|
|
6
|
+
throw new Error('Optional archive dependency "tar" is not installed. Install it to use TAR archive helpers from @openclaw/fs-safe/archive.', { cause });
|
|
7
|
+
}
|
|
8
|
+
}
|
package/dist/archive-tar.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { type ArchiveExtractLimits } from "./archive-limits.js";
|
|
2
|
+
import { type ArchiveEntryFilter, type ArchiveFilteredEntryPolicy } from "./archive-policy.js";
|
|
2
3
|
export type TarEntryInfo = {
|
|
3
4
|
path: string;
|
|
4
5
|
type: string;
|
|
5
6
|
size: number;
|
|
7
|
+
mode?: number;
|
|
6
8
|
};
|
|
7
9
|
export declare function readTarEntryInfo(entry: unknown): TarEntryInfo;
|
|
8
10
|
export declare function createTarEntryPreflightChecker(params: {
|
|
@@ -10,5 +12,7 @@ export declare function createTarEntryPreflightChecker(params: {
|
|
|
10
12
|
stripComponents?: number;
|
|
11
13
|
limits?: ArchiveExtractLimits;
|
|
12
14
|
escapeLabel?: string;
|
|
13
|
-
|
|
15
|
+
entryFilter?: ArchiveEntryFilter;
|
|
16
|
+
onFiltered?: ArchiveFilteredEntryPolicy;
|
|
17
|
+
}): (entry: TarEntryInfo) => boolean;
|
|
14
18
|
//# sourceMappingURL=archive-tar.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive-tar.d.ts","sourceRoot":"","sources":["../src/archive-tar.ts"],"names":[],"mappings":"AAKA,OAAO,
|
|
1
|
+
{"version":3,"file":"archive-tar.d.ts","sourceRoot":"","sources":["../src/archive-tar.ts"],"names":[],"mappings":"AAKA,OAAO,EAKL,KAAK,oBAAoB,EAC1B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAGL,KAAK,kBAAkB,EACvB,KAAK,0BAA0B,EAChC,MAAM,qBAAqB,CAAC;AAG7B,MAAM,MAAM,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAWvF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,CAyB7D;AAED,wBAAgB,8BAA8B,CAAC,MAAM,EAAE;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,UAAU,CAAC,EAAE,0BAA0B,CAAC;CACzC,GAAG,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CA2CnC"}
|
package/dist/archive-tar.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { resolveArchiveOutputPath, stripArchivePath, validateArchiveEntryPath, } from "./archive-entry.js";
|
|
2
|
-
import { assertArchiveEntryCountWithinLimit, createByteBudgetTracker, resolveExtractLimits, } from "./archive-limits.js";
|
|
2
|
+
import { assertArchiveEntryCountWithinLimit, assertArchiveEntryPathComponentsWithinLimit, createByteBudgetTracker, resolveExtractLimits, } from "./archive-limits.js";
|
|
3
|
+
import { archiveEntryKindFromTarType, shouldExtractArchiveEntry, } from "./archive-policy.js";
|
|
4
|
+
import { ArchiveSecurityError } from "./archive-errors.js";
|
|
3
5
|
const BLOCKED_TAR_ENTRY_TYPES = new Set([
|
|
4
6
|
"SymbolicLink",
|
|
5
7
|
"Link",
|
|
@@ -22,7 +24,13 @@ export function readTarEntryInfo(entry) {
|
|
|
22
24
|
Number.isFinite(entry.size)
|
|
23
25
|
? Math.max(0, Math.floor(entry.size))
|
|
24
26
|
: 0;
|
|
25
|
-
|
|
27
|
+
const mode = typeof entry === "object" &&
|
|
28
|
+
entry !== null &&
|
|
29
|
+
"mode" in entry &&
|
|
30
|
+
typeof entry.mode === "number"
|
|
31
|
+
? entry.mode
|
|
32
|
+
: undefined;
|
|
33
|
+
return { path: p, type: t, size: s, mode };
|
|
26
34
|
}
|
|
27
35
|
export function createTarEntryPreflightChecker(params) {
|
|
28
36
|
const strip = Math.max(0, Math.floor(params.stripComponents ?? 0));
|
|
@@ -33,20 +41,30 @@ export function createTarEntryPreflightChecker(params) {
|
|
|
33
41
|
validateArchiveEntryPath(entry.path, { escapeLabel: params.escapeLabel });
|
|
34
42
|
const relPath = stripArchivePath(entry.path, strip);
|
|
35
43
|
if (!relPath) {
|
|
36
|
-
return;
|
|
44
|
+
return false;
|
|
37
45
|
}
|
|
38
46
|
validateArchiveEntryPath(relPath, { escapeLabel: params.escapeLabel });
|
|
47
|
+
assertArchiveEntryPathComponentsWithinLimit(relPath, limits);
|
|
39
48
|
resolveArchiveOutputPath({
|
|
40
49
|
rootDir: params.rootDir,
|
|
41
50
|
relPath,
|
|
42
51
|
originalPath: entry.path,
|
|
43
52
|
escapeLabel: params.escapeLabel,
|
|
44
53
|
});
|
|
45
|
-
if (BLOCKED_TAR_ENTRY_TYPES.has(entry.type)) {
|
|
46
|
-
throw new Error(`tar entry is a link: ${entry.path}`);
|
|
47
|
-
}
|
|
48
54
|
entryCount += 1;
|
|
49
55
|
assertArchiveEntryCountWithinLimit(entryCount, limits);
|
|
56
|
+
const kind = archiveEntryKindFromTarType(entry.type);
|
|
57
|
+
if (!shouldExtractArchiveEntry({
|
|
58
|
+
filter: params.entryFilter,
|
|
59
|
+
onFiltered: params.onFiltered,
|
|
60
|
+
entry: { path: entry.path, kind, size: entry.size },
|
|
61
|
+
})) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
if (BLOCKED_TAR_ENTRY_TYPES.has(entry.type)) {
|
|
65
|
+
throw new ArchiveSecurityError("entry-link", `tar entry is a link: ${entry.path}`);
|
|
66
|
+
}
|
|
50
67
|
budget.addEntrySize(entry.size);
|
|
68
|
+
return true;
|
|
51
69
|
};
|
|
52
70
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type ArchiveEntryModePolicy } from "./archive-policy.js";
|
|
2
|
+
export type ZipEntry = {
|
|
3
|
+
name: string;
|
|
4
|
+
dir: boolean;
|
|
5
|
+
unixPermissions?: number;
|
|
6
|
+
_data?: {
|
|
7
|
+
uncompressedSize?: number;
|
|
8
|
+
};
|
|
9
|
+
nodeStream?: () => NodeJS.ReadableStream;
|
|
10
|
+
async: (type: "nodebuffer") => Promise<Buffer>;
|
|
11
|
+
};
|
|
12
|
+
export declare function isZipSymlinkEntry(entry: ZipEntry): boolean;
|
|
13
|
+
export declare function zipEntryMode(entry: ZipEntry, policy: ArchiveEntryModePolicy | undefined): number;
|
|
14
|
+
export declare function zipEntryDeclaredSize(entry: ZipEntry): number;
|
|
15
|
+
//# sourceMappingURL=archive-zip-entry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archive-zip-entry.d.ts","sourceRoot":"","sources":["../src/archive-zip-entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,sBAAsB,EAC5B,MAAM,qBAAqB,CAAC;AAE7B,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,OAAO,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE;QAAE,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,UAAU,CAAC,EAAE,MAAM,MAAM,CAAC,cAAc,CAAC;IACzC,KAAK,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAChD,CAAC;AAKF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAK1D;AAED,wBAAgB,YAAY,CAC1B,KAAK,EAAE,QAAQ,EACf,MAAM,EAAE,sBAAsB,GAAG,SAAS,GACzC,MAAM,CAMR;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CAE5D"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { resolveArchiveEntryMode, } from "./archive-policy.js";
|
|
2
|
+
const ZIP_UNIX_FILE_TYPE_MASK = 0o170000;
|
|
3
|
+
const ZIP_UNIX_SYMLINK_TYPE = 0o120000;
|
|
4
|
+
export function isZipSymlinkEntry(entry) {
|
|
5
|
+
return (typeof entry.unixPermissions === "number" &&
|
|
6
|
+
(entry.unixPermissions & ZIP_UNIX_FILE_TYPE_MASK) === ZIP_UNIX_SYMLINK_TYPE);
|
|
7
|
+
}
|
|
8
|
+
export function zipEntryMode(entry, policy) {
|
|
9
|
+
return resolveArchiveEntryMode({
|
|
10
|
+
kind: entry.dir ? "directory" : "file",
|
|
11
|
+
archivedMode: entry.unixPermissions,
|
|
12
|
+
policy,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
export function zipEntryDeclaredSize(entry) {
|
|
16
|
+
return Math.max(0, Math.floor(entry._data?.uncompressedSize ?? 0));
|
|
17
|
+
}
|
package/dist/archive.d.ts
CHANGED
|
@@ -1,24 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
export type
|
|
4
|
-
info?: (message: string) => void;
|
|
5
|
-
warn?: (message: string) => void;
|
|
6
|
-
};
|
|
1
|
+
import type { ExtractArchiveOptions } from "./archive-options.js";
|
|
2
|
+
export type { ArchiveLogger, ExtractArchiveOptions } from "./archive-options.js";
|
|
3
|
+
export type { ArchiveEntryFilter, ArchiveEntryKind, ArchiveEntryModePolicy, ArchiveFilteredEntryPolicy, } from "./archive-policy.js";
|
|
7
4
|
export { isWindowsDrivePath, normalizeArchiveEntryPath, resolveArchiveOutputPath, stripArchivePath, validateArchiveEntryPath, } from "./archive-entry.js";
|
|
8
5
|
export { resolveArchiveKind, resolvePackedRootDir, type ArchiveKind } from "./archive-kind.js";
|
|
9
|
-
export {
|
|
6
|
+
export { readArchiveEntry } from "./archive-read.js";
|
|
7
|
+
export { ARCHIVE_LIMIT_ERROR_CODE, ArchiveLimitError, DEFAULT_MAX_ARCHIVE_BYTES_ZIP, DEFAULT_MAX_ENTRIES, DEFAULT_MAX_EXTRACTED_BYTES, DEFAULT_MAX_ENTRY_BYTES, DEFAULT_MAX_META_ENTRY_BYTES, DEFAULT_MAX_ENTRY_PATH_COMPONENTS, type ArchiveExtractLimits, type ArchiveLimitErrorCode, } from "./archive-limits.js";
|
|
8
|
+
export { ArchiveFormatError, type ArchiveFormatErrorCode } from "./archive-errors.js";
|
|
10
9
|
export { ArchiveSecurityError, type ArchiveSecurityErrorCode } from "./archive-staging.js";
|
|
11
10
|
export { createArchiveSymlinkTraversalError, mergeExtractedTreeIntoDestination, prepareArchiveDestinationDir, prepareArchiveOutputPath, withStagedArchiveDestination, } from "./archive-staging.js";
|
|
12
11
|
export { createTarEntryPreflightChecker, type TarEntryInfo } from "./archive-tar.js";
|
|
13
12
|
export { loadZipArchiveWithPreflight, readZipCentralDirectoryEntryCount, type ZipArchiveWithFiles, } from "./archive-zip-preflight.js";
|
|
14
|
-
export declare function extractArchive(params:
|
|
15
|
-
archivePath: string;
|
|
16
|
-
destDir: string;
|
|
17
|
-
timeoutMs: number;
|
|
18
|
-
kind?: ArchiveKind;
|
|
19
|
-
stripComponents?: number;
|
|
20
|
-
tarGzip?: boolean;
|
|
21
|
-
limits?: ArchiveExtractLimits;
|
|
22
|
-
logger?: ArchiveLogger;
|
|
23
|
-
}): Promise<void>;
|
|
13
|
+
export declare function extractArchive(params: ExtractArchiveOptions): Promise<void>;
|
|
24
14
|
//# sourceMappingURL=archive.d.ts.map
|
package/dist/archive.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../src/archive.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../src/archive.ts"],"names":[],"mappings":"AAuDA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAElE,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AACjF,YAAY,EACV,kBAAkB,EAClB,gBAAgB,EAChB,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,yBAAyB,EACzB,wBAAwB,EACxB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC/F,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EACL,wBAAwB,EACxB,iBAAiB,EACjB,6BAA6B,EAC7B,mBAAmB,EACnB,2BAA2B,EAC3B,uBAAuB,EACvB,4BAA4B,EAC5B,iCAAiC,EACjC,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,KAAK,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AACtF,OAAO,EAAE,oBAAoB,EAAE,KAAK,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAC3F,OAAO,EACL,kCAAkC,EAClC,iCAAiC,EACjC,4BAA4B,EAC5B,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,8BAA8B,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrF,OAAO,EACL,2BAA2B,EAC3B,iCAAiC,EACjC,KAAK,mBAAmB,GACzB,MAAM,4BAA4B,CAAC;AAiNpC,wBAAsB,cAAc,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiKjF"}
|