@openclaw/fs-safe 0.10.0 → 0.11.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 +56 -0
- package/LICENSE +1 -0
- package/README.md +36 -5
- package/dist/absolute-path.d.ts.map +1 -1
- package/dist/absolute-path.js +3 -2
- package/dist/advanced.d.ts +4 -0
- package/dist/advanced.d.ts.map +1 -1
- package/dist/advanced.js +4 -0
- package/dist/archive-durability.d.ts +6 -6
- package/dist/archive-durability.d.ts.map +1 -1
- package/dist/archive-durability.js +1 -1
- package/dist/archive-entry.d.ts.map +1 -1
- package/dist/archive-entry.js +4 -5
- package/dist/archive-gzip-tail.d.ts +1 -0
- package/dist/archive-gzip-tail.d.ts.map +1 -1
- package/dist/archive-gzip-tail.js +3 -0
- package/dist/archive-input.d.ts.map +1 -1
- package/dist/archive-input.js +4 -2
- package/dist/archive-merge.d.ts +5 -1
- package/dist/archive-merge.d.ts.map +1 -1
- package/dist/archive-merge.js +15 -12
- package/dist/archive-native.js +4 -4
- package/dist/archive-parser.wasm +0 -0
- package/dist/archive-read.d.ts.map +1 -1
- package/dist/archive-read.js +17 -9
- package/dist/archive-staging.d.ts +6 -3
- package/dist/archive-staging.d.ts.map +1 -1
- package/dist/archive-staging.js +42 -22
- package/dist/archive-tar-stream.d.ts.map +1 -1
- package/dist/archive-tar-stream.js +7 -6
- package/dist/archive-tar-wasm.d.ts.map +1 -1
- package/dist/archive-tar-wasm.js +16 -13
- package/dist/archive-zip-admission.d.ts +1 -1
- package/dist/archive-zip-admission.d.ts.map +1 -1
- package/dist/archive-zip-admission.js +48 -12
- package/dist/archive-zip-loader.d.ts +6 -0
- package/dist/archive-zip-loader.d.ts.map +1 -0
- package/dist/archive-zip-loader.js +38 -0
- package/dist/archive-zip-names.d.ts.map +1 -1
- package/dist/archive-zip-names.js +13 -8
- package/dist/archive-zip-preflight.d.ts +2 -3
- package/dist/archive-zip-preflight.d.ts.map +1 -1
- package/dist/archive-zip-preflight.js +2 -34
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +12 -10
- package/dist/bounded-read.d.ts +5 -0
- package/dist/bounded-read.d.ts.map +1 -1
- package/dist/bounded-read.js +16 -9
- package/dist/copy-file-input.d.ts +0 -1
- package/dist/copy-file-input.d.ts.map +1 -1
- package/dist/copy-file-input.js +4 -26
- package/dist/copy-tree-portable.d.ts.map +1 -1
- package/dist/copy-tree-portable.js +57 -26
- package/dist/copy.d.ts +1 -1
- package/dist/copy.d.ts.map +1 -1
- package/dist/copy.js +3 -1
- package/dist/directory-durability.d.ts.map +1 -1
- package/dist/directory-durability.js +5 -4
- package/dist/directory-guard.d.ts +11 -1
- package/dist/directory-guard.d.ts.map +1 -1
- package/dist/directory-guard.js +53 -11
- package/dist/durability.d.ts +1 -1
- package/dist/durability.d.ts.map +1 -1
- package/dist/durability.js +1 -1
- package/dist/file-handle-transfer.d.ts +14 -0
- package/dist/file-handle-transfer.d.ts.map +1 -0
- package/dist/file-handle-transfer.js +64 -0
- package/dist/file-hash.d.ts +3 -0
- package/dist/file-hash.d.ts.map +1 -1
- package/dist/file-hash.js +91 -31
- package/dist/file-lock-sync.d.ts.map +1 -1
- package/dist/file-lock-sync.js +8 -4
- package/dist/file-store-boundary.d.ts.map +1 -1
- package/dist/file-store-boundary.js +7 -5
- package/dist/file-store-path.d.ts +3 -0
- package/dist/file-store-path.d.ts.map +1 -0
- package/dist/file-store-path.js +27 -0
- package/dist/file-store-prune.d.ts.map +1 -1
- package/dist/file-store-prune.js +6 -4
- package/dist/file-store-sync-write.d.ts.map +1 -1
- package/dist/file-store-sync-write.js +56 -44
- package/dist/file-store.d.ts.map +1 -1
- package/dist/file-store.js +2 -18
- package/dist/filename.d.ts.map +1 -1
- package/dist/filename.js +2 -1
- package/dist/guarded-mkdir.d.ts.map +1 -1
- package/dist/guarded-mkdir.js +3 -2
- package/dist/guest-dispatch-python.d.ts +2 -0
- package/dist/guest-dispatch-python.d.ts.map +1 -0
- package/dist/guest-dispatch-python.js +117 -0
- package/dist/guest-native-python.d.ts +4 -0
- package/dist/guest-native-python.d.ts.map +1 -0
- package/dist/guest-native-python.js +135 -0
- package/dist/guest.d.ts +9 -0
- package/dist/guest.d.ts.map +1 -0
- package/dist/guest.js +413 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/install-path.d.ts.map +1 -1
- package/dist/install-path.js +3 -2
- package/dist/json-durable-queue-directory.js +3 -3
- package/dist/json-durable-queue.d.ts.map +1 -1
- package/dist/json-durable-queue.js +6 -4
- package/dist/json.d.ts.map +1 -1
- package/dist/json.js +2 -1
- package/dist/local-roots.d.ts.map +1 -1
- package/dist/local-roots.js +2 -1
- package/dist/move-path-stage.d.ts.map +1 -1
- package/dist/move-path-stage.js +2 -1
- package/dist/move-path.d.ts.map +1 -1
- package/dist/move-path.js +4 -3
- package/dist/mutation-authority.d.ts +1 -0
- package/dist/mutation-authority.d.ts.map +1 -1
- package/dist/mutation-authority.js +4 -4
- package/dist/native-binding.d.ts +7 -2
- package/dist/native-binding.d.ts.map +1 -1
- package/dist/native-pinned-write-windows.d.ts.map +1 -1
- package/dist/native-pinned-write-windows.js +3 -2
- package/dist/native-pinned-write.d.ts.map +1 -1
- package/dist/native-pinned-write.js +2 -1
- package/dist/opened-realpath.d.ts.map +1 -1
- package/dist/opened-realpath.js +5 -4
- package/dist/output.d.ts +2 -0
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +2 -0
- package/dist/overwrite-file-handle.d.ts +8 -0
- package/dist/overwrite-file-handle.d.ts.map +1 -0
- package/dist/overwrite-file-handle.js +42 -0
- package/dist/path-case.d.ts +7 -0
- package/dist/path-case.d.ts.map +1 -0
- package/dist/path-case.js +136 -0
- package/dist/path.d.ts.map +1 -1
- package/dist/path.js +2 -1
- package/dist/permissions-windows.d.ts +1 -1
- package/dist/permissions-windows.d.ts.map +1 -1
- package/dist/permissions-windows.js +48 -6
- package/dist/pinned-open.d.ts.map +1 -1
- package/dist/pinned-open.js +3 -1
- package/dist/pinned-write.d.ts +2 -2
- package/dist/pinned-write.d.ts.map +1 -1
- package/dist/pinned-write.js +2 -1
- package/dist/private-temp-workspace.d.ts.map +1 -1
- package/dist/private-temp-workspace.js +6 -4
- package/dist/realpath.d.ts +4 -0
- package/dist/realpath.d.ts.map +1 -0
- package/dist/realpath.js +43 -0
- package/dist/recursive-mkdir-path.d.ts +3 -0
- package/dist/recursive-mkdir-path.d.ts.map +1 -0
- package/dist/recursive-mkdir-path.js +8 -0
- package/dist/replace-directory.d.ts.map +1 -1
- package/dist/replace-directory.js +2 -1
- package/dist/replace-file-copy-fallback.d.ts.map +1 -1
- package/dist/replace-file-copy-fallback.js +23 -31
- package/dist/replace-file-copy-source.d.ts.map +1 -1
- package/dist/replace-file-copy-source.js +7 -12
- package/dist/replace-file-mode.d.ts +3 -0
- package/dist/replace-file-mode.d.ts.map +1 -0
- package/dist/replace-file-mode.js +10 -0
- package/dist/replace-file.d.ts +1 -0
- package/dist/replace-file.d.ts.map +1 -1
- package/dist/replace-file.js +14 -8
- package/dist/root-context.d.ts.map +1 -1
- package/dist/root-context.js +8 -8
- package/dist/root-create-input.d.ts +10 -0
- package/dist/root-create-input.d.ts.map +1 -0
- package/dist/root-create-input.js +80 -0
- package/dist/root-directory-list.d.ts +3 -1
- package/dist/root-directory-list.d.ts.map +1 -1
- package/dist/root-directory-list.js +21 -3
- package/dist/root-entries.d.ts +11 -0
- package/dist/root-entries.d.ts.map +1 -0
- package/dist/root-entries.js +61 -0
- package/dist/root-errors.d.ts +5 -5
- package/dist/root-errors.d.ts.map +1 -1
- package/dist/root-errors.js +13 -12
- package/dist/root-impl.d.ts +13 -3
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +40 -36
- package/dist/root-options.d.ts +12 -1
- package/dist/root-options.d.ts.map +1 -1
- package/dist/root-path-existing.d.ts.map +1 -1
- package/dist/root-path-existing.js +4 -3
- package/dist/root-path-symlink.d.ts.map +1 -1
- package/dist/root-path-symlink.js +3 -2
- package/dist/root-paths.d.ts.map +1 -1
- package/dist/root-paths.js +13 -9
- package/dist/root-remove.d.ts +5 -0
- package/dist/root-remove.d.ts.map +1 -0
- package/dist/root-remove.js +286 -0
- package/dist/root-symlink-policy.d.ts +2 -1
- package/dist/root-symlink-policy.d.ts.map +1 -1
- package/dist/root-symlink-policy.js +2 -2
- package/dist/root-write-mode.d.ts.map +1 -1
- package/dist/root-write-mode.js +2 -1
- package/dist/root.d.ts +2 -1
- package/dist/root.d.ts.map +1 -1
- package/dist/secret-file.d.ts.map +1 -1
- package/dist/secret-file.js +2 -1
- package/dist/secret-read-async.d.ts.map +1 -1
- package/dist/secret-read-async.js +2 -1
- package/dist/secure-file.d.ts.map +1 -1
- package/dist/secure-file.js +21 -4
- package/dist/secure-temp-dir.d.ts.map +1 -1
- package/dist/secure-temp-dir.js +2 -1
- package/dist/sibling-staged-file.d.ts +1 -0
- package/dist/sibling-staged-file.d.ts.map +1 -1
- package/dist/sibling-staged-file.js +42 -8
- package/dist/sibling-temp.d.ts +2 -0
- package/dist/sibling-temp.d.ts.map +1 -1
- package/dist/sibling-temp.js +6 -4
- package/dist/sidecar-lock-acquire.d.ts.map +1 -1
- package/dist/sidecar-lock-acquire.js +15 -4
- package/dist/sidecar-lock-policy.d.ts +2 -0
- package/dist/sidecar-lock-policy.d.ts.map +1 -1
- package/dist/sidecar-lock-policy.js +17 -0
- package/dist/staged-directory.d.ts.map +1 -1
- package/dist/staged-directory.js +4 -3
- package/dist/temp-target.d.ts +14 -12
- package/dist/temp-target.d.ts.map +1 -1
- package/dist/temp-target.js +12 -6
- package/dist/trash.d.ts.map +1 -1
- package/dist/trash.js +7 -5
- package/dist/unicode-path.d.ts +3 -0
- package/dist/unicode-path.d.ts.map +1 -0
- package/dist/unicode-path.js +13 -0
- package/dist/walk.d.ts.map +1 -1
- package/dist/walk.js +3 -2
- package/dist/write-file-handle.d.ts +1 -0
- package/dist/write-file-handle.d.ts.map +1 -1
- package/dist/write-file-handle.js +3 -2
- package/docs/advanced.md +4 -0
- package/docs/archive.md +25 -5
- package/docs/atomic.md +17 -1
- package/docs/config.md +1 -0
- package/docs/contributing.md +29 -1
- package/docs/copy.md +75 -6
- package/docs/directory-identity.md +85 -0
- package/docs/durability.md +36 -1
- package/docs/entries.md +109 -0
- package/docs/errors.md +3 -3
- package/docs/file-store.md +15 -0
- package/docs/guest.md +141 -0
- package/docs/in-place-write.md +81 -0
- package/docs/index.md +2 -0
- package/docs/install.md +31 -0
- package/docs/native-helper.md +10 -3
- package/docs/native.md +4 -0
- package/docs/output.md +32 -6
- package/docs/path-case.md +64 -0
- package/docs/path-scope.md +1 -1
- package/docs/permissions.md +11 -2
- package/docs/public-api.md +31 -2
- package/docs/root.md +30 -2
- package/docs/secure-file.md +2 -0
- package/docs/sidecar-lock.md +12 -3
- package/docs/temp.md +35 -6
- package/docs/types.md +1 -1
- package/docs/writing.md +153 -3
- package/package.json +14 -8
package/dist/root-paths.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root-paths.d.ts","sourceRoot":"","sources":["../src/root-paths.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"root-paths.d.ts","sourceRoot":"","sources":["../src/root-paths.ts"],"names":[],"mappings":"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"}
|
package/dist/root-paths.js
CHANGED
|
@@ -5,6 +5,7 @@ import { formatErrorDetail } from "./error-detail.js";
|
|
|
5
5
|
import { FsSafeError } from "./errors.js";
|
|
6
6
|
import { assertNoNulPathInput, hasNodeErrorCode, isNodeError, isNotFoundPathError, isPathInside, isPathRelativeEscape, } from "./path.js";
|
|
7
7
|
import { root as openRoot } from "./root.js";
|
|
8
|
+
import { realpathSync } from "./realpath.js";
|
|
8
9
|
function invalidPath(scopeLabel) {
|
|
9
10
|
return {
|
|
10
11
|
ok: false,
|
|
@@ -12,12 +13,15 @@ function invalidPath(scopeLabel) {
|
|
|
12
13
|
};
|
|
13
14
|
}
|
|
14
15
|
function pathStaysWithinRoot(rootDir, candidatePath) {
|
|
16
|
+
if (process.platform !== "win32") {
|
|
17
|
+
return candidatePath !== rootDir && isPathInside(rootDir, candidatePath);
|
|
18
|
+
}
|
|
15
19
|
const relative = path.relative(rootDir, candidatePath);
|
|
16
20
|
return Boolean(relative) && !isPathRelativeEscape(relative);
|
|
17
21
|
}
|
|
18
22
|
async function resolveRealPathIfExists(targetPath) {
|
|
19
23
|
try {
|
|
20
|
-
return
|
|
24
|
+
return realpathSync.native(targetPath);
|
|
21
25
|
}
|
|
22
26
|
catch {
|
|
23
27
|
return undefined;
|
|
@@ -29,7 +33,7 @@ async function resolveTrustedRootRealPath(rootDir) {
|
|
|
29
33
|
if (!rootLstat.isDirectory() || rootLstat.isSymbolicLink()) {
|
|
30
34
|
return undefined;
|
|
31
35
|
}
|
|
32
|
-
return
|
|
36
|
+
return realpathSync.native(rootDir);
|
|
33
37
|
}
|
|
34
38
|
catch {
|
|
35
39
|
return undefined;
|
|
@@ -50,7 +54,7 @@ async function validateCanonicalPathWithinRoot(params) {
|
|
|
50
54
|
if (params.expect === "file" && candidateLstat.nlink > 1) {
|
|
51
55
|
return "invalid";
|
|
52
56
|
}
|
|
53
|
-
const candidateRealPath =
|
|
57
|
+
const candidateRealPath = realpathSync.native(params.candidatePath);
|
|
54
58
|
return isPathInside(params.rootRealPath, candidateRealPath) ? "ok" : "invalid";
|
|
55
59
|
}
|
|
56
60
|
catch (err) {
|
|
@@ -169,9 +173,9 @@ export async function ensureDirectoryWithinRoot(params) {
|
|
|
169
173
|
return invalidPath(scopeLabel);
|
|
170
174
|
}
|
|
171
175
|
await assertNoSymlinkSegments({ rootDir, targetPath, scopeLabel });
|
|
172
|
-
const rootReal =
|
|
176
|
+
const rootReal = realpathSync.native(rootDir);
|
|
173
177
|
const nearestExistingPath = await resolveNearestExistingPath(targetPath);
|
|
174
|
-
const nearestExistingReal =
|
|
178
|
+
const nearestExistingReal = realpathSync.native(nearestExistingPath);
|
|
175
179
|
if (!isPathInside(rootReal, nearestExistingReal)) {
|
|
176
180
|
return invalidPath(scopeLabel);
|
|
177
181
|
}
|
|
@@ -202,12 +206,12 @@ export async function ensureDirectoryWithinRoot(params) {
|
|
|
202
206
|
}
|
|
203
207
|
}
|
|
204
208
|
}
|
|
205
|
-
const currentReal =
|
|
209
|
+
const currentReal = realpathSync.native(current);
|
|
206
210
|
if (!isPathInside(rootReal, currentReal)) {
|
|
207
211
|
return invalidPath(scopeLabel);
|
|
208
212
|
}
|
|
209
213
|
}
|
|
210
|
-
const targetReal =
|
|
214
|
+
const targetReal = realpathSync.native(targetPath);
|
|
211
215
|
if (!isPathInside(rootReal, targetReal)) {
|
|
212
216
|
return invalidPath(scopeLabel);
|
|
213
217
|
}
|
|
@@ -309,7 +313,7 @@ async function resolveCheckedPathsWithinRoot(params, allowMissingFallback) {
|
|
|
309
313
|
return lexicalPathResult;
|
|
310
314
|
}
|
|
311
315
|
try {
|
|
312
|
-
const resolvedExistingPath =
|
|
316
|
+
const resolvedExistingPath = realpathSync.native(raw);
|
|
313
317
|
const relativePath = path.relative(rootRealPath, resolvedExistingPath);
|
|
314
318
|
if (!isInRoot(relativePath)) {
|
|
315
319
|
return lexicalPathResult;
|
|
@@ -351,7 +355,7 @@ async function resolveCheckedPathsWithinRoot(params, allowMissingFallback) {
|
|
|
351
355
|
scopeLabel: params.scopeLabel,
|
|
352
356
|
});
|
|
353
357
|
const existingPath = await resolveNearestExistingPath(pathResult.fallbackPath);
|
|
354
|
-
const existingRealPath =
|
|
358
|
+
const existingRealPath = realpathSync.native(existingPath);
|
|
355
359
|
if (!isPathInside(rootRealPath, existingRealPath)) {
|
|
356
360
|
return invalidPath(params.scopeLabel);
|
|
357
361
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type RootContext } from "./root-context.js";
|
|
2
|
+
import type { RootRemoveOptions } from "./root-options.js";
|
|
3
|
+
export declare function validateRemoveOptions(options: RootRemoveOptions): void;
|
|
4
|
+
export declare function removePathInRootFallback(root: RootContext, targetPath: string, options: RootRemoveOptions): Promise<void>;
|
|
5
|
+
//# sourceMappingURL=root-remove.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root-remove.d.ts","sourceRoot":"","sources":["../src/root-remove.ts"],"names":[],"mappings":"AAeA,OAAO,EAA6B,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAS3D,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAgBtE;AA0CD,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,WAAW,EACjB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,IAAI,CAAC,CA6Lf"}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import fsSync from "node:fs";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { assertAsyncDirectoryGuard, assertSyncDirectoryGuard, createAsyncDirectoryGuard, } from "./directory-guard.js";
|
|
5
|
+
import { assertMutationNotDenied } from "./deny-mutations.js";
|
|
6
|
+
import { FsSafeError } from "./errors.js";
|
|
7
|
+
import { sameFileIdentityForCleanup } from "./file-identity.js";
|
|
8
|
+
import { MutationAuthorityError } from "./mutation-authority.js";
|
|
9
|
+
import { isNotFoundPathError, isPathInside } from "./path.js";
|
|
10
|
+
import { assertRootIdentityCurrent } from "./root-context.js";
|
|
11
|
+
import { normalizeRemoveGuardError, normalizeRemovePathError, rootPathChangedError } from "./root-errors.js";
|
|
12
|
+
import { assertFinalSymlinkRejected } from "./root-symlink-policy.js";
|
|
13
|
+
import { inspectFileIdentitySync } from "./strict-file-identity.js";
|
|
14
|
+
import { createSuppressedError } from "./suppressed-error.js";
|
|
15
|
+
import { getFsSafeTestHooks } from "./test-hooks.js";
|
|
16
|
+
const DEFAULT_MAX_ENTRIES = 100_000;
|
|
17
|
+
const DEFAULT_MAX_DEPTH = 64;
|
|
18
|
+
export function validateRemoveOptions(options) {
|
|
19
|
+
if (options.order !== undefined && options.order !== "filesystem" && options.order !== "sorted") {
|
|
20
|
+
throw new TypeError("remove order must be filesystem or sorted");
|
|
21
|
+
}
|
|
22
|
+
for (const key of ["maxEntries", "maxDepth"]) {
|
|
23
|
+
const value = options[key];
|
|
24
|
+
if (value !== undefined && value !== Number.POSITIVE_INFINITY && (!Number.isSafeInteger(value) || value < 0)) {
|
|
25
|
+
throw new RangeError(`${key} must be a non-negative safe integer`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (!options.recursive && (options.maxEntries !== undefined || options.maxDepth !== undefined)) {
|
|
29
|
+
throw new TypeError("remove budgets require recursive: true");
|
|
30
|
+
}
|
|
31
|
+
if (!options.recursive && options.order !== undefined) {
|
|
32
|
+
throw new TypeError("remove order requires recursive: true");
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function assertNotAborted(signal) {
|
|
36
|
+
try {
|
|
37
|
+
signal?.throwIfAborted();
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
// Preserve arbitrary abort reasons through Root's filesystem error mapping.
|
|
41
|
+
throw new MutationAuthorityError(error);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function removeOne(root, targetPath, options) {
|
|
45
|
+
let guard;
|
|
46
|
+
try {
|
|
47
|
+
guard = await createAsyncDirectoryGuard(path.dirname(targetPath));
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
if (options.force && isNotFoundPathError(error)) {
|
|
51
|
+
await assertRootIdentityCurrent(root);
|
|
52
|
+
assertNotAborted(options.signal);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
throw normalizeRemoveGuardError(error);
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
await getFsSafeTestHooks()?.beforeRootFallbackMutation?.("remove", targetPath);
|
|
59
|
+
await assertAsyncDirectoryGuard(guard);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
throw normalizeRemoveGuardError(error);
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const isDirectory = fsSync.lstatSync(targetPath).isDirectory();
|
|
66
|
+
assertFinalSymlinkRejected(targetPath, options.mutationSymlinks !== undefined);
|
|
67
|
+
assertNotAborted(options.signal);
|
|
68
|
+
options.assertBeforeMutation?.();
|
|
69
|
+
await (isDirectory ? fs.rmdir(targetPath) : fs.unlink(targetPath));
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (!(options.force && isNotFoundPathError(error)))
|
|
73
|
+
throw normalizeRemovePathError(error);
|
|
74
|
+
}
|
|
75
|
+
await assertAsyncDirectoryGuard(guard).catch(error => { throw normalizeRemoveGuardError(error); });
|
|
76
|
+
assertNotAborted(options.signal);
|
|
77
|
+
}
|
|
78
|
+
export async function removePathInRootFallback(root, targetPath, options) {
|
|
79
|
+
assertNotAborted(options.signal);
|
|
80
|
+
if (!options.recursive) {
|
|
81
|
+
await removeOne(root, targetPath, options);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;
|
|
85
|
+
const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
86
|
+
const details = (target, phase) => ({
|
|
87
|
+
operation: "remove",
|
|
88
|
+
phase,
|
|
89
|
+
relativePath: path.relative(targetPath, target),
|
|
90
|
+
});
|
|
91
|
+
let examined = 0;
|
|
92
|
+
const rootGuard = await createAsyncDirectoryGuard(root.rootReal, { bigint: true });
|
|
93
|
+
if (!sameFileIdentityForCleanup(rootGuard.stat, root.rootIdentity))
|
|
94
|
+
throw rootPathChangedError();
|
|
95
|
+
const guards = [rootGuard];
|
|
96
|
+
let ancestor = root.rootReal;
|
|
97
|
+
const parentRelative = path.relative(root.rootReal, path.dirname(targetPath));
|
|
98
|
+
for (const segment of parentRelative.split(path.sep).filter(Boolean)) {
|
|
99
|
+
ancestor = path.join(ancestor, segment);
|
|
100
|
+
let guard;
|
|
101
|
+
try {
|
|
102
|
+
guard = await createAsyncDirectoryGuard(ancestor, { bigint: true });
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
if (isNotFoundPathError(error)) {
|
|
106
|
+
assertCurrent();
|
|
107
|
+
assertNotAborted(options.signal);
|
|
108
|
+
if (options.force)
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
throw normalizeRemoveGuardError(error);
|
|
112
|
+
}
|
|
113
|
+
if (!isPathInside(guard.realPath, ancestor) || !isPathInside(ancestor, guard.realPath)) {
|
|
114
|
+
throw new FsSafeError("path-mismatch", "removal ancestor changed during operation");
|
|
115
|
+
}
|
|
116
|
+
guards.push(guard);
|
|
117
|
+
}
|
|
118
|
+
function assertCurrent() {
|
|
119
|
+
for (const guard of guards) {
|
|
120
|
+
try {
|
|
121
|
+
assertSyncDirectoryGuard(guard);
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
// force tolerates a vanished leaf, never a lost traversal boundary.
|
|
125
|
+
throw new FsSafeError("path-mismatch", "removal ancestor changed during operation", { cause: error });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const inspect = (target, expected) => {
|
|
130
|
+
assertCurrent();
|
|
131
|
+
try {
|
|
132
|
+
return inspectFileIdentitySync(() => fsSync.lstatSync(target, { bigint: true }), expected);
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
if (options.force && isNotFoundPathError(error))
|
|
136
|
+
return;
|
|
137
|
+
throw normalizeRemovePathError(error, details(target, expected ? "remove" : "inspect"));
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
async function visit(target, depth, entryCounted = false) {
|
|
141
|
+
assertNotAborted(options.signal);
|
|
142
|
+
if ((!entryCounted && examined >= maxEntries) || depth > maxDepth) {
|
|
143
|
+
throw new FsSafeError("too-large", "recursive removal budget exceeded", {
|
|
144
|
+
details: details(target, "inspect"),
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
if (!entryCounted)
|
|
148
|
+
examined += 1;
|
|
149
|
+
await assertMutationNotDenied(target, options.denyMutations, { protectAncestors: true });
|
|
150
|
+
assertNotAborted(options.signal);
|
|
151
|
+
const initial = inspect(target);
|
|
152
|
+
if (!initial)
|
|
153
|
+
return;
|
|
154
|
+
assertFinalSymlinkRejected(target, options.mutationSymlinks !== undefined, details(target, "inspect"));
|
|
155
|
+
if (initial.isDirectory()) {
|
|
156
|
+
let directoryGuard;
|
|
157
|
+
try {
|
|
158
|
+
directoryGuard = await createAsyncDirectoryGuard(target, { bigint: true });
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
assertCurrent();
|
|
162
|
+
assertNotAborted(options.signal);
|
|
163
|
+
if (options.force && isNotFoundPathError(error))
|
|
164
|
+
return;
|
|
165
|
+
throw normalizeRemovePathError(error, details(target, "inspect"));
|
|
166
|
+
}
|
|
167
|
+
if (!isPathInside(directoryGuard.realPath, target) || !isPathInside(target, directoryGuard.realPath) ||
|
|
168
|
+
!sameFileIdentityForCleanup(directoryGuard.stat, initial)) {
|
|
169
|
+
throw new FsSafeError("path-mismatch", "removal directory changed during operation");
|
|
170
|
+
}
|
|
171
|
+
guards.push(directoryGuard);
|
|
172
|
+
try {
|
|
173
|
+
assertCurrent();
|
|
174
|
+
assertNotAborted(options.signal);
|
|
175
|
+
const handle = await fs.opendir(target, { bufferSize: 1 }).catch(error => {
|
|
176
|
+
if (options.force && isNotFoundPathError(error)) {
|
|
177
|
+
assertNotAborted(options.signal);
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
180
|
+
throw normalizeRemovePathError(error, details(target, "enumerate"));
|
|
181
|
+
});
|
|
182
|
+
if (handle) {
|
|
183
|
+
let failed = false;
|
|
184
|
+
let operationError;
|
|
185
|
+
try {
|
|
186
|
+
let names = options.order === "sorted" ? [] : undefined;
|
|
187
|
+
if (names && maxEntries === Infinity) {
|
|
188
|
+
assertCurrent();
|
|
189
|
+
assertNotAborted(options.signal);
|
|
190
|
+
const snapshot = await fs.readdir(target).catch(error => {
|
|
191
|
+
if (options.force && isNotFoundPathError(error))
|
|
192
|
+
return undefined;
|
|
193
|
+
throw normalizeRemovePathError(error, details(target, "enumerate"));
|
|
194
|
+
});
|
|
195
|
+
assertNotAborted(options.signal);
|
|
196
|
+
if (snapshot) {
|
|
197
|
+
assertCurrent();
|
|
198
|
+
names = snapshot;
|
|
199
|
+
examined += names.length;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
while (true) {
|
|
204
|
+
assertCurrent();
|
|
205
|
+
assertNotAborted(options.signal);
|
|
206
|
+
const entry = await handle.read().catch(error => {
|
|
207
|
+
if (options.force && isNotFoundPathError(error))
|
|
208
|
+
return undefined;
|
|
209
|
+
throw normalizeRemovePathError(error, details(target, "enumerate"));
|
|
210
|
+
});
|
|
211
|
+
if (entry === undefined) {
|
|
212
|
+
assertNotAborted(options.signal);
|
|
213
|
+
if (names)
|
|
214
|
+
names.length = 0;
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
assertCurrent();
|
|
218
|
+
assertNotAborted(options.signal);
|
|
219
|
+
if (!entry)
|
|
220
|
+
break;
|
|
221
|
+
if (names) {
|
|
222
|
+
if (examined >= maxEntries) {
|
|
223
|
+
throw new FsSafeError("too-large", "recursive removal budget exceeded", {
|
|
224
|
+
details: details(path.join(target, entry.name), "enumerate"),
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
examined += 1;
|
|
228
|
+
names.push(entry.name);
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
await visit(path.join(target, entry.name), depth + 1);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (names) {
|
|
236
|
+
for (const name of names.sort()) {
|
|
237
|
+
await visit(path.join(target, name), depth + 1, true);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
catch (error) {
|
|
242
|
+
failed = true;
|
|
243
|
+
operationError = error instanceof MutationAuthorityError ? error.rejection : error;
|
|
244
|
+
throw error;
|
|
245
|
+
}
|
|
246
|
+
finally {
|
|
247
|
+
try {
|
|
248
|
+
// Close before rmdir, including on Windows and after revocation.
|
|
249
|
+
await handle.close();
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
const closeError = normalizeRemovePathError(error, details(target, "enumerate"));
|
|
253
|
+
if (failed)
|
|
254
|
+
throw createSuppressedError(closeError, operationError, "recursive removal and close both failed");
|
|
255
|
+
throw closeError;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
finally {
|
|
261
|
+
guards.pop();
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
// A tolerated missing-directory observation still rechecks its target and ancestors.
|
|
265
|
+
await getFsSafeTestHooks()?.beforeRootFallbackMutation?.("remove", target);
|
|
266
|
+
await assertMutationNotDenied(target, options.denyMutations, { protectAncestors: true });
|
|
267
|
+
assertNotAborted(options.signal);
|
|
268
|
+
if (!inspect(target, initial))
|
|
269
|
+
return;
|
|
270
|
+
assertFinalSymlinkRejected(target, options.mutationSymlinks !== undefined, details(target, "remove"));
|
|
271
|
+
assertNotAborted(options.signal);
|
|
272
|
+
options.assertBeforeMutation?.();
|
|
273
|
+
try {
|
|
274
|
+
await (initial.isDirectory() ? fs.rmdir(target) : fs.unlink(target));
|
|
275
|
+
}
|
|
276
|
+
catch (error) {
|
|
277
|
+
if (!(options.force && isNotFoundPathError(error))) {
|
|
278
|
+
throw normalizeRemovePathError(error, details(target, "remove"));
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
assertCurrent();
|
|
282
|
+
assertNotAborted(options.signal);
|
|
283
|
+
}
|
|
284
|
+
await assertRootIdentityCurrent(root);
|
|
285
|
+
await visit(targetPath, 0);
|
|
286
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type FsSafeErrorDetails } from "./errors.js";
|
|
1
2
|
export type SymlinkPolicy = "reject" | "follow-within-root" | "follow-parents-within-root";
|
|
2
3
|
export type MutationSymlinkPolicy = "reject" | "follow-parents-within-root";
|
|
3
4
|
export declare function readSymlinkResolution(symlinks: SymlinkPolicy | undefined): {
|
|
@@ -9,5 +10,5 @@ export declare function mutationSymlinkResolution(symlinks: MutationSymlinkPolic
|
|
|
9
10
|
rejectFinalSymlink: boolean;
|
|
10
11
|
resolveCanonical: boolean;
|
|
11
12
|
};
|
|
12
|
-
export declare function assertFinalSymlinkRejected(targetPath: string, reject: boolean | undefined): void;
|
|
13
|
+
export declare function assertFinalSymlinkRejected(targetPath: string, reject: boolean | undefined, details?: FsSafeErrorDetails): void;
|
|
13
14
|
//# sourceMappingURL=root-symlink-policy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root-symlink-policy.d.ts","sourceRoot":"","sources":["../src/root-symlink-policy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"root-symlink-policy.d.ts","sourceRoot":"","sources":["../src/root-symlink-policy.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGnE,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,oBAAoB,GAAG,4BAA4B,CAAC;AAC3F,MAAM,MAAM,qBAAqB,GAAG,QAAQ,GAAG,4BAA4B,CAAC;AAE5E,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,aAAa,GAAG,SAAS;IAErE,cAAc;IACd,kBAAkB;EAErB;AAED,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,qBAAqB,GAAG,SAAS;IAKjF,cAAc;IACd,kBAAkB;IAClB,gBAAgB;EAEnB;AAID,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,OAAO,GAAG,SAAS,EAC3B,OAAO,CAAC,EAAE,kBAAkB,GAC3B,IAAI,CASN"}
|
|
@@ -19,12 +19,12 @@ export function mutationSymlinkResolution(symlinks) {
|
|
|
19
19
|
}
|
|
20
20
|
// Call after parent fences and immediately before the mutation. This is a
|
|
21
21
|
// final-component check, not an atomic conditional rename against another process.
|
|
22
|
-
export function assertFinalSymlinkRejected(targetPath, reject) {
|
|
22
|
+
export function assertFinalSymlinkRejected(targetPath, reject, details) {
|
|
23
23
|
if (!reject)
|
|
24
24
|
return;
|
|
25
25
|
try {
|
|
26
26
|
if (fs.lstatSync(targetPath).isSymbolicLink()) {
|
|
27
|
-
throw new FsSafeError("symlink", "final symlink not allowed");
|
|
27
|
+
throw new FsSafeError("symlink", "final symlink not allowed", { details });
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root-write-mode.d.ts","sourceRoot":"","sources":["../src/root-write-mode.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"root-write-mode.d.ts","sourceRoot":"","sources":["../src/root-write-mode.ts"],"names":[],"mappings":"AAUA,wBAAsB,sBAAsB,CAAC,MAAM,EAAE;IACnD,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,GAAG,OAAO,CAAC,MAAM,CAAC,CAsClB"}
|
package/dist/root-write-mode.js
CHANGED
|
@@ -3,6 +3,7 @@ import fs from "node:fs/promises";
|
|
|
3
3
|
import { FsSafeError } from "./errors.js";
|
|
4
4
|
import { isNotFoundPathError, isPathInside } from "./path.js";
|
|
5
5
|
import { resolveReadOpenFlags } from "./read-open-flags.js";
|
|
6
|
+
import { realpathSync } from "./realpath.js";
|
|
6
7
|
import { hardlinkedPathNotAllowedError, outsideWorkspaceError } from "./root-errors.js";
|
|
7
8
|
import { inspectFileIdentity } from "./strict-file-identity.js";
|
|
8
9
|
// The caller has already resolved and guarded this write target.
|
|
@@ -30,7 +31,7 @@ export async function inheritWriteTargetMode(params) {
|
|
|
30
31
|
try {
|
|
31
32
|
// A parent can change after guarded resolution. Do not inherit metadata
|
|
32
33
|
// from an outside inode, even if the parent is restored before publication.
|
|
33
|
-
const realPath =
|
|
34
|
+
const realPath = realpathSync.native(params.targetPath);
|
|
34
35
|
if (!isPathInside(params.rootWithSep, realPath))
|
|
35
36
|
throw outsideWorkspaceError();
|
|
36
37
|
await inspectFileIdentity(async () => {
|
package/dist/root.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
export { DEFAULT_ROOT_MAX_BYTES, openLocalFileSafely, readLocalFileSafely, resolveOpenedFileRealPathForHandle, root, type DenyMutationPolicy, type HardlinkPolicy, type MutationSymlinkPolicy, type RenameIdentityPolicy, type OpenResult, type ReadResult, type Root, type RootAppendOptions, type RootCopyOptions, type RootCopySource, type RootCreateJsonOptions, type RootCreateOptions, type RootDefaults, type RootMkdirOptions, type RootMoveOptions, type RootOpenOptions, type RootOpenWritableOptions, type RootOptions, type RootReadOptions, type RootRemoveOptions, type RootWriteJsonOptions, type RootWriteOptions, type SymlinkPolicy, type WritableOpenMode, type WritableOpenResult, } from "./root-impl.js";
|
|
1
|
+
export { DEFAULT_ROOT_MAX_BYTES, openLocalFileSafely, readLocalFileSafely, resolveOpenedFileRealPathForHandle, root, type DenyMutationPolicy, type HardlinkPolicy, type MutationSymlinkPolicy, type RenameIdentityPolicy, type OpenResult, type ReadResult, type Root, type RootAppendOptions, type RootCopyOptions, type RootCopySource, type RootCreateJsonOptions, type RootCreateOptions, type RootCreateStreamOptions, type RootDefaults, type RootMkdirOptions, type RootMoveOptions, type RootOpenOptions, type RootOpenWritableOptions, type RootOptions, type RootReadOptions, type RootRemoveOptions, type RootWriteJsonOptions, type RootWriteOptions, type SymlinkPolicy, type WritableOpenMode, type WritableOpenResult, } from "./root-impl.js";
|
|
2
2
|
export type { RootCopyPublicationReceipt } from "./copy-publication.js";
|
|
3
3
|
export type { CopyCloneMode } from "./copy-policy.js";
|
|
4
|
+
export type { RootEntriesOptions } from "./root-entries.js";
|
|
4
5
|
export type { ContainmentGuarantee } from "./containment.js";
|
|
5
6
|
export type { RootWalkDataEntry, RootWalkDataEntryKind, RootWalkDirectoryErrorBehavior, RootWalkEntry, RootWalkEntryFilter, RootWalkEntryFilterResult, RootWalkEntryKind, RootWalkLimitBehavior, RootWalkOptions, RootWalkSymlinkPolicy, } from "./root-walk.js";
|
|
6
7
|
//# sourceMappingURL=root.d.ts.map
|
package/dist/root.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root.d.ts","sourceRoot":"","sources":["../src/root.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,EACnB,kCAAkC,EAClC,IAAI,EACJ,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,IAAI,EACT,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,GACxB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AACxE,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,YAAY,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC7D,YAAY,EACV,iBAAiB,EACjB,qBAAqB,EACrB,8BAA8B,EAC9B,aAAa,EACb,mBAAmB,EACnB,yBAAyB,EACzB,iBAAiB,EACjB,qBAAqB,EACrB,eAAe,EACf,qBAAqB,GACtB,MAAM,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"root.d.ts","sourceRoot":"","sources":["../src/root.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,EACnB,kCAAkC,EAClC,IAAI,EACJ,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,IAAI,EACT,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,GACxB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AACxE,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,YAAY,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC7D,YAAY,EACV,iBAAiB,EACjB,qBAAqB,EACrB,8BAA8B,EAC9B,aAAa,EACb,mBAAmB,EACnB,yBAAyB,EACzB,iBAAiB,EACjB,qBAAqB,EACrB,eAAe,EACf,qBAAqB,GACtB,MAAM,gBAAgB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"secret-file.d.ts","sourceRoot":"","sources":["../src/secret-file.ts"],"names":[],"mappings":"AAAA,OAAW,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"secret-file.d.ts","sourceRoot":"","sources":["../src/secret-file.ts"],"names":[],"mappings":"AAAA,OAAW,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAM/C,OAAO,EAAkF,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAUhJ,OAAO,EAML,KAAK,qBAAqB,EAC3B,MAAM,yBAAyB,CAAC;AAIjC,eAAO,MAAM,uBAAuB,MAAQ,CAAC;AAC7C,eAAO,MAAM,wBAAwB,MAAQ,CAAC;AAE9C,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,qBAA0B,GAClC,MAAM,CAkER;AAED,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,qBAA0B,GAClC,MAAM,GAAG,SAAS,CAUpB;AA8JD,KAAK,qBAAqB,GAAG;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,UAAU,CAAC;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAYF,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,IAAI,CAAC,qBAAqB,EAAE,SAAS,CAAC,GAC7C,OAAO,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,mBAAmB,CAAC,WAAW,CAAC,CAAC;IAC5C,WAAW,EAAE,mBAAmB,CAAC,WAAW,CAAC,CAAC;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC,CAuBD;AAwDD,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAKxF;AAED,wBAAsB,sBAAsB,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAezF"}
|
package/dist/secret-file.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs, {} from "node:fs";
|
|
2
2
|
import fsp from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
|
|
4
5
|
import { canonicalPathFromExistingAncestor } from "./absolute-path.js";
|
|
5
6
|
import { readFileDescriptorBoundedSync } from "./bounded-read.js";
|
|
6
7
|
import { assertAsyncDirectoryGuard, createAsyncDirectoryGuard, inspectDirectoryIdentity } from "./directory-guard.js";
|
|
@@ -166,7 +167,7 @@ async function ensurePrivateDirectory(rootDir, targetDir, mode) {
|
|
|
166
167
|
catch (error) {
|
|
167
168
|
if (error.code !== "ENOENT")
|
|
168
169
|
throw error;
|
|
169
|
-
await fsp.mkdir(path.dirname(resolvedRoot), { recursive: true, mode });
|
|
170
|
+
await fsp.mkdir(recursiveMkdirPath(path.dirname(resolvedRoot)), { recursive: true, mode });
|
|
170
171
|
createdRoot = await createPrivateDirectory(resolvedRoot, mode);
|
|
171
172
|
}
|
|
172
173
|
rootStat = await inspectPrivateDirectory(resolvedRoot, "root");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"secret-read-async.d.ts","sourceRoot":"","sources":["../src/secret-read-async.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"secret-read-async.d.ts","sourceRoot":"","sources":["../src/secret-read-async.ts"],"names":[],"mappings":"AAQA,OAAO,EAML,KAAK,qBAAqB,EAC3B,MAAM,yBAAyB,CAAC;AAEjC,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,MAAM,CAAC,CA0DjB;AAED,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAQ7B"}
|
|
@@ -4,6 +4,7 @@ import { readFileHandleBounded } from "./bounded-read.js";
|
|
|
4
4
|
import { assertNoUnsafeDeviceReadPath } from "./device-path.js";
|
|
5
5
|
import { FsSafeError } from "./errors.js";
|
|
6
6
|
import { resolveReadOpenFlags } from "./read-open-flags.js";
|
|
7
|
+
import { realpathSync } from "./realpath.js";
|
|
7
8
|
import { inspectFileIdentity } from "./strict-file-identity.js";
|
|
8
9
|
import { assertSecretFilePreview, resolveSecretReadPolicy, secretPathErrorCode, secretReadError, trimSecretFileContent, } from "./secret-read-policy.js";
|
|
9
10
|
export async function readSecretFile(filePath, label, options = {}) {
|
|
@@ -29,7 +30,7 @@ export async function readSecretFile(filePath, label, options = {}) {
|
|
|
29
30
|
let handle;
|
|
30
31
|
let raw;
|
|
31
32
|
try {
|
|
32
|
-
const realPath =
|
|
33
|
+
const realPath = realpathSync.native(resolvedPath);
|
|
33
34
|
assertNoUnsafeDeviceReadPath(realPath);
|
|
34
35
|
handle = await fs.open(realPath, resolveReadOpenFlags());
|
|
35
36
|
const openedHandle = handle;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"secure-file.d.ts","sourceRoot":"","sources":["../src/secure-file.ts"],"names":[],"mappings":"AAAA,OAAe,
|
|
1
|
+
{"version":3,"file":"secure-file.d.ts","sourceRoot":"","sources":["../src/secure-file.ts"],"names":[],"mappings":"AAAA,OAAe,EAAoB,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAc/D,OAAO,EAOL,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC5B,MAAM,kBAAkB,CAAC;AAI1B,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,sBAAsB,CAAC;IAC/B,WAAW,CAAC,EAAE,2BAA2B,CAAC;IAC1C,MAAM,CAAC,EAAE,uBAAuB,CAAC;IACjC,EAAE,CAAC,EAAE,mBAAmB,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG,sBAAsB,CAAC;AAE7D,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,KAAK,CAAC;IACZ,WAAW,CAAC,EAAE,eAAe,CAAC;CAC/B,CAAC;AAmNF,wBAAsB,cAAc,CAClC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,oBAAoB,CAAC,CAuB/B"}
|
package/dist/secure-file.js
CHANGED
|
@@ -9,6 +9,7 @@ import { isWindowsDriveLetterPath, isWindowsNetworkPath } from "./local-file-acc
|
|
|
9
9
|
import { isPathInside, isSymlinkOpenError } from "./path.js";
|
|
10
10
|
import { formatPermissionErrorDetail } from "./permission-exec.js";
|
|
11
11
|
import { resolveReadOpenFlags } from "./read-open-flags.js";
|
|
12
|
+
import { realpathSync } from "./realpath.js";
|
|
12
13
|
import { inspectPathPermissions, isGroupReadable, isGroupWritable, isWorldReadable, isWorldWritable, modeBits, } from "./permissions.js";
|
|
13
14
|
import { inspectFileIdentity } from "./strict-file-identity.js";
|
|
14
15
|
import { scheduleTimeout } from "./timing.js";
|
|
@@ -20,6 +21,11 @@ function isAbsolutePathname(value) {
|
|
|
20
21
|
function label(options) {
|
|
21
22
|
return options.label ?? "Secure file";
|
|
22
23
|
}
|
|
24
|
+
function assertNotHardlinked(options, stat) {
|
|
25
|
+
if (stat.nlink > 1n) {
|
|
26
|
+
throw new FsSafeError("hardlink", `${label(options)} must not be hardlinked: ${options.filePath}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
23
29
|
async function openSecureHandle(options, maxBytes) {
|
|
24
30
|
assertNoUnsafeDeviceReadPath(options.filePath);
|
|
25
31
|
if (isWindowsNetworkPath(options.filePath, "win32") && !options.trust?.allowNetworkPath) {
|
|
@@ -63,6 +69,7 @@ async function openSecureHandle(options, maxBytes) {
|
|
|
63
69
|
throw new FsSafeError("not-file", `${label(options)} must be a file: ${options.filePath}`);
|
|
64
70
|
}
|
|
65
71
|
const openedIdentity = await inspectFileIdentity(() => fsSync.fstatSync(handle.fd, { bigint: true }));
|
|
72
|
+
assertNotHardlinked(options, openedIdentity);
|
|
66
73
|
await inspectFileIdentity(async () => {
|
|
67
74
|
const pathStat = options.trust?.allowSymlink
|
|
68
75
|
? fsSync.statSync(options.filePath, { bigint: true })
|
|
@@ -70,14 +77,19 @@ async function openSecureHandle(options, maxBytes) {
|
|
|
70
77
|
if (!options.trust?.allowSymlink && pathStat.isSymbolicLink()) {
|
|
71
78
|
throw new FsSafeError("symlink", `${label(options)} must not be a symlink: ${options.filePath}`);
|
|
72
79
|
}
|
|
80
|
+
assertNotHardlinked(options, pathStat);
|
|
73
81
|
return pathStat;
|
|
74
82
|
}, openedIdentity);
|
|
75
|
-
const realPath =
|
|
76
|
-
await inspectFileIdentity(() =>
|
|
83
|
+
const realPath = realpathSync.native(options.filePath);
|
|
84
|
+
await inspectFileIdentity(() => {
|
|
85
|
+
const realPathStat = fsSync.statSync(realPath, { bigint: true });
|
|
86
|
+
assertNotHardlinked(options, realPathStat);
|
|
87
|
+
return realPathStat;
|
|
88
|
+
}, openedIdentity);
|
|
77
89
|
if (maxBytes !== undefined && openedStat.size > maxBytes) {
|
|
78
90
|
throw new FsSafeError("too-large", `${label(options)} exceeded maxBytes (${maxBytes}).`);
|
|
79
91
|
}
|
|
80
|
-
return { handle, pathStat: openedStat, realPath };
|
|
92
|
+
return { handle, identity: openedIdentity, pathStat: openedStat, realPath };
|
|
81
93
|
}
|
|
82
94
|
catch (err) {
|
|
83
95
|
await handle.close().catch(() => undefined);
|
|
@@ -91,7 +103,7 @@ async function assertTrustedDirs(options, realPath) {
|
|
|
91
103
|
const trusted = await Promise.all(options.trust.trustedDirs.map(async (dir) => {
|
|
92
104
|
const resolved = path.resolve(dir);
|
|
93
105
|
try {
|
|
94
|
-
return
|
|
106
|
+
return realpathSync.native(resolved);
|
|
95
107
|
}
|
|
96
108
|
catch {
|
|
97
109
|
return resolved;
|
|
@@ -184,6 +196,11 @@ export async function readSecureFile(options) {
|
|
|
184
196
|
await assertTrustedDirs(options, opened.realPath);
|
|
185
197
|
const permissions = await assertSecurePermissions(options, opened.pathStat, opened.realPath);
|
|
186
198
|
const buffer = await readHandleWithTimeout(opened.handle, options.io?.timeoutMs, maxBytes);
|
|
199
|
+
const finalIdentity = await inspectFileIdentity(() => fsSync.fstatSync(opened.handle.fd, { bigint: true }), opened.identity);
|
|
200
|
+
if (!finalIdentity.isFile()) {
|
|
201
|
+
throw new FsSafeError("not-file", `${label(options)} must remain a file: ${options.filePath}`);
|
|
202
|
+
}
|
|
203
|
+
assertNotHardlinked(options, finalIdentity);
|
|
187
204
|
return { buffer, realPath: opened.realPath, stat: opened.pathStat, permissions };
|
|
188
205
|
}
|
|
189
206
|
finally {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"secure-temp-dir.d.ts","sourceRoot":"","sources":["../src/secure-temp-dir.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"secure-temp-dir.d.ts","sourceRoot":"","sources":["../src/secure-temp-dir.ts"],"names":[],"mappings":"AAQA,KAAK,aAAa,GAAG;IACnB,WAAW,IAAI,OAAO,CAAC;IACvB,cAAc,IAAI,OAAO,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IAClC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,aAAa,CAAC;IAC5C,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC;IACtB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAWF,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,4BAA4B,GAAG,MAAM,CA+JnF"}
|
package/dist/secure-temp-dir.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import { tmpdir as getOsTmpDir } from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
|
|
4
5
|
import { assertSafePathSegment } from "./safe-path-segment.js";
|
|
5
6
|
function isNodeErrorWithCode(err, code) {
|
|
6
7
|
return (typeof err === "object" &&
|
|
@@ -17,7 +18,7 @@ export function resolveSecureTempRoot(options) {
|
|
|
17
18
|
const accessSync = options.accessSync ?? fs.accessSync;
|
|
18
19
|
const chmodSync = options.chmodSync ?? fs.chmodSync;
|
|
19
20
|
const lstatSync = options.lstatSync ?? fs.lstatSync;
|
|
20
|
-
const mkdirSync = options.mkdirSync ?? fs.mkdirSync;
|
|
21
|
+
const mkdirSync = options.mkdirSync ?? ((directory, mkdirOptions) => fs.mkdirSync(recursiveMkdirPath(directory), mkdirOptions));
|
|
21
22
|
const warn = options.warn ?? ((message) => console.warn(message));
|
|
22
23
|
const warningPrefix = options.warningPrefix ?? "[fs-safe]";
|
|
23
24
|
const unsafeFallbackLabel = options.unsafeFallbackLabel ?? "secure temp dir";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export declare function writeCallbackSibling<T>(params: {
|
|
2
2
|
tempPath: string;
|
|
3
3
|
write: (tempPath: string) => Promise<T>;
|
|
4
|
+
producerIsolation?: "private-directory";
|
|
4
5
|
resolveFinalPath: (result: T) => string;
|
|
5
6
|
mode?: number;
|
|
6
7
|
/** Preserve the caller's historical best-effort mode behavior. */
|