@openclaw/fs-safe 0.7.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +71 -0
- package/README.md +5 -1
- package/dist/absolute-path.d.ts.map +1 -1
- package/dist/absolute-path.js +9 -10
- package/dist/archive-merge.d.ts +18 -0
- package/dist/archive-merge.d.ts.map +1 -0
- package/dist/archive-merge.js +157 -0
- package/dist/archive-native.d.ts.map +1 -1
- package/dist/archive-native.js +25 -5
- package/dist/archive-policy.d.ts +1 -1
- package/dist/archive-policy.d.ts.map +1 -1
- package/dist/archive-policy.js +1 -1
- package/dist/archive-read.d.ts.map +1 -1
- package/dist/archive-read.js +3 -2
- package/dist/archive-staging.d.ts +13 -8
- package/dist/archive-staging.d.ts.map +1 -1
- package/dist/archive-staging.js +44 -192
- package/dist/archive-tar-meta.d.ts.map +1 -1
- package/dist/archive-tar-meta.js +9 -5
- package/dist/archive-tar-runtime.d.ts +1 -0
- package/dist/archive-tar-runtime.d.ts.map +1 -1
- package/dist/archive-zip-admission.d.ts +2 -1
- package/dist/archive-zip-admission.d.ts.map +1 -1
- package/dist/archive-zip-admission.js +2 -2
- package/dist/archive-zip-directory.d.ts +8 -1
- package/dist/archive-zip-directory.d.ts.map +1 -1
- package/dist/archive-zip-directory.js +6 -2
- package/dist/archive-zip-entry.d.ts +1 -1
- package/dist/archive-zip-entry.d.ts.map +1 -1
- package/dist/archive-zip-names.d.ts +1 -1
- package/dist/archive-zip-names.d.ts.map +1 -1
- package/dist/archive-zip-names.js +1 -0
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +17 -25
- package/dist/directory-guard.d.ts +17 -5
- package/dist/directory-guard.d.ts.map +1 -1
- package/dist/directory-guard.js +8 -6
- package/dist/directory-mode-node.d.ts +8 -0
- package/dist/directory-mode-node.d.ts.map +1 -0
- package/dist/directory-mode-node.js +93 -0
- package/dist/directory-mode-owner.d.ts +21 -0
- package/dist/directory-mode-owner.d.ts.map +1 -0
- package/dist/directory-mode-owner.js +73 -0
- package/dist/file-observation.d.ts +1 -1
- package/dist/file-observation.d.ts.map +1 -1
- package/dist/file-store-boundary.d.ts +2 -0
- package/dist/file-store-boundary.d.ts.map +1 -1
- package/dist/file-store-boundary.js +13 -0
- package/dist/file-store.d.ts.map +1 -1
- package/dist/file-store.js +8 -11
- package/dist/filename.d.ts +6 -0
- package/dist/filename.d.ts.map +1 -1
- package/dist/filename.js +36 -0
- package/dist/guarded-mkdir.d.ts +2 -0
- package/dist/guarded-mkdir.d.ts.map +1 -1
- package/dist/guarded-mkdir.js +2 -2
- package/dist/guarded-mutation.d.ts +3 -2
- package/dist/guarded-mutation.d.ts.map +1 -1
- package/dist/guarded-mutation.js +2 -0
- package/dist/json-document-store.d.ts +3 -0
- package/dist/json-document-store.d.ts.map +1 -1
- package/dist/json-document-store.js +2 -0
- package/dist/json-durable-queue-ownership.d.ts +3 -1
- package/dist/json-durable-queue-ownership.d.ts.map +1 -1
- package/dist/json-durable-queue-ownership.js +7 -4
- package/dist/json-durable-queue.d.ts.map +1 -1
- package/dist/json-durable-queue.js +25 -28
- package/dist/local-roots.d.ts.map +1 -1
- package/dist/local-roots.js +18 -56
- package/dist/move-path-cleanup.d.ts +43 -0
- package/dist/move-path-cleanup.d.ts.map +1 -0
- package/dist/move-path-cleanup.js +159 -0
- package/dist/move-path.d.ts +2 -0
- package/dist/move-path.d.ts.map +1 -1
- package/dist/move-path.js +47 -83
- package/dist/native-operations.d.ts +2 -4
- package/dist/native-operations.d.ts.map +1 -1
- package/dist/native-operations.js +7 -9
- package/dist/native-pinned-write-windows.d.ts +2 -2
- package/dist/native-pinned-write-windows.d.ts.map +1 -1
- package/dist/native-pinned-write-windows.js +5 -5
- package/dist/native-pinned-write.d.ts.map +1 -1
- package/dist/native-pinned-write.js +14 -4
- package/dist/native-staged-file.d.ts +2 -2
- package/dist/native-staged-file.d.ts.map +1 -1
- package/dist/opened-file-failure.d.ts +1 -0
- package/dist/opened-file-failure.d.ts.map +1 -1
- package/dist/opened-file-failure.js +4 -0
- package/dist/output-sibling.d.ts.map +1 -1
- package/dist/output-sibling.js +9 -3
- package/dist/pinned-write.d.ts +2 -2
- package/dist/pinned-write.d.ts.map +1 -1
- package/dist/pinned-write.js +32 -13
- package/dist/publish-file.d.ts.map +1 -1
- package/dist/publish-file.js +7 -3
- package/dist/regular-file.d.ts +1 -1
- package/dist/regular-file.d.ts.map +1 -1
- package/dist/regular-file.js +35 -6
- package/dist/replace-file-descriptor.d.ts +9 -1
- package/dist/replace-file-descriptor.d.ts.map +1 -1
- package/dist/replace-file-descriptor.js +58 -10
- package/dist/replace-file-temp-owner.d.ts +7 -0
- package/dist/replace-file-temp-owner.d.ts.map +1 -1
- package/dist/replace-file-temp-owner.js +29 -0
- package/dist/replace-file.d.ts +1 -0
- package/dist/replace-file.d.ts.map +1 -1
- package/dist/replace-file.js +12 -38
- package/dist/root-context.d.ts +4 -0
- package/dist/root-context.d.ts.map +1 -1
- package/dist/root-context.js +19 -0
- package/dist/root-impl.d.ts +43 -0
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +13 -11
- package/dist/root-path-symlink.d.ts +6 -2
- package/dist/root-path-symlink.d.ts.map +1 -1
- package/dist/root-path-symlink.js +7 -6
- package/dist/root-path.d.ts +1 -0
- package/dist/root-path.d.ts.map +1 -1
- package/dist/root-path.js +6 -2
- package/dist/root-walk.d.ts.map +1 -1
- package/dist/root-walk.js +4 -0
- package/dist/root-write-verification.d.ts +3 -2
- package/dist/root-write-verification.d.ts.map +1 -1
- package/dist/root-write-verification.js +6 -0
- package/dist/secret-file.d.ts +9 -0
- package/dist/secret-file.d.ts.map +1 -1
- package/dist/secret-file.js +136 -74
- package/dist/sibling-temp.d.ts.map +1 -1
- package/dist/sibling-temp.js +9 -3
- package/dist/sidecar-lock-acquire.d.ts.map +1 -1
- package/dist/sidecar-lock-acquire.js +18 -5
- package/dist/sidecar-lock-reclaim.d.ts.map +1 -1
- package/dist/sidecar-lock-reclaim.js +6 -4
- package/dist/sidecar-lock.d.ts.map +1 -1
- package/dist/sidecar-lock.js +26 -0
- package/dist/write-open-flags.d.ts +5 -0
- package/dist/write-open-flags.d.ts.map +1 -0
- package/dist/write-open-flags.js +35 -0
- package/docs/advanced.md +1 -1
- package/docs/archive.md +56 -7
- package/docs/atomic.md +42 -2
- package/docs/contributing.md +13 -0
- package/docs/durability.md +3 -2
- package/docs/errors.md +3 -1
- package/docs/file-store.md +38 -8
- package/docs/local-roots.md +4 -3
- package/docs/output.md +4 -1
- package/docs/private-file-store.md +11 -2
- package/docs/reading.md +3 -1
- package/docs/regular-file.md +16 -2
- package/docs/root.md +4 -2
- package/docs/secret-file.md +37 -2
- package/docs/sidecar-lock.md +32 -5
- package/docs/store.md +5 -1
- package/docs/temp.md +19 -1
- package/docs/writing.md +24 -3
- package/package.json +14 -10
package/dist/archive-staging.js
CHANGED
|
@@ -5,11 +5,13 @@ import { ownExtractionDestinationMutation, } from "./archive-deadline.js";
|
|
|
5
5
|
import { ArchiveSecurityError, } from "./archive-errors.js";
|
|
6
6
|
import { FsSafeError } from "./errors.js";
|
|
7
7
|
import { formatErrorDetail } from "./error-detail.js";
|
|
8
|
-
import {
|
|
8
|
+
import { root } from "./root.js";
|
|
9
9
|
import { isNotFoundPathError, isPathInside } from "./path.js";
|
|
10
|
-
import { resolveReadOpenFlags } from "./read-open-flags.js";
|
|
11
10
|
import { resolveSecureTempRoot } from "./secure-temp-dir.js";
|
|
12
11
|
import { getFsSafeTestHooks } from "./test-hooks.js";
|
|
12
|
+
import { mkdirPathComponentsWithGuards } from "./guarded-mkdir.js";
|
|
13
|
+
import { expandRelativePathWithHome } from "./root-context.js";
|
|
14
|
+
import { resolveRootPath } from "./root-path.js";
|
|
13
15
|
const ERROR_ARCHIVE_ENTRY_TRAVERSES_SYMLINK = "archive entry traverses symlink in destination";
|
|
14
16
|
const ARCHIVE_STAGING_MODE = 0o700;
|
|
15
17
|
function checkExtractionDeadline(deadline) {
|
|
@@ -19,7 +21,7 @@ export { ArchiveSecurityError } from "./archive-errors.js";
|
|
|
19
21
|
function symlinkTraversalError(originalPath) {
|
|
20
22
|
return new ArchiveSecurityError("destination-symlink-traversal", `${ERROR_ARCHIVE_ENTRY_TRAVERSES_SYMLINK}: ${formatErrorDetail(originalPath)}`);
|
|
21
23
|
}
|
|
22
|
-
async function createDirectoryIdentityGuard(dir) {
|
|
24
|
+
export async function createDirectoryIdentityGuard(dir) {
|
|
23
25
|
try {
|
|
24
26
|
return await createAsyncDirectoryGuard(dir);
|
|
25
27
|
}
|
|
@@ -30,7 +32,7 @@ async function createDirectoryIdentityGuard(dir) {
|
|
|
30
32
|
throw err;
|
|
31
33
|
}
|
|
32
34
|
}
|
|
33
|
-
async function assertDirectoryIdentityGuard(guard) {
|
|
35
|
+
export async function assertDirectoryIdentityGuard(guard) {
|
|
34
36
|
try {
|
|
35
37
|
await assertAsyncDirectoryGuard(guard);
|
|
36
38
|
}
|
|
@@ -82,7 +84,7 @@ async function assertNoSymlinkTraversal(params) {
|
|
|
82
84
|
}
|
|
83
85
|
}
|
|
84
86
|
}
|
|
85
|
-
async function assertResolvedInsideDestination(params) {
|
|
87
|
+
export async function assertResolvedInsideDestination(params) {
|
|
86
88
|
let resolved;
|
|
87
89
|
try {
|
|
88
90
|
resolved = await fs.realpath(params.targetPath);
|
|
@@ -109,8 +111,39 @@ async function mkdirArchiveOutput(params) {
|
|
|
109
111
|
}
|
|
110
112
|
}
|
|
111
113
|
export async function prepareArchiveOutputPath(params) {
|
|
114
|
+
await prepareOutputPath(params);
|
|
115
|
+
}
|
|
116
|
+
export async function preparePrivateArchiveOutputPath(params, assertGuards) {
|
|
117
|
+
await prepareOutputPath(params, assertGuards, true);
|
|
118
|
+
}
|
|
119
|
+
async function prepareOutputPath(params, assertGuards, privateWorkingMode = false) {
|
|
112
120
|
checkExtractionDeadline(params.deadline);
|
|
113
|
-
const targetRoot =
|
|
121
|
+
const targetRoot = privateWorkingMode ? {
|
|
122
|
+
async mkdir(relativePath) {
|
|
123
|
+
// Retain Root.mkdir's strict alias admission before the shared traversal,
|
|
124
|
+
// while selecting private creation modes without adding a public option.
|
|
125
|
+
const resolved = await resolveRootPath({
|
|
126
|
+
absolutePath: path.resolve(params.destinationRealDir, await expandRelativePathWithHome(relativePath)),
|
|
127
|
+
rootPath: params.destinationRealDir,
|
|
128
|
+
rootCanonicalPath: params.destinationRealDir,
|
|
129
|
+
boundaryLabel: "archive destination",
|
|
130
|
+
rejectSymlinks: true,
|
|
131
|
+
});
|
|
132
|
+
checkExtractionDeadline(params.deadline);
|
|
133
|
+
await mkdirPathComponentsWithGuards({
|
|
134
|
+
rootReal: params.destinationRealDir,
|
|
135
|
+
targetPath: resolved.canonicalPath,
|
|
136
|
+
mode: 0o700,
|
|
137
|
+
rejectSymlinks: true,
|
|
138
|
+
beforeComponent: async () => {
|
|
139
|
+
await assertDirectoryIdentityGuard(destinationGuard);
|
|
140
|
+
checkExtractionDeadline(params.deadline);
|
|
141
|
+
await assertGuards?.();
|
|
142
|
+
checkExtractionDeadline(params.deadline);
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
},
|
|
146
|
+
} : await root(params.destinationRealDir);
|
|
114
147
|
checkExtractionDeadline(params.deadline);
|
|
115
148
|
const destinationGuard = await createDirectoryIdentityGuard(params.destinationRealDir);
|
|
116
149
|
checkExtractionDeadline(params.deadline);
|
|
@@ -127,6 +160,8 @@ export async function prepareArchiveOutputPath(params) {
|
|
|
127
160
|
await ownExtractionDestinationMutation(params.deadline, async () => {
|
|
128
161
|
await assertDirectoryIdentityGuard(destinationGuard);
|
|
129
162
|
checkExtractionDeadline(params.deadline);
|
|
163
|
+
await assertGuards?.();
|
|
164
|
+
checkExtractionDeadline(params.deadline);
|
|
130
165
|
await mkdirArchiveOutput({
|
|
131
166
|
targetRoot,
|
|
132
167
|
relativePath: relPath,
|
|
@@ -151,6 +186,8 @@ export async function prepareArchiveOutputPath(params) {
|
|
|
151
186
|
await ownExtractionDestinationMutation(params.deadline, async () => {
|
|
152
187
|
await assertDirectoryIdentityGuard(destinationGuard);
|
|
153
188
|
checkExtractionDeadline(params.deadline);
|
|
189
|
+
await assertGuards?.();
|
|
190
|
+
checkExtractionDeadline(params.deadline);
|
|
154
191
|
await mkdirArchiveOutput({
|
|
155
192
|
targetRoot,
|
|
156
193
|
relativePath: parentRel,
|
|
@@ -168,104 +205,6 @@ export async function prepareArchiveOutputPath(params) {
|
|
|
168
205
|
});
|
|
169
206
|
checkExtractionDeadline(params.deadline);
|
|
170
207
|
}
|
|
171
|
-
async function chmodInsideDestinationBestEffort(params) {
|
|
172
|
-
await getFsSafeTestHooks()?.beforeArchiveOutputMutation?.("chmod", params.destinationPath);
|
|
173
|
-
checkExtractionDeadline(params.deadline);
|
|
174
|
-
const destinationGuard = await createDirectoryIdentityGuard(params.destinationRealDir);
|
|
175
|
-
checkExtractionDeadline(params.deadline);
|
|
176
|
-
await assertDirectoryIdentityGuard(destinationGuard);
|
|
177
|
-
checkExtractionDeadline(params.deadline);
|
|
178
|
-
const handle = await fs
|
|
179
|
-
.open(params.destinationPath, resolveReadOpenFlags())
|
|
180
|
-
.catch(() => null);
|
|
181
|
-
checkExtractionDeadline(params.deadline);
|
|
182
|
-
if (!handle) {
|
|
183
|
-
const stat = await fs.lstat(params.destinationPath).catch(() => null);
|
|
184
|
-
checkExtractionDeadline(params.deadline);
|
|
185
|
-
if (stat?.isSymbolicLink()) {
|
|
186
|
-
throw symlinkTraversalError(params.originalPath);
|
|
187
|
-
}
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
try {
|
|
191
|
-
const stat = await handle.stat();
|
|
192
|
-
checkExtractionDeadline(params.deadline);
|
|
193
|
-
if (!stat.isDirectory() && !stat.isFile()) {
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
const realPath = await resolveOpenedFileRealPathForHandle(handle, params.destinationPath);
|
|
197
|
-
checkExtractionDeadline(params.deadline);
|
|
198
|
-
if (!isPathInside(params.destinationRealDir, realPath)) {
|
|
199
|
-
throw symlinkTraversalError(params.originalPath);
|
|
200
|
-
}
|
|
201
|
-
await ownExtractionDestinationMutation(params.deadline, async () => {
|
|
202
|
-
await handle.chmod(params.mode).catch(() => undefined);
|
|
203
|
-
checkExtractionDeadline(params.deadline);
|
|
204
|
-
await assertDirectoryIdentityGuard(destinationGuard);
|
|
205
|
-
checkExtractionDeadline(params.deadline);
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
finally {
|
|
209
|
-
await handle.close().catch(() => undefined);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
async function applyStagedEntryMode(params) {
|
|
213
|
-
checkExtractionDeadline(params.deadline);
|
|
214
|
-
const destinationPath = path.join(params.destinationRealDir, params.relPath);
|
|
215
|
-
await assertResolvedInsideDestination({
|
|
216
|
-
destinationRealDir: params.destinationRealDir,
|
|
217
|
-
targetPath: destinationPath,
|
|
218
|
-
originalPath: params.originalPath,
|
|
219
|
-
});
|
|
220
|
-
checkExtractionDeadline(params.deadline);
|
|
221
|
-
if (params.mode !== 0) {
|
|
222
|
-
await chmodInsideDestinationBestEffort({
|
|
223
|
-
destinationRealDir: params.destinationRealDir,
|
|
224
|
-
destinationPath,
|
|
225
|
-
mode: params.mode,
|
|
226
|
-
originalPath: params.originalPath,
|
|
227
|
-
deadline: params.deadline,
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
checkExtractionDeadline(params.deadline);
|
|
231
|
-
}
|
|
232
|
-
async function assertExtractedFileHasNoHardlinkAlias(params) {
|
|
233
|
-
const destinationPath = path.join(params.destinationRealDir, params.relPath);
|
|
234
|
-
await assertResolvedInsideDestination({
|
|
235
|
-
destinationRealDir: params.destinationRealDir,
|
|
236
|
-
targetPath: destinationPath,
|
|
237
|
-
originalPath: params.originalPath,
|
|
238
|
-
});
|
|
239
|
-
const stat = await fs.lstat(destinationPath);
|
|
240
|
-
if (stat.isFile() && stat.nlink > 1) {
|
|
241
|
-
throw symlinkTraversalError(params.originalPath);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
async function removeExtractedDestinationFile(params) {
|
|
245
|
-
const destinationPath = path.join(params.destinationRealDir, params.relPath);
|
|
246
|
-
let stat;
|
|
247
|
-
try {
|
|
248
|
-
stat = await fs.lstat(destinationPath);
|
|
249
|
-
}
|
|
250
|
-
catch {
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
if (!stat.isFile()) {
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
let resolved;
|
|
257
|
-
try {
|
|
258
|
-
resolved = await fs.realpath(destinationPath);
|
|
259
|
-
}
|
|
260
|
-
catch {
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
if (!isPathInside(params.destinationRealDir, resolved)) {
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
const targetRoot = await root(params.destinationRealDir);
|
|
267
|
-
await targetRoot.remove(params.relPath).catch(() => undefined);
|
|
268
|
-
}
|
|
269
208
|
function assertSafeArchiveStagingPrefix(prefix) {
|
|
270
209
|
if (!prefix ||
|
|
271
210
|
prefix === "." ||
|
|
@@ -305,94 +244,7 @@ export async function withStagedArchiveDestination(params) {
|
|
|
305
244
|
}
|
|
306
245
|
}
|
|
307
246
|
}
|
|
308
|
-
export
|
|
309
|
-
const targetRoot = await root(params.destinationRealDir);
|
|
310
|
-
const sourceRootGuard = await createDirectoryIdentityGuard(params.sourceDir);
|
|
311
|
-
const sourceRootReal = sourceRootGuard.realPath;
|
|
312
|
-
const walk = async (currentSourceDir) => {
|
|
313
|
-
await assertDirectoryIdentityGuard(sourceRootGuard);
|
|
314
|
-
const entries = await fs.readdir(currentSourceDir, { withFileTypes: true });
|
|
315
|
-
for (const entry of entries) {
|
|
316
|
-
await assertDirectoryIdentityGuard(sourceRootGuard);
|
|
317
|
-
const sourcePath = path.join(currentSourceDir, entry.name);
|
|
318
|
-
const relPath = path.relative(params.sourceDir, sourcePath);
|
|
319
|
-
const originalPath = relPath.split(path.sep).join("/");
|
|
320
|
-
const destinationPath = path.join(params.destinationDir, relPath);
|
|
321
|
-
const sourceStat = await fs.lstat(sourcePath);
|
|
322
|
-
if (sourceStat.isSymbolicLink()) {
|
|
323
|
-
throw symlinkTraversalError(originalPath);
|
|
324
|
-
}
|
|
325
|
-
const sourceReal = await fs.realpath(sourcePath);
|
|
326
|
-
if (!isPathInside(sourceRootReal, sourceReal)) {
|
|
327
|
-
throw symlinkTraversalError(originalPath);
|
|
328
|
-
}
|
|
329
|
-
if (sourceStat.isDirectory()) {
|
|
330
|
-
await prepareArchiveOutputPath({
|
|
331
|
-
destinationDir: params.destinationDir,
|
|
332
|
-
destinationRealDir: params.destinationRealDir,
|
|
333
|
-
relPath,
|
|
334
|
-
outPath: destinationPath,
|
|
335
|
-
originalPath,
|
|
336
|
-
isDirectory: true,
|
|
337
|
-
deadline: params.deadline,
|
|
338
|
-
});
|
|
339
|
-
await walk(sourcePath);
|
|
340
|
-
await applyStagedEntryMode({
|
|
341
|
-
destinationRealDir: params.destinationRealDir,
|
|
342
|
-
relPath,
|
|
343
|
-
mode: sourceStat.mode & 0o777,
|
|
344
|
-
originalPath,
|
|
345
|
-
deadline: params.deadline,
|
|
346
|
-
});
|
|
347
|
-
continue;
|
|
348
|
-
}
|
|
349
|
-
if (!sourceStat.isFile()) {
|
|
350
|
-
throw new Error(`archive staging contains unsupported entry: ${formatErrorDetail(originalPath)}`);
|
|
351
|
-
}
|
|
352
|
-
await prepareArchiveOutputPath({
|
|
353
|
-
destinationDir: params.destinationDir,
|
|
354
|
-
destinationRealDir: params.destinationRealDir,
|
|
355
|
-
relPath,
|
|
356
|
-
outPath: destinationPath,
|
|
357
|
-
originalPath,
|
|
358
|
-
isDirectory: false,
|
|
359
|
-
deadline: params.deadline,
|
|
360
|
-
});
|
|
361
|
-
await ownExtractionDestinationMutation(params.deadline, async () => {
|
|
362
|
-
try {
|
|
363
|
-
await targetRoot.copyIn(relPath, sourcePath, { mkdir: true });
|
|
364
|
-
checkExtractionDeadline(params.deadline);
|
|
365
|
-
await assertExtractedFileHasNoHardlinkAlias({
|
|
366
|
-
destinationRealDir: params.destinationRealDir,
|
|
367
|
-
relPath,
|
|
368
|
-
originalPath,
|
|
369
|
-
});
|
|
370
|
-
checkExtractionDeadline(params.deadline);
|
|
371
|
-
await applyStagedEntryMode({
|
|
372
|
-
destinationRealDir: params.destinationRealDir,
|
|
373
|
-
relPath,
|
|
374
|
-
mode: sourceStat.mode & 0o777,
|
|
375
|
-
originalPath,
|
|
376
|
-
deadline: params.deadline,
|
|
377
|
-
});
|
|
378
|
-
checkExtractionDeadline(params.deadline);
|
|
379
|
-
}
|
|
380
|
-
catch (err) {
|
|
381
|
-
await removeExtractedDestinationFile({
|
|
382
|
-
destinationRealDir: params.destinationRealDir,
|
|
383
|
-
relPath,
|
|
384
|
-
});
|
|
385
|
-
if (err instanceof FsSafeError &&
|
|
386
|
-
(err.code === "hardlink" || err.code === "path-alias")) {
|
|
387
|
-
throw symlinkTraversalError(originalPath);
|
|
388
|
-
}
|
|
389
|
-
throw err;
|
|
390
|
-
}
|
|
391
|
-
});
|
|
392
|
-
}
|
|
393
|
-
};
|
|
394
|
-
await walk(params.sourceDir);
|
|
395
|
-
}
|
|
247
|
+
export { mergeExtractedTreeIntoDestination } from "./archive-merge.js";
|
|
396
248
|
export function createArchiveSymlinkTraversalError(originalPath) {
|
|
397
249
|
return symlinkTraversalError(originalPath);
|
|
398
250
|
}
|
|
@@ -1 +1 @@
|
|
|
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,
|
|
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
CHANGED
|
@@ -261,13 +261,17 @@ async function isGzip(filePath) {
|
|
|
261
261
|
}
|
|
262
262
|
}
|
|
263
263
|
export async function preflightTarMetadata(params) {
|
|
264
|
-
const input = fs.createReadStream(params.archivePath);
|
|
265
264
|
const meter = new TarMetadataMeter(params.limits, params.onMember);
|
|
266
265
|
const sink = new Writable({ write(_chunk, _encoding, callback) { callback(); } });
|
|
267
|
-
|
|
268
|
-
|
|
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 }));
|
|
269
273
|
}
|
|
270
|
-
|
|
271
|
-
|
|
274
|
+
finally {
|
|
275
|
+
input.destroy();
|
|
272
276
|
}
|
|
273
277
|
}
|
|
@@ -1 +1 @@
|
|
|
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,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
|
+
{"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,7 +1,8 @@
|
|
|
1
1
|
import type { ExtractionDeadline } from "./archive-deadline.js";
|
|
2
2
|
import { type ResolvedArchiveExtractLimits } from "./archive-limits.js";
|
|
3
|
+
import { type ZipDirectoryEntry } from "./archive-zip-directory.js";
|
|
3
4
|
/** Admit physical records before a decoder can normalize or collapse names. */
|
|
4
5
|
export declare function admitZipBuffer(input: Uint8Array, limits: ResolvedArchiveExtractLimits): number;
|
|
5
6
|
/** The extraction input is already private and staged; read only bounded metadata. */
|
|
6
|
-
export declare function admitZipFile(archivePath: string, limits: ResolvedArchiveExtractLimits, deadline: ExtractionDeadline): Promise<number>;
|
|
7
|
+
export declare function admitZipFile(archivePath: string, limits: ResolvedArchiveExtractLimits, deadline: ExtractionDeadline, onEntry?: (entry: ZipDirectoryEntry) => void): Promise<number>;
|
|
7
8
|
//# sourceMappingURL=archive-zip-admission.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive-zip-admission.d.ts","sourceRoot":"","sources":["../src/archive-zip-admission.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAA+C,KAAK,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"archive-zip-admission.d.ts","sourceRoot":"","sources":["../src/archive-zip-admission.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAA+C,KAAK,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AAErH,OAAO,EAAoB,KAAK,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAUtF,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,4BAA4B,GAAG,MAAM,CAU9F;AAED,sFAAsF;AACtF,wBAAsB,YAAY,CAChC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,4BAA4B,EAAE,QAAQ,EAAE,kBAAkB,EACvF,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,GAC3C,OAAO,CAAC,MAAM,CAAC,CA+BjB"}
|
|
@@ -24,7 +24,7 @@ export function admitZipBuffer(input, limits) {
|
|
|
24
24
|
return step.value;
|
|
25
25
|
}
|
|
26
26
|
/** The extraction input is already private and staged; read only bounded metadata. */
|
|
27
|
-
export async function admitZipFile(archivePath, limits, deadline) {
|
|
27
|
+
export async function admitZipFile(archivePath, limits, deadline, onEntry) {
|
|
28
28
|
deadline.check();
|
|
29
29
|
const opened = await openRootFile({
|
|
30
30
|
absolutePath: archivePath, rootPath: path.dirname(archivePath),
|
|
@@ -34,7 +34,7 @@ export async function admitZipFile(archivePath, limits, deadline) {
|
|
|
34
34
|
throw opened.error ?? new Error("cannot open staged ZIP archive");
|
|
35
35
|
try {
|
|
36
36
|
checkSize(opened.stat.size, limits);
|
|
37
|
-
const scan = scanZipDirectory(opened.stat.size, limits);
|
|
37
|
+
const scan = scanZipDirectory(opened.stat.size, limits, onEntry);
|
|
38
38
|
let step = scan.next();
|
|
39
39
|
while (!step.done) {
|
|
40
40
|
const { offset, length } = step.value;
|
|
@@ -4,5 +4,12 @@ export type ZipRead = {
|
|
|
4
4
|
length: number;
|
|
5
5
|
};
|
|
6
6
|
export type ZipScan = Generator<ZipRead, number, Buffer>;
|
|
7
|
-
export
|
|
7
|
+
export type ZipDirectoryEntry = {
|
|
8
|
+
index: number;
|
|
9
|
+
creatorSystem: number;
|
|
10
|
+
externalAttributes: number;
|
|
11
|
+
size: number;
|
|
12
|
+
path?: string;
|
|
13
|
+
};
|
|
14
|
+
export declare function scanZipDirectory(size: number, limits: ResolvedArchiveExtractLimits, onEntry?: (entry: ZipDirectoryEntry) => void): ZipScan;
|
|
8
15
|
//# sourceMappingURL=archive-zip-directory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive-zip-directory.d.ts","sourceRoot":"","sources":["../src/archive-zip-directory.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,4BAA4B,EAClC,MAAM,qBAAqB,CAAC;AAG7B,MAAM,MAAM,OAAO,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AACzD,MAAM,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"archive-zip-directory.d.ts","sourceRoot":"","sources":["../src/archive-zip-directory.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,4BAA4B,EAClC,MAAM,qBAAqB,CAAC;AAG7B,MAAM,MAAM,OAAO,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AACzD,MAAM,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AACzD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAmIF,wBAAiB,gBAAgB,CAC/B,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,4BAA4B,EAAE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,GAC/F,OAAO,CAkET"}
|
|
@@ -150,7 +150,7 @@ function* descriptorEnd(at, bound, crc, compressed, uncompressed, wide) {
|
|
|
150
150
|
zipFormat("invalid or ambiguous data descriptor");
|
|
151
151
|
return matches[0];
|
|
152
152
|
}
|
|
153
|
-
export function* scanZipDirectory(size, limits) {
|
|
153
|
+
export function* scanZipDirectory(size, limits, onEntry) {
|
|
154
154
|
const directory = yield* layout(size);
|
|
155
155
|
assertArchiveEntryCountWithinLimit(directory.count, limits);
|
|
156
156
|
const seen = new Set();
|
|
@@ -193,7 +193,7 @@ export function* scanZipDirectory(size, limits) {
|
|
|
193
193
|
const localExtraLength = local.readUInt16LE(28);
|
|
194
194
|
const localNames = yield* read(localAt + 30, localNameLength + localExtraLength, directory.start);
|
|
195
195
|
const localExtra = zipExtraFields(localNames.subarray(localNameLength));
|
|
196
|
-
admitZipNames({ central: centralName, local: localNames.subarray(0, localNameLength), flags, centralExtra, localExtra, seen });
|
|
196
|
+
const entryPath = admitZipNames({ central: centralName, local: localNames.subarray(0, localNameLength), flags, centralExtra, localExtra, seen });
|
|
197
197
|
const localValues = wideValues(local, localExtra, false);
|
|
198
198
|
const crc = central.readUInt32LE(16);
|
|
199
199
|
if (!(flags & 8) && (local.readUInt32LE(14) !== crc || localValues.compressed !== values.compressed || localValues.uncompressed !== values.uncompressed)) {
|
|
@@ -211,6 +211,10 @@ export function* scanZipDirectory(size, limits) {
|
|
|
211
211
|
if (flags & 8)
|
|
212
212
|
dataEnd = yield* descriptorEnd(dataEnd, directory.start, crc, values.compressed, values.uncompressed, values.wide || localValues.wide);
|
|
213
213
|
spans.push({ start: localAt, end: dataEnd });
|
|
214
|
+
onEntry?.({
|
|
215
|
+
index: count - 1, creatorSystem: central[5], externalAttributes: central.readUInt32LE(38),
|
|
216
|
+
size: values.uncompressed, path: entryPath,
|
|
217
|
+
});
|
|
214
218
|
at = next;
|
|
215
219
|
}
|
|
216
220
|
if (count !== directory.count)
|
|
@@ -1 +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;
|
|
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,GAAG,IAAI,CAAC;IAChC,KAAK,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IAC7E,UAAU,CAAC,EAAE,MAAM,MAAM,CAAC,cAAc,CAAC;IACzC,KAAK,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAChD,CAAC;AAEF,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,QAAQ,GACd;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAI3D;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAGhE;AAKD,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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive-zip-names.d.ts","sourceRoot":"","sources":["../src/archive-zip-names.ts"],"names":[],"mappings":"AAIA,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,CAEhD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAK/D;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAcjE;AA6BD,wBAAgB,aAAa,CAAC,MAAM,EAAE;IACpC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAC9C,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACnB,GAAG,
|
|
1
|
+
{"version":3,"file":"archive-zip-names.d.ts","sourceRoot":"","sources":["../src/archive-zip-names.ts"],"names":[],"mappings":"AAIA,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,CAEhD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAK/D;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAcjE;AA6BD,wBAAgB,aAAa,CAAC,MAAM,EAAE;IACpC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAC9C,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACnB,GAAG,MAAM,GAAG,SAAS,CA8BrB"}
|
package/dist/archive.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../src/archive.ts"],"names":[],"mappings":"AA+DA,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;
|
|
1
|
+
{"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../src/archive.ts"],"names":[],"mappings":"AA+DA,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;AA6MpC,wBAAsB,cAAc,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqKjF"}
|
package/dist/archive.js
CHANGED
|
@@ -7,7 +7,8 @@ import { createArchiveOutputPathTracker, resolveArchiveOutputPath, stripArchiveP
|
|
|
7
7
|
import { createPipelineTimeoutError, waitForDeadline, withExtractionDeadline, } from "./archive-deadline.js";
|
|
8
8
|
import { assertArchiveEntryCountWithinLimit, assertArchiveEntryPathComponentsWithinLimit, createByteBudgetTracker, createExtractBudgetTransform, resolveExtractLimits, resolveTarMeterLimits, } from "./archive-limits.js";
|
|
9
9
|
import { resolveArchiveKind } from "./archive-kind.js";
|
|
10
|
-
import {
|
|
10
|
+
import { prepareArchiveDestinationDir, preparePrivateArchiveOutputPath, withStagedArchiveDestination, } from "./archive-staging.js";
|
|
11
|
+
import { mergePlannedArchiveIntoDestination } from "./archive-merge.js";
|
|
11
12
|
import { createTarEntryPreflightChecker, readTarEntryInfo, } from "./archive-tar.js";
|
|
12
13
|
import { loadZipArchiveWithPreflight } from "./archive-zip-preflight.js";
|
|
13
14
|
import { isZipSymlinkEntry, zipEntryDeclaredSize, zipEntryMode, } from "./archive-zip-entry.js";
|
|
@@ -64,9 +65,6 @@ function resolveZipOutputPath(params) {
|
|
|
64
65
|
}),
|
|
65
66
|
};
|
|
66
67
|
}
|
|
67
|
-
async function prepareZipOutputPath(params) {
|
|
68
|
-
await prepareArchiveOutputPath(params);
|
|
69
|
-
}
|
|
70
68
|
async function writeZipFileEntry(params) {
|
|
71
69
|
params.deadline.check();
|
|
72
70
|
params.budget.startEntry();
|
|
@@ -78,9 +76,9 @@ async function writeZipFileEntry(params) {
|
|
|
78
76
|
await writeSiblingTempFile({
|
|
79
77
|
dir: path.dirname(destinationPath),
|
|
80
78
|
chmodDir: false,
|
|
81
|
-
mode:
|
|
79
|
+
mode: 0o600,
|
|
82
80
|
writeTemp: async (tempPath) => {
|
|
83
|
-
tempHandle = await fs.open(tempPath, OPEN_WRITE_CREATE_FLAGS,
|
|
81
|
+
tempHandle = await fs.open(tempPath, OPEN_WRITE_CREATE_FLAGS, 0o600);
|
|
84
82
|
const writable = tempHandle.createWriteStream();
|
|
85
83
|
writable.once("close", () => {
|
|
86
84
|
handleClosedByStream = true;
|
|
@@ -137,6 +135,7 @@ async function extractZip(params) {
|
|
|
137
135
|
destinationRealDir,
|
|
138
136
|
run: async (stagingDir) => {
|
|
139
137
|
const stagingRealDir = await fs.realpath(stagingDir);
|
|
138
|
+
const acceptedEntries = [];
|
|
140
139
|
for (const entry of entries) {
|
|
141
140
|
params.deadline.check();
|
|
142
141
|
const output = resolveZipOutputPath({
|
|
@@ -163,7 +162,8 @@ async function extractZip(params) {
|
|
|
163
162
|
throw new ArchiveSecurityError("entry-link", `zip entry is a link: ${entry.name}`);
|
|
164
163
|
}
|
|
165
164
|
const mode = zipEntryMode(entry, params.entryModes);
|
|
166
|
-
|
|
165
|
+
acceptedEntries.push({ path: output.relPath, kind: entry.dir ? "directory" : "file", mode });
|
|
166
|
+
await preparePrivateArchiveOutputPath({
|
|
167
167
|
destinationDir: stagingRealDir,
|
|
168
168
|
destinationRealDir: stagingRealDir,
|
|
169
169
|
relPath: output.relPath,
|
|
@@ -173,7 +173,6 @@ async function extractZip(params) {
|
|
|
173
173
|
deadline: params.deadline,
|
|
174
174
|
});
|
|
175
175
|
if (entry.dir) {
|
|
176
|
-
await fs.chmod(output.outPath, mode);
|
|
177
176
|
continue;
|
|
178
177
|
}
|
|
179
178
|
await writeZipFileEntry({
|
|
@@ -181,11 +180,11 @@ async function extractZip(params) {
|
|
|
181
180
|
outPath: output.outPath,
|
|
182
181
|
budget,
|
|
183
182
|
deadline: params.deadline,
|
|
184
|
-
mode,
|
|
185
183
|
});
|
|
186
184
|
}
|
|
187
185
|
params.deadline.check();
|
|
188
|
-
await
|
|
186
|
+
await mergePlannedArchiveIntoDestination({
|
|
187
|
+
entries: acceptedEntries,
|
|
189
188
|
sourceDir: stagingRealDir,
|
|
190
189
|
destinationDir: params.destDir,
|
|
191
190
|
destinationRealDir,
|
|
@@ -276,6 +275,7 @@ export async function extractArchive(params) {
|
|
|
276
275
|
noChmod: true,
|
|
277
276
|
preserveOwner: false,
|
|
278
277
|
noMtime: true,
|
|
278
|
+
dmode: 0o700,
|
|
279
279
|
strict: true,
|
|
280
280
|
maxMetaEntrySize: tarLimits.maxMetaEntryBytes,
|
|
281
281
|
maxDecompressionRatio: NODE_TAR_RATIO_DISABLED,
|
|
@@ -286,18 +286,19 @@ export async function extractArchive(params) {
|
|
|
286
286
|
if (!relPath) {
|
|
287
287
|
return false;
|
|
288
288
|
}
|
|
289
|
-
|
|
289
|
+
const kind = info.type === "Directory" || info.type === "GNUDumpDir" ? "directory" : "file";
|
|
290
|
+
// Keep archived modes before changing node-tar's creation mode.
|
|
290
291
|
entry.path = relPath;
|
|
291
292
|
acceptedEntries.push({
|
|
292
293
|
path: relPath,
|
|
294
|
+
kind,
|
|
293
295
|
mode: resolveArchiveEntryMode({
|
|
294
|
-
kind
|
|
295
|
-
? "directory"
|
|
296
|
-
: "file",
|
|
296
|
+
kind,
|
|
297
297
|
archivedMode: info.mode,
|
|
298
298
|
policy: params.entryModes,
|
|
299
299
|
}),
|
|
300
300
|
});
|
|
301
|
+
entry.mode = kind === "directory" ? 0o700 : 0o600;
|
|
301
302
|
return true;
|
|
302
303
|
}
|
|
303
304
|
catch (error) {
|
|
@@ -329,18 +330,9 @@ export async function extractArchive(params) {
|
|
|
329
330
|
throw normalizeTarParserError(createPipelineTimeoutError(error, deadline));
|
|
330
331
|
}
|
|
331
332
|
admission.finish();
|
|
332
|
-
for (const accepted of acceptedEntries) {
|
|
333
|
-
deadline.check();
|
|
334
|
-
const outputPath = resolveArchiveOutputPath({
|
|
335
|
-
rootDir: stagingDir,
|
|
336
|
-
relPath: accepted.path,
|
|
337
|
-
originalPath: accepted.path,
|
|
338
|
-
});
|
|
339
|
-
await fs.chmod(outputPath, accepted.mode);
|
|
340
|
-
deadline.check();
|
|
341
|
-
}
|
|
342
333
|
deadline.check();
|
|
343
|
-
await
|
|
334
|
+
await mergePlannedArchiveIntoDestination({
|
|
335
|
+
entries: acceptedEntries,
|
|
344
336
|
sourceDir: stagingDir,
|
|
345
337
|
destinationDir: params.destDir,
|
|
346
338
|
destinationRealDir,
|
|
@@ -1,19 +1,31 @@
|
|
|
1
1
|
import type { BigIntStats, Stats } from "node:fs";
|
|
2
|
-
export type AsyncDirectoryGuard = {
|
|
2
|
+
export type AsyncDirectoryGuard<T extends Stats | BigIntStats = Stats> = {
|
|
3
3
|
dir: string;
|
|
4
4
|
realPath: string;
|
|
5
|
-
stat:
|
|
5
|
+
stat: T;
|
|
6
6
|
};
|
|
7
|
+
export type AnyAsyncDirectoryGuard = AsyncDirectoryGuard<Stats | BigIntStats>;
|
|
7
8
|
export type SyncDirectoryGuard = {
|
|
8
9
|
dir: string;
|
|
9
10
|
realPath: string;
|
|
10
11
|
stat: Stats;
|
|
11
12
|
};
|
|
12
|
-
export declare function createAsyncDirectoryGuard(dir: string
|
|
13
|
-
|
|
13
|
+
export declare function createAsyncDirectoryGuard(dir: string, options: {
|
|
14
|
+
bigint: true;
|
|
15
|
+
}): Promise<AsyncDirectoryGuard<BigIntStats>>;
|
|
16
|
+
export declare function createAsyncDirectoryGuard(dir: string, options?: {
|
|
17
|
+
bigint?: false;
|
|
18
|
+
}): Promise<AsyncDirectoryGuard>;
|
|
19
|
+
export declare function createAsyncDirectoryGuard(dir: string, options: {
|
|
20
|
+
bigint: boolean;
|
|
21
|
+
}): Promise<AnyAsyncDirectoryGuard>;
|
|
22
|
+
export declare function assertAsyncDirectoryGuard(guard: AnyAsyncDirectoryGuard): Promise<void>;
|
|
14
23
|
export declare function createSyncDirectoryGuard(dir: string): SyncDirectoryGuard;
|
|
15
24
|
export declare function assertSyncDirectoryGuard(guard: SyncDirectoryGuard): void;
|
|
16
25
|
export declare function createNearestExistingDirectoryGuard(rootReal: string, targetPath: string): Promise<AsyncDirectoryGuard>;
|
|
26
|
+
export declare function createNearestExistingDirectoryGuard(rootReal: string, targetPath: string, options: {
|
|
27
|
+
bigint: boolean;
|
|
28
|
+
}): Promise<AnyAsyncDirectoryGuard>;
|
|
17
29
|
export declare function createNearestExistingSyncDirectoryGuard(rootReal: string, targetPath: string): SyncDirectoryGuard;
|
|
18
|
-
export declare function inspectDirectoryIdentity(dir: string, expected?: BigIntStats): Promise<BigIntStats>;
|
|
30
|
+
export declare function inspectDirectoryIdentity(dir: string, expected?: Pick<BigIntStats, "dev" | "ino">): Promise<BigIntStats>;
|
|
19
31
|
//# sourceMappingURL=directory-guard.d.ts.map
|