@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
package/dist/root-impl.d.ts
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import type { Stats } from "node:fs";
|
|
2
2
|
import type { FileHandle } from "node:fs/promises";
|
|
3
|
+
import type { ContainmentGuarantee } from "./containment.js";
|
|
3
4
|
import { type DenyMutationPolicy } from "./deny-mutations.js";
|
|
4
5
|
import { type RenameIdentityPolicy } from "./pinned-write.js";
|
|
5
6
|
import { type ReadResult } from "./read-opened-file.js";
|
|
6
7
|
import type { DirEntry, PathStat } from "./types.js";
|
|
8
|
+
import { type RootWalkEntry, type RootWalkOptions } from "./root-walk.js";
|
|
7
9
|
export type { DenyMutationPolicy } from "./deny-mutations.js";
|
|
8
10
|
export type { RenameIdentityPolicy } from "./pinned-write.js";
|
|
9
11
|
export { resolveOpenedFileRealPathForHandle } from "./opened-realpath.js";
|
|
10
12
|
export type { ReadResult } from "./read-opened-file.js";
|
|
11
13
|
export type OpenResult = {
|
|
12
14
|
handle: FileHandle;
|
|
15
|
+
containment: ContainmentGuarantee;
|
|
13
16
|
realPath: string;
|
|
14
17
|
stat: Stats;
|
|
15
18
|
[Symbol.asyncDispose](): Promise<void>;
|
|
@@ -95,6 +98,7 @@ export interface Root {
|
|
|
95
98
|
withFileTypes: true;
|
|
96
99
|
}): Promise<DirEntry[]>;
|
|
97
100
|
move(fromRelative: string, toRelative: string, options?: RootMoveOptions): Promise<void>;
|
|
101
|
+
walk(relativePath: string, options: RootWalkOptions): AsyncIterableIterator<RootWalkEntry>;
|
|
98
102
|
}
|
|
99
103
|
export declare function root(rootDir: string, defaults?: RootDefaults): Promise<Root>;
|
|
100
104
|
export declare function readLocalFileSafely(params: {
|
|
@@ -106,6 +110,7 @@ export declare function openLocalFileSafely(params: {
|
|
|
106
110
|
}): Promise<OpenResult>;
|
|
107
111
|
export type WritableOpenResult = {
|
|
108
112
|
handle: FileHandle;
|
|
113
|
+
containment: ContainmentGuarantee;
|
|
109
114
|
createdForWrite: boolean;
|
|
110
115
|
realPath: string;
|
|
111
116
|
stat: Stats;
|
package/dist/root-impl.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root-impl.d.ts","sourceRoot":"","sources":["../src/root-impl.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"root-impl.d.ts","sourceRoot":"","sources":["../src/root-impl.ts"],"names":[],"mappings":"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"}
|
package/dist/root-impl.js
CHANGED
|
@@ -2,8 +2,6 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { constants as fsConstants } from "node:fs";
|
|
3
3
|
import fs from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import { pipeline } from "node:stream/promises";
|
|
6
|
-
import { createBoundedReadStream } from "./bounded-read-stream.js";
|
|
7
5
|
import { assertAsyncDirectoryGuard, createAsyncDirectoryGuard, createNearestExistingDirectoryGuard } from "./directory-guard.js";
|
|
8
6
|
import { FsSafeError } from "./errors.js";
|
|
9
7
|
import { syncDirectoryBestEffort } from "./fsync.js";
|
|
@@ -12,19 +10,18 @@ import { mkdirPathComponentsWithGuards } from "./guarded-mkdir.js";
|
|
|
12
10
|
import { withAsyncDirectoryGuards } from "./guarded-mutation.js";
|
|
13
11
|
import { assertMutationNotDenied, mergeDenyMutationPolicies, } from "./deny-mutations.js";
|
|
14
12
|
import { resolveOpenedFileRealPathForHandle } from "./opened-realpath.js";
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import { canFallbackFromPythonError, getFsSafePythonConfig } from "./pinned-python-config.js";
|
|
13
|
+
import { runPinnedWriteHelper, runPinnedWriteWithRenamePolicy, } from "./pinned-write.js";
|
|
14
|
+
import { validatePinnedOperationPayload } from "./pinned-operation.js";
|
|
18
15
|
import { assertNoPathAliasEscape, PATH_ALIAS_POLICIES } from "./path-policy.js";
|
|
19
16
|
import { assertNoNulPathInput, assertNoUnsafeDeviceReadPath, hasNodeErrorCode, isNotFoundPathError, isPathInside, isSymlinkOpenError, } from "./path.js";
|
|
20
17
|
import { readOpenedFileSafely } from "./read-opened-file.js";
|
|
21
|
-
import { helperReaddir, helperStat, runPinnedHelper, } from "./pinned-helper.js";
|
|
22
18
|
import { pathStatFromStats } from "./path-stat.js";
|
|
23
19
|
import { resolveRootPath } from "./root-path.js";
|
|
24
20
|
import { assertValidRootRelativePath, ensureTrailingSep, expandRelativePathWithHome, resolvePathInRoot, resolveRootContext, } from "./root-context.js";
|
|
25
21
|
import { isAlreadyExistsError, normalizePinnedPathError, normalizePinnedWriteError, } from "./root-errors.js";
|
|
26
22
|
import { getFsSafeTestHooks } from "./test-hooks.js";
|
|
27
23
|
import { stringifyJsonDocument } from "./json-stringify.js";
|
|
24
|
+
import { walkRoot } from "./root-walk.js";
|
|
28
25
|
import { registerTempPathForExit } from "./temp-cleanup.js";
|
|
29
26
|
import { serializePathWrite } from "./write-queue.js";
|
|
30
27
|
export { resolveOpenedFileRealPathForHandle } from "./opened-realpath.js";
|
|
@@ -54,6 +51,7 @@ export const DEFAULT_ROOT_MAX_BYTES = 16 * 1024 * 1024;
|
|
|
54
51
|
function openResult(params) {
|
|
55
52
|
return {
|
|
56
53
|
handle: params.handle,
|
|
54
|
+
containment: "best-effort",
|
|
57
55
|
realPath: params.realPath,
|
|
58
56
|
stat: params.stat,
|
|
59
57
|
[Symbol.asyncDispose]: () => params.handle.close().catch(() => undefined),
|
|
@@ -172,7 +170,7 @@ class RootHandle {
|
|
|
172
170
|
};
|
|
173
171
|
}
|
|
174
172
|
async resolve(relativePath) {
|
|
175
|
-
return (await resolvePathInRoot(this.context, relativePath)).resolved;
|
|
173
|
+
return (await resolvePathInRoot(this.context, relativePath, { allowFinalSymlink: true })).resolved;
|
|
176
174
|
}
|
|
177
175
|
async open(relativePath, options = {}) {
|
|
178
176
|
return await openFileInRoot(this.context, {
|
|
@@ -311,58 +309,34 @@ class RootHandle {
|
|
|
311
309
|
}
|
|
312
310
|
async stat(relativePath) {
|
|
313
311
|
assertValidRootRelativePath(relativePath);
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
}
|
|
317
|
-
catch (error) {
|
|
318
|
-
if (canFallbackFromPythonError(error)) {
|
|
319
|
-
return await statPathFallback(this.context, relativePath);
|
|
320
|
-
}
|
|
321
|
-
throw error;
|
|
322
|
-
}
|
|
312
|
+
validatePinnedOperationPayload({ relativePath });
|
|
313
|
+
return await statPathFallback(this.context, relativePath);
|
|
323
314
|
}
|
|
324
315
|
async list(relativePath, options = {}) {
|
|
325
316
|
assertValidRootRelativePath(relativePath);
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
? await helperReaddir(this.rootReal, relativePath, true)
|
|
329
|
-
: await helperReaddir(this.rootReal, relativePath, false);
|
|
330
|
-
}
|
|
331
|
-
catch (error) {
|
|
332
|
-
if (canFallbackFromPythonError(error)) {
|
|
333
|
-
return await listPathFallback(this.context, relativePath, options.withFileTypes === true);
|
|
334
|
-
}
|
|
335
|
-
throw error;
|
|
336
|
-
}
|
|
317
|
+
validatePinnedOperationPayload({ relativePath });
|
|
318
|
+
return await listPathFallback(this.context, relativePath, options.withFileTypes === true);
|
|
337
319
|
}
|
|
338
320
|
async move(fromRelative, toRelative, options = {}) {
|
|
339
321
|
assertValidRootRelativePath(fromRelative);
|
|
340
322
|
assertValidRootRelativePath(toRelative);
|
|
323
|
+
validatePinnedOperationPayload({ from: fromRelative, to: toRelative });
|
|
341
324
|
const denyMutations = mergeDenyMutationPolicies(this.defaults.denyMutations, options.denyMutations);
|
|
342
325
|
await assertMoveMutationAllowed(this.context, {
|
|
343
326
|
fromRelative,
|
|
344
327
|
toRelative,
|
|
345
328
|
denyMutations,
|
|
346
329
|
});
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
fromRelative,
|
|
358
|
-
denyMutations,
|
|
359
|
-
overwrite: options.overwrite ?? false,
|
|
360
|
-
toRelative,
|
|
361
|
-
});
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
364
|
-
throw error;
|
|
365
|
-
}
|
|
330
|
+
await movePathFallback(this.context, {
|
|
331
|
+
fromRelative,
|
|
332
|
+
denyMutations,
|
|
333
|
+
overwrite: options.overwrite ?? false,
|
|
334
|
+
toRelative,
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
walk(relativePath, options) {
|
|
338
|
+
assertValidRootRelativePath(relativePath);
|
|
339
|
+
return walkRoot(this, relativePath, options);
|
|
366
340
|
}
|
|
367
341
|
}
|
|
368
342
|
function readDefaults(defaults) {
|
|
@@ -377,7 +351,9 @@ export async function root(rootDir, defaults = {}) {
|
|
|
377
351
|
return new RootHandle(await resolveRootContext(rootDir), defaults);
|
|
378
352
|
}
|
|
379
353
|
async function openFileInRoot(root, params) {
|
|
380
|
-
const { rootWithSep, resolved } = await resolvePathInRoot(root, params.relativePath
|
|
354
|
+
const { rootWithSep, resolved } = await resolvePathInRoot(root, params.relativePath, {
|
|
355
|
+
allowFinalSymlink: true,
|
|
356
|
+
});
|
|
381
357
|
let opened;
|
|
382
358
|
try {
|
|
383
359
|
opened = await openVerifiedLocalFile(resolved, {
|
|
@@ -448,6 +424,15 @@ function buildAtomicWriteTempPath(targetPath) {
|
|
|
448
424
|
function rootWriteQueueKey(root, relativePath) {
|
|
449
425
|
return `${root.rootReal}\0${relativePath}`;
|
|
450
426
|
}
|
|
427
|
+
async function prepareRootWriteTarget(rootReal, targetPath) {
|
|
428
|
+
const parentPath = await mkdirPathComponentsWithGuards({
|
|
429
|
+
rootReal,
|
|
430
|
+
targetPath: path.dirname(targetPath),
|
|
431
|
+
});
|
|
432
|
+
// Continue through the guarded walk's real parent instead of re-entering
|
|
433
|
+
// the original path through a symlinked component.
|
|
434
|
+
return path.join(parentPath, path.basename(targetPath));
|
|
435
|
+
}
|
|
451
436
|
async function writeTempFileForAtomicReplace(params) {
|
|
452
437
|
const tempHandle = await fs.open(params.tempPath, OPEN_WRITE_CREATE_FLAGS, params.mode);
|
|
453
438
|
try {
|
|
@@ -478,7 +463,7 @@ async function verifyAtomicWriteResult(params) {
|
|
|
478
463
|
}
|
|
479
464
|
}
|
|
480
465
|
async function openWritableFileInRoot(root, params) {
|
|
481
|
-
const { rootReal, rootWithSep, resolved } = await resolvePathInRoot(root, params.relativePath);
|
|
466
|
+
const { rootReal, rootWithSep, resolved } = await resolvePathInRoot(root, params.relativePath, { aliasErrorCode: "path-alias" });
|
|
482
467
|
await assertMutationNotDenied(resolved, params.denyMutations);
|
|
483
468
|
try {
|
|
484
469
|
await assertNoPathAliasEscape({
|
|
@@ -490,15 +475,11 @@ async function openWritableFileInRoot(root, params) {
|
|
|
490
475
|
catch (err) {
|
|
491
476
|
throw new FsSafeError("path-alias", "path alias escape blocked", { cause: err });
|
|
492
477
|
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
await
|
|
496
|
-
await fs.mkdir(path.dirname(resolved), { recursive: true });
|
|
497
|
-
});
|
|
498
|
-
}
|
|
499
|
-
let ioPath = resolved;
|
|
478
|
+
let ioPath = params.mkdir === false
|
|
479
|
+
? resolved
|
|
480
|
+
: await prepareRootWriteTarget(rootReal, resolved);
|
|
500
481
|
try {
|
|
501
|
-
const resolvedRealPath = await fs.realpath(
|
|
482
|
+
const resolvedRealPath = await fs.realpath(ioPath);
|
|
502
483
|
if (!isPathInside(rootWithSep, resolvedRealPath)) {
|
|
503
484
|
throw new FsSafeError("outside-workspace", "file is outside workspace root");
|
|
504
485
|
}
|
|
@@ -583,6 +564,7 @@ async function openWritableFileInRoot(root, params) {
|
|
|
583
564
|
}
|
|
584
565
|
return {
|
|
585
566
|
handle,
|
|
567
|
+
containment: "best-effort",
|
|
586
568
|
createdForWrite,
|
|
587
569
|
realPath,
|
|
588
570
|
stat,
|
|
@@ -638,44 +620,22 @@ async function appendFileInRoot(root, params) {
|
|
|
638
620
|
}
|
|
639
621
|
}
|
|
640
622
|
async function removePathInRoot(root, params) {
|
|
623
|
+
validatePinnedOperationPayload({ relativePath: params.relativePath });
|
|
641
624
|
const resolved = await resolvePinnedRemovePathInRoot(root, params.relativePath, params.denyMutations);
|
|
642
|
-
if (process.platform === "win32") {
|
|
643
|
-
await removePathFallback(resolved);
|
|
644
|
-
return;
|
|
645
|
-
}
|
|
646
625
|
try {
|
|
647
|
-
await
|
|
648
|
-
operation: "remove",
|
|
649
|
-
rootPath: resolved.rootReal,
|
|
650
|
-
relativePath: resolved.relativePosix,
|
|
651
|
-
});
|
|
626
|
+
await removePathFallback(resolved);
|
|
652
627
|
}
|
|
653
628
|
catch (error) {
|
|
654
|
-
if (isPinnedPathHelperSpawnError(error)) {
|
|
655
|
-
await removePathFallback(resolved);
|
|
656
|
-
return;
|
|
657
|
-
}
|
|
658
629
|
throw normalizePinnedPathError(error);
|
|
659
630
|
}
|
|
660
631
|
}
|
|
661
632
|
async function mkdirPathInRoot(root, params) {
|
|
633
|
+
validatePinnedOperationPayload({ relativePath: params.relativePath });
|
|
662
634
|
const resolved = await resolvePinnedPathInRoot(root, params);
|
|
663
|
-
if (process.platform === "win32") {
|
|
664
|
-
await mkdirPathFallback(resolved);
|
|
665
|
-
return;
|
|
666
|
-
}
|
|
667
635
|
try {
|
|
668
|
-
await
|
|
669
|
-
operation: "mkdirp",
|
|
670
|
-
rootPath: resolved.rootReal,
|
|
671
|
-
relativePath: resolved.relativePosix,
|
|
672
|
-
});
|
|
636
|
+
await mkdirPathFallback(resolved);
|
|
673
637
|
}
|
|
674
638
|
catch (error) {
|
|
675
|
-
if (isPinnedPathHelperSpawnError(error)) {
|
|
676
|
-
await mkdirPathFallback(resolved);
|
|
677
|
-
return;
|
|
678
|
-
}
|
|
679
639
|
throw normalizePinnedPathError(error);
|
|
680
640
|
}
|
|
681
641
|
}
|
|
@@ -741,49 +701,25 @@ async function copyFileInRoot(root, params) {
|
|
|
741
701
|
throw new FsSafeError("too-large", `file exceeds limit of ${params.maxBytes} bytes (got ${source.stat.size})`);
|
|
742
702
|
}
|
|
743
703
|
try {
|
|
744
|
-
if (process.platform === "win32") {
|
|
745
|
-
await serializePathWrite(rootWriteQueueKey(root, params.relativePath), async () => {
|
|
746
|
-
await copyFileFallback(root, params, source);
|
|
747
|
-
});
|
|
748
|
-
return;
|
|
749
|
-
}
|
|
750
704
|
const pinned = await resolvePinnedWriteTargetInRoot(root, params.relativePath, params.mode, params.denyMutations);
|
|
751
705
|
await serializePathWrite(pinned.targetPath, async () => {
|
|
752
|
-
|
|
706
|
+
await assertCopySourceCurrent(source);
|
|
707
|
+
const identity = await runPinnedWriteHelper({
|
|
708
|
+
rootPath: pinned.rootReal,
|
|
709
|
+
relativeParentPath: pinned.relativeParentPath,
|
|
710
|
+
basename: pinned.basename,
|
|
711
|
+
mkdir: params.mkdir !== false,
|
|
712
|
+
mode: pinned.mode,
|
|
713
|
+
overwrite: true,
|
|
714
|
+
maxBytes: params.maxBytes,
|
|
715
|
+
input: { kind: "stream", stream: source.handle.createReadStream() },
|
|
716
|
+
});
|
|
753
717
|
try {
|
|
754
|
-
|
|
755
|
-
await copyFileFallback(root, params, source);
|
|
756
|
-
return;
|
|
757
|
-
}
|
|
758
|
-
identity = await runPinnedCopyHelper({
|
|
759
|
-
rootPath: pinned.rootReal,
|
|
760
|
-
relativeParentPath: pinned.relativeParentPath,
|
|
761
|
-
basename: pinned.basename,
|
|
762
|
-
mkdir: params.mkdir !== false,
|
|
763
|
-
mode: pinned.mode,
|
|
764
|
-
overwrite: true,
|
|
765
|
-
maxBytes: params.maxBytes,
|
|
766
|
-
sourcePath: source.realPath,
|
|
767
|
-
sourceIdentity: { dev: source.stat.dev, ino: source.stat.ino },
|
|
768
|
-
});
|
|
718
|
+
await assertCopySourcePathCurrent(source);
|
|
769
719
|
}
|
|
770
720
|
catch (error) {
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
return;
|
|
774
|
-
}
|
|
775
|
-
throw normalizePinnedWriteError(error);
|
|
776
|
-
}
|
|
777
|
-
try {
|
|
778
|
-
await verifyAtomicWriteResult({
|
|
779
|
-
root,
|
|
780
|
-
targetPath: pinned.targetPath,
|
|
781
|
-
expectedIdentity: identity,
|
|
782
|
-
});
|
|
783
|
-
}
|
|
784
|
-
catch (err) {
|
|
785
|
-
emitWriteBoundaryWarning(`post-copy verification failed: ${String(err)}`);
|
|
786
|
-
throw err;
|
|
721
|
+
await removeCopyTargetIfUnchanged(pinned.targetPath, identity).catch(() => undefined);
|
|
722
|
+
throw error;
|
|
787
723
|
}
|
|
788
724
|
});
|
|
789
725
|
}
|
|
@@ -791,8 +727,33 @@ async function copyFileInRoot(root, params) {
|
|
|
791
727
|
await source.handle.close().catch(() => { });
|
|
792
728
|
}
|
|
793
729
|
}
|
|
730
|
+
async function assertCopySourceCurrent(source) {
|
|
731
|
+
const opened = await source.handle.stat();
|
|
732
|
+
if (!sameFileIdentity(opened, source.stat)) {
|
|
733
|
+
throw new FsSafeError("path-mismatch", "copy source descriptor changed");
|
|
734
|
+
}
|
|
735
|
+
await assertCopySourcePathCurrent(source);
|
|
736
|
+
}
|
|
737
|
+
async function assertCopySourcePathCurrent(source) {
|
|
738
|
+
const current = await fs.lstat(source.realPath);
|
|
739
|
+
if (current.isSymbolicLink() || !current.isFile() || !sameFileIdentity(current, source.stat)) {
|
|
740
|
+
throw new FsSafeError("path-mismatch", "copy source path changed");
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
async function removeCopyTargetIfUnchanged(targetPath, identity) {
|
|
744
|
+
const parentGuard = await createAsyncDirectoryGuard(path.dirname(targetPath));
|
|
745
|
+
const current = await fs.lstat(targetPath);
|
|
746
|
+
if (current.isSymbolicLink() || !sameFileIdentity(current, identity)) {
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
await withAsyncDirectoryGuards([parentGuard], async () => {
|
|
750
|
+
await fs.rm(targetPath);
|
|
751
|
+
});
|
|
752
|
+
}
|
|
794
753
|
async function resolvePinnedWriteTargetInRoot(root, relativePath, requestedMode, denyMutations) {
|
|
795
|
-
const { rootReal, rootWithSep, resolved } = await resolvePathInRoot(root, relativePath
|
|
754
|
+
const { rootReal, rootWithSep, resolved } = await resolvePathInRoot(root, relativePath, {
|
|
755
|
+
aliasErrorCode: "path-alias",
|
|
756
|
+
});
|
|
796
757
|
await assertMutationNotDenied(resolved, denyMutations);
|
|
797
758
|
try {
|
|
798
759
|
await assertNoPathAliasEscape({
|
|
@@ -894,8 +855,11 @@ async function resolvePinnedRootPathInRoot(root, params) {
|
|
|
894
855
|
const rootReal = root.rootReal;
|
|
895
856
|
let resolved;
|
|
896
857
|
try {
|
|
858
|
+
const expandedPath = await expandRelativePathWithHome(params.relativePath);
|
|
897
859
|
resolved = await resolveRootPath({
|
|
898
|
-
absolutePath: path.
|
|
860
|
+
absolutePath: path.isAbsolute(expandedPath)
|
|
861
|
+
? expandedPath
|
|
862
|
+
: `${ensureTrailingSep(rootReal)}${expandedPath}`,
|
|
899
863
|
rootPath: rootReal,
|
|
900
864
|
rootCanonicalPath: rootReal,
|
|
901
865
|
boundaryLabel: "root",
|
|
@@ -966,35 +930,51 @@ async function listPathFallback(root, relativePath, withFileTypes) {
|
|
|
966
930
|
}
|
|
967
931
|
}
|
|
968
932
|
async function assertMoveMutationAllowed(root, params) {
|
|
969
|
-
const source = await resolvePathInRoot(root, params.fromRelative
|
|
933
|
+
const source = await resolvePathInRoot(root, params.fromRelative, {
|
|
934
|
+
aliasErrorCode: "path-alias",
|
|
935
|
+
allowFinalSymlink: true,
|
|
936
|
+
});
|
|
970
937
|
await assertMutationNotDenied(source.resolved, params.denyMutations, { protectAncestors: true });
|
|
971
|
-
const target = await resolvePathInRoot(root, params.toRelative
|
|
938
|
+
const target = await resolvePathInRoot(root, params.toRelative, {
|
|
939
|
+
aliasErrorCode: "path-alias",
|
|
940
|
+
allowFinalSymlink: true,
|
|
941
|
+
});
|
|
972
942
|
await assertMutationNotDenied(target.resolved, params.denyMutations, { protectAncestors: true });
|
|
973
943
|
}
|
|
974
944
|
async function movePathFallback(root, params) {
|
|
975
|
-
const source = await resolvePathInRoot(root, params.fromRelative
|
|
945
|
+
const source = await resolvePathInRoot(root, params.fromRelative, {
|
|
946
|
+
aliasErrorCode: "path-alias",
|
|
947
|
+
allowFinalSymlink: true,
|
|
948
|
+
});
|
|
976
949
|
await assertMutationNotDenied(source.resolved, params.denyMutations, { protectAncestors: true });
|
|
977
950
|
await resolvePinnedRootPathInRoot(root, {
|
|
978
951
|
relativePath: params.fromRelative,
|
|
979
952
|
policy: PATH_ALIAS_POLICIES.strict,
|
|
980
953
|
});
|
|
981
|
-
const target = await resolvePathInRoot(root, params.toRelative
|
|
954
|
+
const target = await resolvePathInRoot(root, params.toRelative, {
|
|
955
|
+
aliasErrorCode: "path-alias",
|
|
956
|
+
allowFinalSymlink: true,
|
|
957
|
+
});
|
|
982
958
|
await assertMutationNotDenied(target.resolved, params.denyMutations, { protectAncestors: true });
|
|
983
959
|
await resolvePinnedRootPathInRoot(root, {
|
|
984
960
|
relativePath: params.toRelative,
|
|
985
961
|
policy: PATH_ALIAS_POLICIES.unlinkTarget,
|
|
986
962
|
});
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
963
|
+
const targetStat = await fs.lstat(target.resolved).catch(() => undefined);
|
|
964
|
+
const replacesFinalSymlink = process.platform !== "win32" && params.overwrite && targetStat?.isSymbolicLink() === true;
|
|
965
|
+
if (!replacesFinalSymlink) {
|
|
966
|
+
try {
|
|
967
|
+
await assertNoPathAliasEscape({
|
|
968
|
+
absolutePath: target.resolved,
|
|
969
|
+
rootPath: target.rootReal,
|
|
970
|
+
boundaryLabel: "root",
|
|
971
|
+
});
|
|
972
|
+
}
|
|
973
|
+
catch (error) {
|
|
974
|
+
throw new FsSafeError("path-alias", "path alias escape blocked", {
|
|
975
|
+
cause: error instanceof Error ? error : undefined,
|
|
976
|
+
});
|
|
977
|
+
}
|
|
998
978
|
}
|
|
999
979
|
let sourceStat;
|
|
1000
980
|
try {
|
|
@@ -1081,6 +1061,7 @@ async function writeFileFallback(root, params) {
|
|
|
1081
1061
|
encoding: params.encoding,
|
|
1082
1062
|
mode: mode || 0o600,
|
|
1083
1063
|
});
|
|
1064
|
+
unregisterTempPath.setIdentity(writtenStat);
|
|
1084
1065
|
const commitTempPath = tempPath;
|
|
1085
1066
|
await withAsyncDirectoryGuards([destinationGuard], async () => {
|
|
1086
1067
|
await fs.rename(commitTempPath, destinationPath);
|
|
@@ -1108,7 +1089,9 @@ async function writeFileFallback(root, params) {
|
|
|
1108
1089
|
}
|
|
1109
1090
|
}
|
|
1110
1091
|
async function writeMissingFileFallback(root, params) {
|
|
1111
|
-
const { rootReal, resolved } = await resolvePathInRoot(root, params.relativePath
|
|
1092
|
+
const { rootReal, resolved } = await resolvePathInRoot(root, params.relativePath, {
|
|
1093
|
+
aliasErrorCode: "path-alias",
|
|
1094
|
+
});
|
|
1112
1095
|
await assertMutationNotDenied(resolved, params.denyMutations);
|
|
1113
1096
|
try {
|
|
1114
1097
|
await assertNoPathAliasEscape({
|
|
@@ -1120,14 +1103,14 @@ async function writeMissingFileFallback(root, params) {
|
|
|
1120
1103
|
catch (err) {
|
|
1121
1104
|
throw new FsSafeError("path-alias", "path alias escape blocked", { cause: err });
|
|
1122
1105
|
}
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
const parentGuard = await createAsyncDirectoryGuard(path.dirname(
|
|
1106
|
+
const targetPath = params.mkdir === false
|
|
1107
|
+
? resolved
|
|
1108
|
+
: await prepareRootWriteTarget(rootReal, resolved);
|
|
1109
|
+
const parentGuard = await createAsyncDirectoryGuard(path.dirname(targetPath));
|
|
1127
1110
|
let created = false;
|
|
1128
1111
|
try {
|
|
1129
1112
|
const { handle, writtenStat } = await withAsyncDirectoryGuards([parentGuard], async () => {
|
|
1130
|
-
const handle = await fs.open(
|
|
1113
|
+
const handle = await fs.open(targetPath, OPEN_WRITE_CREATE_FLAGS, params.mode ?? 0o600);
|
|
1131
1114
|
created = true;
|
|
1132
1115
|
try {
|
|
1133
1116
|
if (typeof params.data === "string") {
|
|
@@ -1151,7 +1134,7 @@ async function writeMissingFileFallback(root, params) {
|
|
|
1151
1134
|
await handle.close();
|
|
1152
1135
|
await verifyAtomicWriteResult({
|
|
1153
1136
|
root,
|
|
1154
|
-
targetPath
|
|
1137
|
+
targetPath,
|
|
1155
1138
|
expectedIdentity: writtenStat,
|
|
1156
1139
|
});
|
|
1157
1140
|
created = false;
|
|
@@ -1166,87 +1149,7 @@ async function writeMissingFileFallback(root, params) {
|
|
|
1166
1149
|
}
|
|
1167
1150
|
finally {
|
|
1168
1151
|
if (created) {
|
|
1169
|
-
await fs.rm(
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
|
-
async function copyFileFallback(root, params, source) {
|
|
1174
|
-
let target = null;
|
|
1175
|
-
let sourceClosedByStream = false;
|
|
1176
|
-
let targetClosedByUs = false;
|
|
1177
|
-
let tempHandle = null;
|
|
1178
|
-
let tempPath = null;
|
|
1179
|
-
let unregisterTempPath = null;
|
|
1180
|
-
let tempClosedByStream = false;
|
|
1181
|
-
try {
|
|
1182
|
-
target = await openWritableFileInRoot(root, {
|
|
1183
|
-
relativePath: params.relativePath,
|
|
1184
|
-
mkdir: params.mkdir,
|
|
1185
|
-
mode: params.mode,
|
|
1186
|
-
denyMutations: params.denyMutations,
|
|
1187
|
-
truncateExisting: false,
|
|
1188
|
-
});
|
|
1189
|
-
const destinationPath = target.realPath;
|
|
1190
|
-
const mode = params.mode ?? (target.stat.mode & 0o777);
|
|
1191
|
-
await target.handle.close().catch(() => { });
|
|
1192
|
-
targetClosedByUs = true;
|
|
1193
|
-
const destinationGuard = await createAsyncDirectoryGuard(path.dirname(destinationPath));
|
|
1194
|
-
tempPath = buildAtomicWriteTempPath(destinationPath);
|
|
1195
|
-
unregisterTempPath = registerTempPathForExit(tempPath);
|
|
1196
|
-
tempHandle = await fs.open(tempPath, OPEN_WRITE_CREATE_FLAGS, mode || 0o600);
|
|
1197
|
-
const sourceStream = createBoundedReadStream(source, params.maxBytes);
|
|
1198
|
-
const targetStream = tempHandle.createWriteStream();
|
|
1199
|
-
sourceStream.once("close", () => {
|
|
1200
|
-
sourceClosedByStream = true;
|
|
1201
|
-
});
|
|
1202
|
-
targetStream.once("close", () => {
|
|
1203
|
-
tempClosedByStream = true;
|
|
1204
|
-
});
|
|
1205
|
-
await pipeline(sourceStream, targetStream);
|
|
1206
|
-
const writtenStat = await fs.stat(tempPath);
|
|
1207
|
-
if (!tempClosedByStream) {
|
|
1208
|
-
await tempHandle.close().catch(() => { });
|
|
1209
|
-
tempClosedByStream = true;
|
|
1210
|
-
}
|
|
1211
|
-
tempHandle = null;
|
|
1212
|
-
const commitTempPath = tempPath;
|
|
1213
|
-
await withAsyncDirectoryGuards([destinationGuard], async () => {
|
|
1214
|
-
await fs.rename(commitTempPath, destinationPath);
|
|
1215
|
-
});
|
|
1216
|
-
tempPath = null;
|
|
1217
|
-
unregisterTempPath();
|
|
1218
|
-
unregisterTempPath = null;
|
|
1219
|
-
try {
|
|
1220
|
-
await verifyAtomicWriteResult({
|
|
1221
|
-
root,
|
|
1222
|
-
targetPath: destinationPath,
|
|
1223
|
-
expectedIdentity: writtenStat,
|
|
1224
|
-
});
|
|
1225
|
-
}
|
|
1226
|
-
catch (err) {
|
|
1227
|
-
emitWriteBoundaryWarning(`post-copy verification failed: ${String(err)}`);
|
|
1228
|
-
throw err;
|
|
1229
|
-
}
|
|
1230
|
-
}
|
|
1231
|
-
catch (err) {
|
|
1232
|
-
if (target?.createdForWrite) {
|
|
1233
|
-
await fs.rm(target.realPath, { force: true }).catch(() => { });
|
|
1234
|
-
}
|
|
1235
|
-
throw err;
|
|
1236
|
-
}
|
|
1237
|
-
finally {
|
|
1238
|
-
if (!sourceClosedByStream) {
|
|
1239
|
-
await source.handle.close().catch(() => { });
|
|
1240
|
-
}
|
|
1241
|
-
if (tempHandle && !tempClosedByStream) {
|
|
1242
|
-
await tempHandle.close().catch(() => { });
|
|
1243
|
-
}
|
|
1244
|
-
if (tempPath) {
|
|
1245
|
-
await fs.rm(tempPath, { force: true }).catch(() => { });
|
|
1246
|
-
}
|
|
1247
|
-
unregisterTempPath?.();
|
|
1248
|
-
if (target && !targetClosedByUs) {
|
|
1249
|
-
await target.handle.close().catch(() => { });
|
|
1152
|
+
await fs.rm(targetPath, { force: true }).catch(() => undefined);
|
|
1250
1153
|
}
|
|
1251
1154
|
}
|
|
1252
1155
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root-path-existing.d.ts","sourceRoot":"","sources":["../src/root-path-existing.ts"],"names":[],"mappings":"AAqBA,wBAAsB,8BAA8B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA0BxF;AAED,wBAAgB,kCAAkC,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CA0B7E"}
|