@openclaw/fs-safe 0.8.6 → 0.10.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 +93 -0
- package/README.md +17 -0
- package/dist/advanced.d.ts +1 -0
- package/dist/advanced.d.ts.map +1 -1
- package/dist/advanced.js +1 -0
- package/dist/archive-crc32.d.ts.map +1 -1
- package/dist/archive-crc32.js +6 -1
- package/dist/archive-deadline.d.ts.map +1 -1
- package/dist/archive-deadline.js +3 -4
- package/dist/archive-durability.d.ts +24 -0
- package/dist/archive-durability.d.ts.map +1 -0
- package/dist/archive-durability.js +180 -0
- package/dist/archive-gzip-tail.d.ts +2 -0
- package/dist/archive-gzip-tail.d.ts.map +1 -1
- package/dist/archive-gzip-tail.js +22 -4
- package/dist/archive-input.js +4 -4
- package/dist/archive-kind.d.ts.map +1 -1
- package/dist/archive-kind.js +3 -2
- package/dist/archive-merge.d.ts +1 -0
- package/dist/archive-merge.d.ts.map +1 -1
- package/dist/archive-merge.js +42 -9
- package/dist/archive-native.d.ts +1 -0
- package/dist/archive-native.d.ts.map +1 -1
- package/dist/archive-native.js +4 -2
- package/dist/archive-options.d.ts +2 -0
- package/dist/archive-options.d.ts.map +1 -1
- package/dist/archive-parser.wasm +0 -0
- package/dist/archive-read.d.ts.map +1 -1
- package/dist/archive-read.js +72 -70
- package/dist/archive-staging.d.ts +1 -1
- package/dist/archive-staging.d.ts.map +1 -1
- package/dist/archive-staging.js +24 -19
- package/dist/archive-tar-stream.d.ts +11 -4
- package/dist/archive-tar-stream.d.ts.map +1 -1
- package/dist/archive-tar-stream.js +20 -8
- package/dist/archive-tar-wasm.d.ts.map +1 -1
- package/dist/archive-tar-wasm.js +2 -1
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +9 -4
- package/dist/bounded-read.d.ts +7 -0
- package/dist/bounded-read.d.ts.map +1 -1
- package/dist/bounded-read.js +73 -43
- package/dist/clone-metadata.d.ts +19 -0
- package/dist/clone-metadata.d.ts.map +1 -0
- package/dist/clone-metadata.js +32 -0
- package/dist/copy-file-input.d.ts +23 -0
- package/dist/copy-file-input.d.ts.map +1 -0
- package/dist/copy-file-input.js +91 -0
- package/dist/copy-policy.d.ts +3 -0
- package/dist/copy-policy.d.ts.map +1 -0
- package/dist/copy-policy.js +8 -0
- package/dist/copy-publication.d.ts +15 -0
- package/dist/copy-publication.d.ts.map +1 -0
- package/dist/copy-publication.js +30 -0
- package/dist/copy-tree-portable.d.ts +9 -0
- package/dist/copy-tree-portable.d.ts.map +1 -0
- package/dist/copy-tree-portable.js +191 -0
- package/dist/copy.d.ts +16 -0
- package/dist/copy.d.ts.map +1 -0
- package/dist/copy.js +123 -0
- package/dist/directory-mode-node.d.ts.map +1 -1
- package/dist/directory-mode-node.js +4 -4
- package/dist/durability.d.ts +1 -1
- package/dist/durability.d.ts.map +1 -1
- package/dist/error-detail.d.ts.map +1 -1
- package/dist/error-detail.js +4 -1
- package/dist/file-hash.d.ts +6 -2
- package/dist/file-hash.d.ts.map +1 -1
- package/dist/file-hash.js +49 -12
- package/dist/file-store-sync-write.d.ts +1 -0
- package/dist/file-store-sync-write.d.ts.map +1 -1
- package/dist/file-store-sync-write.js +9 -6
- package/dist/file-store.d.ts +4 -0
- package/dist/file-store.d.ts.map +1 -1
- package/dist/file-store.js +10 -1
- package/dist/filename.d.ts.map +1 -1
- package/dist/filename.js +2 -12
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +1 -2
- package/dist/guarded-mkdir.d.ts +1 -0
- package/dist/guarded-mkdir.d.ts.map +1 -1
- package/dist/guarded-mkdir.js +1 -0
- package/dist/guarded-mutation.d.ts +2 -0
- package/dist/guarded-mutation.d.ts.map +1 -1
- package/dist/guarded-mutation.js +9 -5
- package/dist/home-dir.d.ts.map +1 -1
- package/dist/home-dir.js +9 -7
- 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 +9 -13
- 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 +1 -0
- package/dist/json-durable-queue-directory.js +3 -3
- package/dist/json-durable-queue-ownership.js +3 -2
- package/dist/json-durable-queue.d.ts.map +1 -1
- package/dist/json-durable-queue.js +31 -27
- package/dist/local-roots.d.ts.map +1 -1
- package/dist/local-roots.js +22 -24
- package/dist/move-path-cleanup.d.ts.map +1 -1
- package/dist/move-path-cleanup.js +4 -3
- package/dist/move-path-stage.d.ts +8 -0
- package/dist/move-path-stage.d.ts.map +1 -0
- package/dist/move-path-stage.js +55 -0
- package/dist/move-path.d.ts.map +1 -1
- package/dist/move-path.js +55 -47
- package/dist/mutation-authority.d.ts +8 -0
- package/dist/mutation-authority.d.ts.map +1 -0
- package/dist/mutation-authority.js +36 -0
- package/dist/native-binding.d.ts +24 -1
- package/dist/native-binding.d.ts.map +1 -1
- package/dist/native-operations.d.ts +1 -1
- package/dist/native-operations.d.ts.map +1 -1
- package/dist/native-operations.js +11 -5
- package/dist/native-pinned-write-windows.d.ts.map +1 -1
- package/dist/native-pinned-write-windows.js +18 -7
- package/dist/native-pinned-write.d.ts.map +1 -1
- package/dist/native-pinned-write.js +1 -0
- package/dist/native-staged-file.d.ts +3 -3
- package/dist/native-staged-file.d.ts.map +1 -1
- package/dist/native-staged-file.js +35 -8
- package/dist/opened-file-failure.d.ts +1 -1
- package/dist/opened-file-failure.d.ts.map +1 -1
- package/dist/opened-file-failure.js +3 -2
- package/dist/path.d.ts.map +1 -1
- package/dist/path.js +3 -1
- package/dist/permissions-windows.d.ts.map +1 -1
- package/dist/permissions-windows.js +38 -48
- package/dist/permissions.js +3 -3
- package/dist/pinned-operation.js +1 -1
- package/dist/pinned-write.d.ts +5 -1
- package/dist/pinned-write.d.ts.map +1 -1
- package/dist/pinned-write.js +50 -30
- package/dist/positional-read.d.ts +9 -0
- package/dist/positional-read.d.ts.map +1 -0
- package/dist/positional-read.js +36 -0
- package/dist/private-temp-workspace.d.ts.map +1 -1
- package/dist/private-temp-workspace.js +9 -3
- package/dist/publish-copy-stage.d.ts +13 -0
- package/dist/publish-copy-stage.d.ts.map +1 -0
- package/dist/publish-copy-stage.js +47 -0
- package/dist/publish-file.d.ts.map +1 -1
- package/dist/publish-file.js +26 -25
- package/dist/replace-file-copy-fallback.d.ts.map +1 -1
- package/dist/replace-file-copy-fallback.js +30 -17
- package/dist/replace-file-copy-source.d.ts.map +1 -1
- package/dist/replace-file-copy-source.js +10 -5
- package/dist/replace-file-temp-owner.d.ts.map +1 -1
- package/dist/replace-file-temp-owner.js +6 -2
- package/dist/replace-file.d.ts.map +1 -1
- package/dist/replace-file.js +8 -3
- package/dist/root-context.d.ts +3 -0
- package/dist/root-context.d.ts.map +1 -1
- package/dist/root-context.js +51 -26
- package/dist/root-directory-list.d.ts +24 -0
- package/dist/root-directory-list.d.ts.map +1 -0
- package/dist/root-directory-list.js +201 -0
- package/dist/root-errors.d.ts +1 -0
- package/dist/root-errors.d.ts.map +1 -1
- package/dist/root-errors.js +3 -0
- package/dist/root-file.d.ts +2 -0
- package/dist/root-file.d.ts.map +1 -1
- package/dist/root-file.js +12 -4
- package/dist/root-impl.d.ts +6 -50
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +342 -208
- package/dist/root-options.d.ts +66 -0
- package/dist/root-options.d.ts.map +1 -0
- package/dist/root-options.js +18 -0
- package/dist/root-path-existing.d.ts +5 -0
- package/dist/root-path-existing.d.ts.map +1 -1
- package/dist/root-path-existing.js +56 -1
- package/dist/root-path.d.ts +1 -0
- package/dist/root-path.d.ts.map +1 -1
- package/dist/root-path.js +74 -162
- package/dist/root-symlink-policy.d.ts +13 -0
- package/dist/root-symlink-policy.d.ts.map +1 -0
- package/dist/root-symlink-policy.js +34 -0
- package/dist/root-walk.d.ts +5 -4
- package/dist/root-walk.d.ts.map +1 -1
- package/dist/root-walk.js +99 -50
- package/dist/root-write-mode.d.ts.map +1 -1
- package/dist/root-write-mode.js +1 -4
- package/dist/root.d.ts +3 -1
- package/dist/root.d.ts.map +1 -1
- package/dist/secret-file.d.ts +1 -0
- package/dist/secret-file.d.ts.map +1 -1
- package/dist/secret-file.js +1 -0
- package/dist/secret-read-async.js +5 -5
- package/dist/secure-file.d.ts +1 -1
- package/dist/secure-file.d.ts.map +1 -1
- package/dist/secure-file.js +23 -13
- package/dist/sibling-staged-file.js +7 -7
- package/dist/sibling-temp.d.ts.map +1 -1
- package/dist/sibling-temp.js +16 -7
- package/dist/sidecar-lock-acquire.d.ts +1 -1
- package/dist/sidecar-lock-acquire.d.ts.map +1 -1
- package/dist/sidecar-lock-acquire.js +6 -4
- package/dist/sidecar-lock-policy.d.ts.map +1 -1
- package/dist/sidecar-lock-policy.js +5 -3
- package/dist/sidecar-lock-reclaim.d.ts.map +1 -1
- package/dist/sidecar-lock-reclaim.js +16 -4
- package/dist/sidecar-lock.d.ts.map +1 -1
- package/dist/sidecar-lock.js +2 -1
- package/dist/temp-cleanup.d.ts.map +1 -1
- package/dist/temp-cleanup.js +3 -2
- package/dist/temp-target.d.ts.map +1 -1
- package/dist/temp-target.js +9 -4
- package/dist/timing.d.ts +1 -0
- package/dist/timing.d.ts.map +1 -1
- package/dist/timing.js +25 -6
- package/dist/trash.js +2 -2
- package/dist/walk.d.ts.map +1 -1
- package/dist/walk.js +7 -27
- package/dist/windows-owner.d.ts +9 -12
- package/dist/windows-owner.d.ts.map +1 -1
- package/dist/windows-owner.js +24 -58
- package/dist/write-file-handle.d.ts +6 -0
- package/dist/write-file-handle.d.ts.map +1 -0
- package/dist/write-file-handle.js +25 -0
- package/dist/write-open-flags.d.ts.map +1 -1
- package/dist/write-open-flags.js +1 -2
- package/docs/advanced.md +18 -4
- package/docs/archive.md +57 -9
- package/docs/atomic.md +7 -0
- package/docs/contributing.md +7 -0
- package/docs/copy.md +86 -0
- package/docs/durability.md +17 -2
- package/docs/errors.md +1 -1
- package/docs/file-store.md +48 -3
- package/docs/json-store.md +10 -0
- package/docs/local-roots.md +8 -1
- package/docs/native.md +14 -1
- package/docs/permissions.md +20 -10
- package/docs/positional-read.md +63 -0
- package/docs/reading.md +6 -0
- package/docs/root.md +168 -6
- package/docs/secret-file.md +6 -0
- package/docs/security-model.md +14 -0
- package/docs/sidecar-lock.md +2 -0
- package/docs/timing.md +2 -0
- package/docs/types.md +18 -11
- package/docs/walk.md +54 -5
- package/docs/writing.md +25 -5
- package/package.json +16 -11
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { DenyMutationPolicy } from "./deny-mutations.js";
|
|
2
|
+
import type { RenameIdentityPolicy } from "./pinned-write.js";
|
|
3
|
+
import type { MutationSymlinkPolicy, SymlinkPolicy } from "./root-symlink-policy.js";
|
|
4
|
+
import type { CopyCloneMode } from "./copy-policy.js";
|
|
5
|
+
import type { RootCopyPublicationReceipt } from "./copy-publication.js";
|
|
6
|
+
import type { Root } from "./root-impl.js";
|
|
7
|
+
export declare const DEFAULT_ROOT_MAX_BYTES: number;
|
|
8
|
+
export type RootOptions = {
|
|
9
|
+
rootDir: string;
|
|
10
|
+
defaults?: RootDefaults;
|
|
11
|
+
};
|
|
12
|
+
export type HardlinkPolicy = "reject" | "allow";
|
|
13
|
+
export type WritableOpenMode = "replace" | "append" | "update";
|
|
14
|
+
export type RootDefaults = {
|
|
15
|
+
assertBeforeMutation?: () => void;
|
|
16
|
+
durable?: boolean;
|
|
17
|
+
hardlinks?: HardlinkPolicy;
|
|
18
|
+
maxBytes?: number;
|
|
19
|
+
mkdir?: boolean;
|
|
20
|
+
mode?: number;
|
|
21
|
+
denyMutations?: DenyMutationPolicy;
|
|
22
|
+
nonBlockingRead?: boolean;
|
|
23
|
+
renameIdentity?: RenameIdentityPolicy;
|
|
24
|
+
symlinks?: SymlinkPolicy;
|
|
25
|
+
mutationSymlinks?: MutationSymlinkPolicy;
|
|
26
|
+
};
|
|
27
|
+
export type RootReadOptions = Pick<RootDefaults, "hardlinks" | "maxBytes" | "nonBlockingRead" | "symlinks">;
|
|
28
|
+
export type RootOpenOptions = Omit<RootReadOptions, "maxBytes">;
|
|
29
|
+
export type RootWriteOptions = Pick<RootDefaults, "assertBeforeMutation" | "denyMutations" | "mutationSymlinks" | "durable" | "mkdir" | "mode" | "renameIdentity"> & {
|
|
30
|
+
encoding?: BufferEncoding;
|
|
31
|
+
overwrite?: boolean;
|
|
32
|
+
};
|
|
33
|
+
export type RootOpenWritableOptions = Pick<RootDefaults, "assertBeforeMutation" | "denyMutations" | "mutationSymlinks" | "mkdir" | "mode"> & {
|
|
34
|
+
writeMode?: WritableOpenMode;
|
|
35
|
+
};
|
|
36
|
+
export type RootCopyOptions = Pick<RootDefaults, "assertBeforeMutation" | "denyMutations" | "mutationSymlinks" | "durable" | "maxBytes" | "mkdir" | "mode"> & {
|
|
37
|
+
sourceHardlinks?: HardlinkPolicy;
|
|
38
|
+
overwrite?: boolean;
|
|
39
|
+
clone?: CopyCloneMode;
|
|
40
|
+
signal?: AbortSignal;
|
|
41
|
+
preserveSourceMode?: boolean;
|
|
42
|
+
onDestinationPublished?: (receipt: RootCopyPublicationReceipt) => void;
|
|
43
|
+
};
|
|
44
|
+
export type RootCopySource = string | {
|
|
45
|
+
root: Pick<Root, "open" | "stat">;
|
|
46
|
+
relativePath: string;
|
|
47
|
+
};
|
|
48
|
+
export type RootWriteJsonOptions = RootWriteOptions & {
|
|
49
|
+
replacer?: Parameters<typeof JSON.stringify>[1];
|
|
50
|
+
space?: Parameters<typeof JSON.stringify>[2];
|
|
51
|
+
trailingNewline?: boolean;
|
|
52
|
+
};
|
|
53
|
+
export type RootCreateOptions = Omit<RootWriteOptions, "overwrite">;
|
|
54
|
+
export type RootCreateJsonOptions = Omit<RootWriteJsonOptions, "overwrite">;
|
|
55
|
+
export type RootAppendOptions = RootWriteOptions & {
|
|
56
|
+
prependNewlineIfNeeded?: boolean;
|
|
57
|
+
};
|
|
58
|
+
export type RootMoveOptions = Pick<RootDefaults, "assertBeforeMutation" | "denyMutations" | "mutationSymlinks"> & {
|
|
59
|
+
overwrite?: boolean;
|
|
60
|
+
};
|
|
61
|
+
export type RootRemoveOptions = Pick<RootDefaults, "assertBeforeMutation" | "denyMutations" | "mutationSymlinks">;
|
|
62
|
+
export type RootMkdirOptions = Pick<RootDefaults, "assertBeforeMutation" | "denyMutations" | "mutationSymlinks">;
|
|
63
|
+
export type RootReadParams = Omit<RootReadOptions, "nonBlockingRead">;
|
|
64
|
+
export declare function readDefaults(defaults: RootDefaults): RootReadParams;
|
|
65
|
+
export declare function mergeReadOptions(defaults: RootDefaults, options: RootReadOptions): RootReadParams;
|
|
66
|
+
//# sourceMappingURL=root-options.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root-options.d.ts","sourceRoot":"","sources":["../src/root-options.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { normalizeMaxBytes } from "./byte-budget.js";
|
|
2
|
+
export const DEFAULT_ROOT_MAX_BYTES = 16 * 1024 * 1024;
|
|
3
|
+
export function readDefaults(defaults) {
|
|
4
|
+
return {
|
|
5
|
+
hardlinks: defaults.hardlinks,
|
|
6
|
+
maxBytes: normalizeMaxBytes(defaults.maxBytes, { defaultValue: DEFAULT_ROOT_MAX_BYTES }),
|
|
7
|
+
symlinks: defaults.symlinks,
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export function mergeReadOptions(defaults, options) {
|
|
11
|
+
const merged = readDefaults(defaults);
|
|
12
|
+
if (options.hardlinks !== undefined)
|
|
13
|
+
merged.hardlinks = options.hardlinks;
|
|
14
|
+
merged.maxBytes = normalizeMaxBytes(options.maxBytes, { defaultValue: merged.maxBytes });
|
|
15
|
+
if (options.symlinks !== undefined)
|
|
16
|
+
merged.symlinks = options.symlinks;
|
|
17
|
+
return merged;
|
|
18
|
+
}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
export declare function absolutePathWithRawSegments(candidate: string): string;
|
|
2
|
+
export declare function rawPathRelativeToCanonicalRoot(candidate: string, rootCanonicalPath: string, options?: {
|
|
3
|
+
rejectSymlinks?: boolean;
|
|
4
|
+
rejectFinalSymlink?: boolean;
|
|
5
|
+
}): string | undefined;
|
|
1
6
|
export declare function resolvePathViaExistingAncestor(targetPath: string): Promise<string>;
|
|
2
7
|
export declare function resolvePathViaExistingAncestorSync(targetPath: string): string;
|
|
3
8
|
//# sourceMappingURL=root-path-existing.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root-path-existing.d.ts","sourceRoot":"","sources":["../src/root-path-existing.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"root-path-existing.d.ts","sourceRoot":"","sources":["../src/root-path-existing.ts"],"names":[],"mappings":"AAMA,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAKrE;AAED,wBAAgB,8BAA8B,CAC5C,SAAS,EAAE,MAAM,EACjB,iBAAiB,EAAE,MAAM,EACzB,OAAO,GAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAAO,GACvE,MAAM,GAAG,SAAS,CAuCpB;AAkBD,wBAAsB,8BAA8B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA0BxF;AAED,wBAAgB,kCAAkC,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CA0B7E"}
|
|
@@ -1,6 +1,61 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { FsSafeError } from "./errors.js";
|
|
4
|
+
import { formatErrorDetail } from "./error-detail.js";
|
|
5
|
+
import { isNotFoundPathError, isPathInside } from "./path.js";
|
|
6
|
+
export function absolutePathWithRawSegments(candidate) {
|
|
7
|
+
if (path.isAbsolute(candidate))
|
|
8
|
+
return candidate;
|
|
9
|
+
const drive = path.parse(candidate).root;
|
|
10
|
+
const base = drive ? path.resolve(drive) : process.cwd();
|
|
11
|
+
return `${base}${path.sep}${candidate.slice(drive.length)}`;
|
|
12
|
+
}
|
|
13
|
+
export function rawPathRelativeToCanonicalRoot(candidate, rootCanonicalPath, options = {}) {
|
|
14
|
+
const absolute = absolutePathWithRawSegments(candidate);
|
|
15
|
+
const raw = process.platform === "win32" ? absolute.replaceAll("/", path.sep) : absolute;
|
|
16
|
+
const filesystemRoot = path.parse(raw).root;
|
|
17
|
+
const segments = raw.slice(filesystemRoot.length).split(path.sep);
|
|
18
|
+
const finalComponentIndex = segments.findLastIndex(segment => segment !== "" && segment !== ".");
|
|
19
|
+
let prefix = filesystemRoot;
|
|
20
|
+
let traversedSymlink = false;
|
|
21
|
+
for (let index = 0; index < segments.length; index += 1) {
|
|
22
|
+
prefix += `${prefix.endsWith(path.sep) ? "" : path.sep}${segments[index]}`;
|
|
23
|
+
let canonical;
|
|
24
|
+
let isSymlink = false;
|
|
25
|
+
try {
|
|
26
|
+
const stat = fs.lstatSync(prefix);
|
|
27
|
+
isSymlink = stat.isSymbolicLink();
|
|
28
|
+
// Check the original leaf before canonicalization can erase an entry alias.
|
|
29
|
+
if (isSymlink && options.rejectFinalSymlink && index === finalComponentIndex) {
|
|
30
|
+
throw new FsSafeError("symlink", "final symlink not allowed");
|
|
31
|
+
}
|
|
32
|
+
if (!isSymlink && !stat.isDirectory() && index < segments.length - 1)
|
|
33
|
+
return undefined;
|
|
34
|
+
traversedSymlink ||= isSymlink;
|
|
35
|
+
canonical = fs.realpathSync.native(prefix);
|
|
36
|
+
if (isSymlink && index < segments.length - 1 && !fs.statSync(canonical).isDirectory())
|
|
37
|
+
return undefined;
|
|
38
|
+
if (isSymlink && !isPathInside(rootCanonicalPath, canonical) && !isPathInside(canonical, rootCanonicalPath)) {
|
|
39
|
+
throw new FsSafeError("outside-workspace", `symlink prefix resolves outside the root ancestry: ${formatErrorDetail(candidate)}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
if (error instanceof FsSafeError)
|
|
44
|
+
throw error;
|
|
45
|
+
if (isSymlink)
|
|
46
|
+
return undefined;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (!isPathInside(rootCanonicalPath, canonical))
|
|
50
|
+
continue;
|
|
51
|
+
if (options.rejectSymlinks && traversedSymlink) {
|
|
52
|
+
throw new FsSafeError("symlink", "symlink path component not allowed");
|
|
53
|
+
}
|
|
54
|
+
return [path.relative(rootCanonicalPath, canonical), ...segments.slice(index + 1)]
|
|
55
|
+
.filter(Boolean).join(path.sep);
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
4
59
|
function isFilesystemRoot(candidate) {
|
|
5
60
|
return path.parse(candidate).root === candidate;
|
|
6
61
|
}
|
package/dist/root-path.d.ts
CHANGED
package/dist/root-path.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root-path.d.ts","sourceRoot":"","sources":["../src/root-path.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"root-path.d.ts","sourceRoot":"","sources":["../src/root-path.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,kCAAkC,EAAE,MAAM,yBAAyB,CAAC;AAE7E,KAAK,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEtE,MAAM,MAAM,mBAAmB,GAAG;IAChC,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC,CAAC;AAEF,eAAO,MAAM,wBAAwB;aACnC,MAAM;;;;aAIN,YAAY;;;;CAIJ,CAAC;AAEX,KAAK,qBAAqB,GAAG;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,KAAK,oBAAoB,GAAG,SAAS,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC;AAEnF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,oBAAoB,CAAC;CAC5B,CAAC;AAEF,wBAAsB,eAAe,CACnC,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,gBAAgB,CAAC,CAM3B;AAeD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,qBAAqB,GAAG,gBAAgB,CAMnF"}
|
package/dist/root-path.js
CHANGED
|
@@ -3,7 +3,7 @@ import path from "node:path";
|
|
|
3
3
|
import { formatErrorDetail, shortPath } from "./error-detail.js";
|
|
4
4
|
import { FsSafeError } from "./errors.js";
|
|
5
5
|
import { assertNoNulPathInput, isNotFoundPathError, isPathInside, isPathRelativeEscape, } from "./path.js";
|
|
6
|
-
import { resolvePathViaExistingAncestor, resolvePathViaExistingAncestorSync, } from "./root-path-existing.js";
|
|
6
|
+
import { absolutePathWithRawSegments, rawPathRelativeToCanonicalRoot, resolvePathViaExistingAncestor, resolvePathViaExistingAncestorSync, } from "./root-path-existing.js";
|
|
7
7
|
import { resolveSymlinkHopPath, resolveSymlinkHopPathSync } from "./root-path-symlink.js";
|
|
8
8
|
import { assertNoDriveRelativePathSegments } from "./safe-path-segment.js";
|
|
9
9
|
export { resolvePathViaExistingAncestorSync } from "./root-path-existing.js";
|
|
@@ -27,34 +27,13 @@ export async function resolveRootPath(params) {
|
|
|
27
27
|
}
|
|
28
28
|
async function resolveRootPathInternal(params) {
|
|
29
29
|
assertValidRootPathInputs(params);
|
|
30
|
+
params = { ...params, absolutePath: absolutePathWithRawSegments(params.absolutePath) };
|
|
30
31
|
const rootPath = path.resolve(params.rootPath);
|
|
31
32
|
const absolutePath = path.resolve(params.absolutePath);
|
|
32
33
|
const rootCanonicalPath = params.rootCanonicalPath
|
|
33
34
|
? path.resolve(params.rootCanonicalPath)
|
|
34
35
|
: await resolvePathViaExistingAncestor(rootPath);
|
|
35
|
-
|
|
36
|
-
resolveParams: params,
|
|
37
|
-
rootPath,
|
|
38
|
-
absolutePath,
|
|
39
|
-
rootCanonicalPath,
|
|
40
|
-
outsideLexicalCanonicalPath: await resolveOutsideLexicalCanonicalPathAsync({
|
|
41
|
-
rootPath,
|
|
42
|
-
absolutePath,
|
|
43
|
-
}),
|
|
44
|
-
});
|
|
45
|
-
const outsideResult = await resolveOutsideRootPathAsync({
|
|
46
|
-
boundaryLabel: params.boundaryLabel,
|
|
47
|
-
context,
|
|
48
|
-
});
|
|
49
|
-
if (outsideResult) {
|
|
50
|
-
return outsideResult;
|
|
51
|
-
}
|
|
52
|
-
return resolveRootPathLexicalAsync({
|
|
53
|
-
params,
|
|
54
|
-
absolutePath: context.absolutePath,
|
|
55
|
-
rootPath: context.rootPath,
|
|
56
|
-
rootCanonicalPath: context.rootCanonicalPath,
|
|
57
|
-
});
|
|
36
|
+
return resolveRootPathLexicalAsync(prepareRootTraversal(params, rootPath, rootCanonicalPath, absolutePath));
|
|
58
37
|
}
|
|
59
38
|
export function resolveRootPathSync(params) {
|
|
60
39
|
try {
|
|
@@ -66,34 +45,28 @@ export function resolveRootPathSync(params) {
|
|
|
66
45
|
}
|
|
67
46
|
function resolveRootPathSyncInternal(params) {
|
|
68
47
|
assertValidRootPathInputs(params);
|
|
48
|
+
params = { ...params, absolutePath: absolutePathWithRawSegments(params.absolutePath) };
|
|
69
49
|
const rootPath = path.resolve(params.rootPath);
|
|
70
50
|
const absolutePath = path.resolve(params.absolutePath);
|
|
71
51
|
const rootCanonicalPath = params.rootCanonicalPath
|
|
72
52
|
? path.resolve(params.rootCanonicalPath)
|
|
73
53
|
: resolvePathViaExistingAncestorSync(rootPath);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
boundaryLabel: params.boundaryLabel,
|
|
86
|
-
context,
|
|
87
|
-
});
|
|
88
|
-
if (outsideResult) {
|
|
89
|
-
return outsideResult;
|
|
54
|
+
return resolveRootPathLexicalSync(prepareRootTraversal(params, rootPath, rootCanonicalPath, absolutePath));
|
|
55
|
+
}
|
|
56
|
+
function prepareRootTraversal(params, rootPath, rootCanonicalPath, absolutePath) {
|
|
57
|
+
let raw = params.absolutePath;
|
|
58
|
+
if (rawPathRelativeToRoot(rootPath, raw) === undefined) {
|
|
59
|
+
const relative = rawPathRelativeToRoot(rootCanonicalPath, raw)
|
|
60
|
+
?? rawPathRelativeToCanonicalRoot(raw, rootCanonicalPath, params);
|
|
61
|
+
if (relative === undefined) {
|
|
62
|
+
throw pathEscapeError({ rootPath, absolutePath: raw, boundaryLabel: params.boundaryLabel });
|
|
63
|
+
}
|
|
64
|
+
raw = `${rootPath}${path.sep}${relative}`;
|
|
90
65
|
}
|
|
91
|
-
return
|
|
92
|
-
params,
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
rootCanonicalPath: context.rootCanonicalPath,
|
|
96
|
-
});
|
|
66
|
+
return {
|
|
67
|
+
params: raw === params.absolutePath ? params : { ...params, absolutePath: raw },
|
|
68
|
+
rootPath, rootCanonicalPath, absolutePath,
|
|
69
|
+
};
|
|
97
70
|
}
|
|
98
71
|
function sanitizeRootPathError(error) {
|
|
99
72
|
if (error instanceof Error) {
|
|
@@ -120,14 +93,18 @@ function assertNoEmbeddedDriveRelativeSegment(filePath, label) {
|
|
|
120
93
|
}
|
|
121
94
|
function createLexicalTraversalState(params) {
|
|
122
95
|
const rawAbsolutePath = params.params.absolutePath;
|
|
123
|
-
const
|
|
124
|
-
|
|
96
|
+
const relative = rawPathRelativeToRoot(params.rootPath, rawAbsolutePath);
|
|
97
|
+
if (relative === undefined)
|
|
98
|
+
throw new Error("Path traversal must begin at the root");
|
|
99
|
+
const segments = splitTraversalSegments(relative);
|
|
125
100
|
return {
|
|
126
|
-
segments
|
|
101
|
+
segments,
|
|
102
|
+
finalComponentIndex: segments.findLastIndex((segment) => segment !== "."),
|
|
127
103
|
allowFinalSymlink: params.params.policy?.allowFinalSymlinkForUnlink === true,
|
|
128
104
|
canonicalCursor: params.rootCanonicalPath,
|
|
129
105
|
lexicalCursor: params.rootPath,
|
|
130
106
|
preserveFinalSymlink: false,
|
|
107
|
+
missingDepth: 0,
|
|
131
108
|
};
|
|
132
109
|
}
|
|
133
110
|
function createLexicalTraversalContext(params) {
|
|
@@ -140,9 +117,12 @@ function createLexicalTraversalContext(params) {
|
|
|
140
117
|
};
|
|
141
118
|
}
|
|
142
119
|
function splitTraversalSegments(value) {
|
|
143
|
-
|
|
120
|
+
const segments = value
|
|
144
121
|
.split(process.platform === "win32" ? /[\\/]+/ : /\/+/)
|
|
145
|
-
.filter(
|
|
122
|
+
.filter(Boolean);
|
|
123
|
+
if (value.endsWith("/") || (process.platform === "win32" && value.endsWith("\\")))
|
|
124
|
+
segments.push(".");
|
|
125
|
+
return segments;
|
|
146
126
|
}
|
|
147
127
|
function rawPathRelativeToRoot(rootPath, candidatePath) {
|
|
148
128
|
if (!path.isAbsolute(candidatePath)) {
|
|
@@ -170,12 +150,6 @@ function assertLexicalCursorInsideBoundary(context, candidatePath) {
|
|
|
170
150
|
absolutePath: context.absolutePath,
|
|
171
151
|
});
|
|
172
152
|
}
|
|
173
|
-
function applyMissingSuffixToCanonicalCursor(context, missingFromIndex) {
|
|
174
|
-
const missingSuffix = context.state.segments.slice(missingFromIndex);
|
|
175
|
-
for (const segment of missingSuffix) {
|
|
176
|
-
advanceCanonicalCursorForSegment(context, segment);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
153
|
function advanceCanonicalCursorForSegment(context, segment) {
|
|
180
154
|
context.state.canonicalCursor = path.resolve(context.state.canonicalCursor, segment);
|
|
181
155
|
assertLexicalCursorInsideBoundary(context, context.state.canonicalCursor);
|
|
@@ -190,17 +164,18 @@ function finalizeLexicalResolution(context, kind) {
|
|
|
190
164
|
kind,
|
|
191
165
|
});
|
|
192
166
|
}
|
|
193
|
-
function handleLexicalLstatFailure(context, error,
|
|
167
|
+
function handleLexicalLstatFailure(context, error, segment) {
|
|
194
168
|
if (!isNotFoundPathError(error)) {
|
|
195
169
|
return false;
|
|
196
170
|
}
|
|
197
|
-
|
|
171
|
+
advanceCanonicalCursorForSegment(context, segment);
|
|
172
|
+
context.state.missingDepth = 1;
|
|
198
173
|
return true;
|
|
199
174
|
}
|
|
200
175
|
function lexicalStatDisposition(params) {
|
|
201
176
|
if (!params.isSymbolicLink)
|
|
202
177
|
return "continue";
|
|
203
|
-
if (params.rejectSymlinks === true && params.isLast) {
|
|
178
|
+
if (params.rejectFinalSymlink || (params.rejectSymlinks === true && params.isLast)) {
|
|
204
179
|
throw new FsSafeError("symlink", "symlink path component not allowed");
|
|
205
180
|
}
|
|
206
181
|
return params.allowFinalSymlink && params.isLast ? "break" : "resolve-link";
|
|
@@ -219,6 +194,19 @@ function applyResolvedSymlinkHop(context, linkCanonical) {
|
|
|
219
194
|
function applyParentTraversalStep(context) {
|
|
220
195
|
context.state.lexicalCursor = path.resolve(context.state.lexicalCursor, "..");
|
|
221
196
|
advanceCanonicalCursorForSegment(context, "..");
|
|
197
|
+
if (context.state.missingDepth > 0)
|
|
198
|
+
context.state.missingDepth -= 1;
|
|
199
|
+
}
|
|
200
|
+
function assertDirectoryBeforeMoreSegments(stat, pathname, isLast) {
|
|
201
|
+
if (!isLast && !stat.isDirectory()) {
|
|
202
|
+
throw Object.assign(new Error(`Path component is not a directory: ${pathname}`), { code: "ENOTDIR" });
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
function assertResolvedLinkDirectory(pathname, isLast) {
|
|
206
|
+
if (isLast)
|
|
207
|
+
return;
|
|
208
|
+
const stat = fs.statSync(pathname);
|
|
209
|
+
assertDirectoryBeforeMoreSegments(stat, pathname, isLast);
|
|
222
210
|
}
|
|
223
211
|
async function resolveRootPathLexicalAsync(params) {
|
|
224
212
|
const context = createLexicalTraversalContext(params);
|
|
@@ -226,25 +214,35 @@ async function resolveRootPathLexicalAsync(params) {
|
|
|
226
214
|
for (let idx = 0; idx < state.segments.length; idx += 1) {
|
|
227
215
|
const segment = state.segments[idx] ?? "";
|
|
228
216
|
const isLast = idx === state.segments.length - 1;
|
|
217
|
+
if (segment === ".")
|
|
218
|
+
continue;
|
|
229
219
|
if (segment === "..") {
|
|
230
220
|
applyParentTraversalStep(context);
|
|
231
221
|
continue;
|
|
232
222
|
}
|
|
233
223
|
state.lexicalCursor = path.join(state.lexicalCursor, segment);
|
|
224
|
+
if (state.missingDepth > 0) {
|
|
225
|
+
advanceCanonicalCursorForSegment(context, segment);
|
|
226
|
+
state.missingDepth += 1;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
234
229
|
let stat;
|
|
235
230
|
try {
|
|
236
231
|
stat = fs.lstatSync(state.lexicalCursor);
|
|
237
232
|
}
|
|
238
233
|
catch (error) {
|
|
239
|
-
if (handleLexicalLstatFailure(context, error,
|
|
240
|
-
|
|
234
|
+
if (handleLexicalLstatFailure(context, error, segment))
|
|
235
|
+
continue;
|
|
241
236
|
throw error;
|
|
242
237
|
}
|
|
243
238
|
const isSymbolicLink = stat.isSymbolicLink();
|
|
239
|
+
if (!isSymbolicLink)
|
|
240
|
+
assertDirectoryBeforeMoreSegments(stat, state.lexicalCursor, isLast);
|
|
244
241
|
const disposition = lexicalStatDisposition({
|
|
245
242
|
isSymbolicLink,
|
|
246
243
|
isLast,
|
|
247
244
|
rejectSymlinks: isSymbolicLink ? context.resolveParams.rejectSymlinks : undefined,
|
|
245
|
+
rejectFinalSymlink: context.resolveParams.rejectFinalSymlink === true && idx === state.finalComponentIndex,
|
|
248
246
|
allowFinalSymlink: state.allowFinalSymlink,
|
|
249
247
|
});
|
|
250
248
|
if (disposition !== "resolve-link") {
|
|
@@ -261,6 +259,7 @@ async function resolveRootPathLexicalAsync(params) {
|
|
|
261
259
|
if (context.resolveParams.rejectSymlinks === true) {
|
|
262
260
|
throw new FsSafeError("symlink", "symlink path component not allowed");
|
|
263
261
|
}
|
|
262
|
+
assertResolvedLinkDirectory(linkCanonical, isLast);
|
|
264
263
|
}
|
|
265
264
|
const kind = await getPathKind(state.canonicalCursor, state.preserveFinalSymlink);
|
|
266
265
|
return finalizeLexicalResolution(context, kind);
|
|
@@ -271,25 +270,35 @@ function resolveRootPathLexicalSync(params) {
|
|
|
271
270
|
for (let idx = 0; idx < state.segments.length; idx += 1) {
|
|
272
271
|
const segment = state.segments[idx] ?? "";
|
|
273
272
|
const isLast = idx === state.segments.length - 1;
|
|
273
|
+
if (segment === ".")
|
|
274
|
+
continue;
|
|
274
275
|
if (segment === "..") {
|
|
275
276
|
applyParentTraversalStep(context);
|
|
276
277
|
continue;
|
|
277
278
|
}
|
|
278
279
|
state.lexicalCursor = path.join(state.lexicalCursor, segment);
|
|
280
|
+
if (state.missingDepth > 0) {
|
|
281
|
+
advanceCanonicalCursorForSegment(context, segment);
|
|
282
|
+
state.missingDepth += 1;
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
279
285
|
let stat;
|
|
280
286
|
try {
|
|
281
287
|
stat = fs.lstatSync(state.lexicalCursor);
|
|
282
288
|
}
|
|
283
289
|
catch (error) {
|
|
284
|
-
if (handleLexicalLstatFailure(context, error,
|
|
285
|
-
|
|
290
|
+
if (handleLexicalLstatFailure(context, error, segment))
|
|
291
|
+
continue;
|
|
286
292
|
throw error;
|
|
287
293
|
}
|
|
288
294
|
const isSymbolicLink = stat.isSymbolicLink();
|
|
295
|
+
if (!isSymbolicLink)
|
|
296
|
+
assertDirectoryBeforeMoreSegments(stat, state.lexicalCursor, isLast);
|
|
289
297
|
const disposition = lexicalStatDisposition({
|
|
290
298
|
isSymbolicLink,
|
|
291
299
|
isLast,
|
|
292
300
|
rejectSymlinks: isSymbolicLink ? context.resolveParams.rejectSymlinks : undefined,
|
|
301
|
+
rejectFinalSymlink: context.resolveParams.rejectFinalSymlink === true && idx === state.finalComponentIndex,
|
|
293
302
|
allowFinalSymlink: state.allowFinalSymlink,
|
|
294
303
|
});
|
|
295
304
|
if (disposition !== "resolve-link") {
|
|
@@ -306,108 +315,11 @@ function resolveRootPathLexicalSync(params) {
|
|
|
306
315
|
if (context.resolveParams.rejectSymlinks === true) {
|
|
307
316
|
throw new FsSafeError("symlink", "symlink path component not allowed");
|
|
308
317
|
}
|
|
318
|
+
assertResolvedLinkDirectory(linkCanonical, isLast);
|
|
309
319
|
}
|
|
310
320
|
const kind = getPathKindSync(state.canonicalCursor, state.preserveFinalSymlink);
|
|
311
321
|
return finalizeLexicalResolution(context, kind);
|
|
312
322
|
}
|
|
313
|
-
function resolveCanonicalOutsideLexicalPath(params) {
|
|
314
|
-
return params.outsideLexicalCanonicalPath ?? params.absolutePath;
|
|
315
|
-
}
|
|
316
|
-
function createBoundaryResolutionContext(params) {
|
|
317
|
-
const lexicalInside = isPathInside(params.rootPath, params.absolutePath);
|
|
318
|
-
const canonicalOutsideLexicalPath = resolveCanonicalOutsideLexicalPath({
|
|
319
|
-
absolutePath: params.absolutePath,
|
|
320
|
-
outsideLexicalCanonicalPath: params.outsideLexicalCanonicalPath,
|
|
321
|
-
});
|
|
322
|
-
assertLexicalBoundaryOrCanonicalAlias({
|
|
323
|
-
skipLexicalRootCheck: params.resolveParams.skipLexicalRootCheck,
|
|
324
|
-
lexicalInside,
|
|
325
|
-
canonicalOutsideLexicalPath,
|
|
326
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
327
|
-
boundaryLabel: params.resolveParams.boundaryLabel,
|
|
328
|
-
rootPath: params.rootPath,
|
|
329
|
-
absolutePath: params.absolutePath,
|
|
330
|
-
});
|
|
331
|
-
return {
|
|
332
|
-
rootPath: params.rootPath,
|
|
333
|
-
absolutePath: params.absolutePath,
|
|
334
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
335
|
-
lexicalInside,
|
|
336
|
-
canonicalOutsideLexicalPath,
|
|
337
|
-
};
|
|
338
|
-
}
|
|
339
|
-
async function resolveOutsideRootPathAsync(params) {
|
|
340
|
-
if (params.context.lexicalInside) {
|
|
341
|
-
return null;
|
|
342
|
-
}
|
|
343
|
-
const kind = await getPathKind(params.context.absolutePath, false);
|
|
344
|
-
return buildOutsideRootPathFromContext({
|
|
345
|
-
boundaryLabel: params.boundaryLabel,
|
|
346
|
-
context: params.context,
|
|
347
|
-
kind,
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
function resolveOutsideRootPathSync(params) {
|
|
351
|
-
if (params.context.lexicalInside) {
|
|
352
|
-
return null;
|
|
353
|
-
}
|
|
354
|
-
const kind = getPathKindSync(params.context.absolutePath, false);
|
|
355
|
-
return buildOutsideRootPathFromContext({
|
|
356
|
-
boundaryLabel: params.boundaryLabel,
|
|
357
|
-
context: params.context,
|
|
358
|
-
kind,
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
|
-
function buildOutsideRootPathFromContext(params) {
|
|
362
|
-
return buildOutsideLexicalRootPath({
|
|
363
|
-
boundaryLabel: params.boundaryLabel,
|
|
364
|
-
rootCanonicalPath: params.context.rootCanonicalPath,
|
|
365
|
-
absolutePath: params.context.absolutePath,
|
|
366
|
-
canonicalOutsideLexicalPath: params.context.canonicalOutsideLexicalPath,
|
|
367
|
-
rootPath: params.context.rootPath,
|
|
368
|
-
kind: params.kind,
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
|
-
async function resolveOutsideLexicalCanonicalPathAsync(params) {
|
|
372
|
-
if (isPathInside(params.rootPath, params.absolutePath)) {
|
|
373
|
-
return undefined;
|
|
374
|
-
}
|
|
375
|
-
return await resolvePathViaExistingAncestor(params.absolutePath);
|
|
376
|
-
}
|
|
377
|
-
function resolveOutsideLexicalCanonicalPathSync(params) {
|
|
378
|
-
if (isPathInside(params.rootPath, params.absolutePath)) {
|
|
379
|
-
return undefined;
|
|
380
|
-
}
|
|
381
|
-
return resolvePathViaExistingAncestorSync(params.absolutePath);
|
|
382
|
-
}
|
|
383
|
-
function buildOutsideLexicalRootPath(params) {
|
|
384
|
-
assertInsideBoundary({
|
|
385
|
-
boundaryLabel: params.boundaryLabel,
|
|
386
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
387
|
-
candidatePath: params.canonicalOutsideLexicalPath,
|
|
388
|
-
absolutePath: params.absolutePath,
|
|
389
|
-
});
|
|
390
|
-
return buildResolvedRootPath({
|
|
391
|
-
absolutePath: params.absolutePath,
|
|
392
|
-
canonicalPath: params.canonicalOutsideLexicalPath,
|
|
393
|
-
rootPath: params.rootPath,
|
|
394
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
395
|
-
kind: params.kind,
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
function assertLexicalBoundaryOrCanonicalAlias(params) {
|
|
399
|
-
if (params.skipLexicalRootCheck || params.lexicalInside) {
|
|
400
|
-
return;
|
|
401
|
-
}
|
|
402
|
-
if (isPathInside(params.rootCanonicalPath, params.canonicalOutsideLexicalPath)) {
|
|
403
|
-
return;
|
|
404
|
-
}
|
|
405
|
-
throw pathEscapeError({
|
|
406
|
-
boundaryLabel: params.boundaryLabel,
|
|
407
|
-
rootPath: params.rootPath,
|
|
408
|
-
absolutePath: params.absolutePath,
|
|
409
|
-
});
|
|
410
|
-
}
|
|
411
323
|
function buildResolvedRootPath(params) {
|
|
412
324
|
return {
|
|
413
325
|
absolutePath: params.absolutePath,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type SymlinkPolicy = "reject" | "follow-within-root" | "follow-parents-within-root";
|
|
2
|
+
export type MutationSymlinkPolicy = "reject" | "follow-parents-within-root";
|
|
3
|
+
export declare function readSymlinkResolution(symlinks: SymlinkPolicy | undefined): {
|
|
4
|
+
rejectSymlinks: boolean;
|
|
5
|
+
rejectFinalSymlink: boolean;
|
|
6
|
+
};
|
|
7
|
+
export declare function mutationSymlinkResolution(symlinks: MutationSymlinkPolicy | undefined): {
|
|
8
|
+
rejectSymlinks: boolean;
|
|
9
|
+
rejectFinalSymlink: boolean;
|
|
10
|
+
resolveCanonical: boolean;
|
|
11
|
+
};
|
|
12
|
+
export declare function assertFinalSymlinkRejected(targetPath: string, reject: boolean | undefined): void;
|
|
13
|
+
//# sourceMappingURL=root-symlink-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root-symlink-policy.d.ts","sourceRoot":"","sources":["../src/root-symlink-policy.ts"],"names":[],"mappings":"AAIA,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,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,SAAS,GAAG,IAAI,CAShG"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { FsSafeError } from "./errors.js";
|
|
3
|
+
import { isNotFoundPathError } from "./path.js";
|
|
4
|
+
export function readSymlinkResolution(symlinks) {
|
|
5
|
+
return {
|
|
6
|
+
rejectSymlinks: symlinks !== "follow-within-root" && symlinks !== "follow-parents-within-root",
|
|
7
|
+
rejectFinalSymlink: symlinks === "follow-parents-within-root",
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export function mutationSymlinkResolution(symlinks) {
|
|
11
|
+
if (symlinks !== undefined && symlinks !== "reject" && symlinks !== "follow-parents-within-root") {
|
|
12
|
+
throw new RangeError("mutationSymlinks must be reject or follow-parents-within-root");
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
rejectSymlinks: symlinks === "reject",
|
|
16
|
+
rejectFinalSymlink: symlinks !== undefined,
|
|
17
|
+
resolveCanonical: symlinks !== undefined,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
// Call after parent fences and immediately before the mutation. This is a
|
|
21
|
+
// final-component check, not an atomic conditional rename against another process.
|
|
22
|
+
export function assertFinalSymlinkRejected(targetPath, reject) {
|
|
23
|
+
if (!reject)
|
|
24
|
+
return;
|
|
25
|
+
try {
|
|
26
|
+
if (fs.lstatSync(targetPath).isSymbolicLink()) {
|
|
27
|
+
throw new FsSafeError("symlink", "final symlink not allowed");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
if (!isNotFoundPathError(error))
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
}
|
package/dist/root-walk.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RootDirectoryListing, RootDirectoryListingOptions } from "./root-directory-list.js";
|
|
2
|
+
import type { PathStat } from "./types.js";
|
|
2
3
|
export type RootWalkSymlinkPolicy = "skip" | "follow-within-root";
|
|
3
4
|
export type RootWalkLimitBehavior = "truncate" | "throw";
|
|
4
5
|
export type RootWalkDirectoryErrorBehavior = "throw" | "skip-and-report";
|
|
@@ -24,6 +25,7 @@ export type RootWalkEntryFilter = (entry: RootWalkDataEntry) => RootWalkEntryFil
|
|
|
24
25
|
export type RootWalkOptions = {
|
|
25
26
|
maxDepth?: number;
|
|
26
27
|
maxEntries?: number;
|
|
28
|
+
order?: "sorted" | "filesystem";
|
|
27
29
|
symlinkPolicy: RootWalkSymlinkPolicy;
|
|
28
30
|
signal?: AbortSignal;
|
|
29
31
|
limitBehavior?: RootWalkLimitBehavior;
|
|
@@ -32,9 +34,8 @@ export type RootWalkOptions = {
|
|
|
32
34
|
};
|
|
33
35
|
type RootWalkCapability = {
|
|
34
36
|
rootReal: string;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}): Promise<DirEntry[]>;
|
|
37
|
+
stat(relativePath: string): Promise<PathStat>;
|
|
38
|
+
list(relativePath: string, options: RootDirectoryListingOptions): Promise<RootDirectoryListing>;
|
|
38
39
|
};
|
|
39
40
|
export declare function walkRoot(root: RootWalkCapability, relativePath: string, options: RootWalkOptions): AsyncGenerator<RootWalkEntry>;
|
|
40
41
|
export {};
|
package/dist/root-walk.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root-walk.d.ts","sourceRoot":"","sources":["../src/root-walk.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"root-walk.d.ts","sourceRoot":"","sources":["../src/root-walk.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAClG,OAAO,KAAK,EAAY,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGrD,MAAM,MAAM,qBAAqB,GAAG,MAAM,GAAG,oBAAoB,CAAC;AAClE,MAAM,MAAM,qBAAqB,GAAG,UAAU,GAAG,OAAO,CAAC;AACzD,MAAM,MAAM,8BAA8B,GAAG,OAAO,GAAG,iBAAiB,CAAC;AACzE,MAAM,MAAM,yBAAyB,GAAG,SAAS,GAAG,MAAM,GAAG,cAAc,CAAC;AAC5E,MAAM,MAAM,qBAAqB,GAAG,MAAM,GAAG,WAAW,GAAG,OAAO,CAAC;AACnE,MAAM,MAAM,iBAAiB,GAAG,qBAAqB,GAAG,iBAAiB,GAAG,WAAW,CAAC;AAExF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,qBAAqB,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,aAAa,GACrB,iBAAiB,GACjB;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,CAAC,CAAA;CAAE,GACpD;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,iBAAiB,CAAC;IAAC,IAAI,EAAE,CAAC,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC;AAE/E,MAAM,MAAM,mBAAmB,GAAG,CAAC,KAAK,EAAE,iBAAiB,KAAK,yBAAyB,CAAC;AAE1F,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,QAAQ,GAAG,YAAY,CAAC;IAChC,aAAa,EAAE,qBAAqB,CAAC;IACrC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,aAAa,CAAC,EAAE,qBAAqB,CAAC;IACtC,WAAW,CAAC,EAAE,mBAAmB,CAAC;IAClC,gBAAgB,CAAC,EAAE,8BAA8B,CAAC;CACnD,CAAC;AAEF,KAAK,kBAAkB,GAAG;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC9C,IAAI,CACF,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,oBAAoB,CAAC,CAAC;CAClC,CAAC;AAqBF,wBAAuB,QAAQ,CAC7B,IAAI,EAAE,kBAAkB,EACxB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,eAAe,GACvB,cAAc,CAAC,aAAa,CAAC,CAqK/B"}
|