@openclaw/fs-safe 0.5.1 → 0.5.2
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 +66 -0
- package/README.md +18 -6
- package/dist/absolute-path.d.ts.map +1 -1
- package/dist/absolute-path.js +20 -12
- package/dist/archive-entry.d.ts +1 -0
- package/dist/archive-entry.d.ts.map +1 -1
- package/dist/archive-entry.js +25 -5
- package/dist/archive-input.d.ts +7 -0
- package/dist/archive-input.d.ts.map +1 -1
- package/dist/archive-input.js +11 -1
- package/dist/archive-limits.js +1 -1
- package/dist/archive-native.d.ts.map +1 -1
- package/dist/archive-native.js +6 -3
- package/dist/archive-policy.d.ts.map +1 -1
- package/dist/archive-policy.js +2 -1
- package/dist/archive-read.d.ts.map +1 -1
- package/dist/archive-read.js +30 -12
- package/dist/archive-staging.d.ts.map +1 -1
- package/dist/archive-staging.js +3 -2
- package/dist/archive-tar-meta.d.ts.map +1 -1
- package/dist/archive-tar-meta.js +7 -1
- package/dist/archive-tar.d.ts.map +1 -1
- package/dist/archive-tar.js +7 -4
- package/dist/archive-zip-entry.d.ts +1 -0
- package/dist/archive-zip-entry.d.ts.map +1 -1
- package/dist/archive-zip-integrity.d.ts +4 -0
- package/dist/archive-zip-integrity.d.ts.map +1 -0
- package/dist/archive-zip-integrity.js +44 -0
- package/dist/archive-zip-preflight.d.ts.map +1 -1
- package/dist/archive-zip-preflight.js +6 -1
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +5 -2
- package/dist/bounded-read-stream.d.ts.map +1 -1
- package/dist/bounded-read-stream.js +13 -2
- package/dist/deny-mutations.d.ts +1 -0
- package/dist/deny-mutations.d.ts.map +1 -1
- package/dist/deny-mutations.js +6 -39
- package/dist/device-path.d.ts +1 -0
- package/dist/device-path.d.ts.map +1 -1
- package/dist/device-path.js +6 -6
- package/dist/directory-guard.d.ts.map +1 -1
- package/dist/directory-guard.js +5 -4
- package/dist/error-detail.d.ts +2 -0
- package/dist/error-detail.d.ts.map +1 -0
- package/dist/error-detail.js +4 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +3 -0
- package/dist/file-hash.d.ts.map +1 -1
- package/dist/file-hash.js +4 -1
- package/dist/file-lock-sync.d.ts.map +1 -1
- package/dist/file-lock-sync.js +88 -35
- package/dist/file-store-boundary.d.ts +6 -0
- package/dist/file-store-boundary.d.ts.map +1 -1
- package/dist/file-store-boundary.js +20 -5
- package/dist/file-store-sync-write.d.ts +9 -0
- package/dist/file-store-sync-write.d.ts.map +1 -0
- package/dist/file-store-sync-write.js +97 -0
- package/dist/file-store.d.ts.map +1 -1
- package/dist/file-store.js +36 -128
- package/dist/filename.d.ts.map +1 -1
- package/dist/filename.js +27 -0
- package/dist/guarded-mkdir.d.ts.map +1 -1
- package/dist/guarded-mkdir.js +5 -6
- package/dist/install-path.js +1 -1
- package/dist/json-document-store.d.ts.map +1 -1
- package/dist/json-document-store.js +19 -13
- package/dist/json.d.ts.map +1 -1
- package/dist/json.js +1 -3
- package/dist/local-file-access.d.ts +3 -2
- package/dist/local-file-access.d.ts.map +1 -1
- package/dist/local-file-access.js +12 -6
- package/dist/local-roots.d.ts.map +1 -1
- package/dist/local-roots.js +35 -30
- package/dist/move-path.d.ts.map +1 -1
- package/dist/move-path.js +51 -12
- package/dist/native/darwin-arm64/fs-safe-native.node +0 -0
- package/dist/native/darwin-x64/fs-safe-native.node +0 -0
- package/dist/native/linux-arm64-gnu/fs-safe-native.node +0 -0
- package/dist/native/linux-arm64-musl/fs-safe-native.node +0 -0
- package/dist/native/linux-x64-gnu/fs-safe-native.node +0 -0
- package/dist/native/linux-x64-musl/fs-safe-native.node +0 -0
- package/dist/native/win32-x64-msvc/fs-safe-native.node +0 -0
- package/dist/native-binding.d.ts +1 -0
- package/dist/native-binding.d.ts.map +1 -1
- package/dist/native-operations.d.ts.map +1 -1
- package/dist/native-operations.js +14 -1
- package/dist/native-pinned-write.d.ts.map +1 -1
- package/dist/native-pinned-write.js +44 -17
- package/dist/output-sibling.d.ts.map +1 -1
- package/dist/output-sibling.js +6 -5
- package/dist/path-policy.d.ts.map +1 -1
- package/dist/path-policy.js +1 -7
- package/dist/path.d.ts.map +1 -1
- package/dist/path.js +4 -0
- package/dist/permission-exec.d.ts +6 -0
- package/dist/permission-exec.d.ts.map +1 -0
- package/dist/permission-exec.js +28 -0
- package/dist/permissions-windows.d.ts +56 -0
- package/dist/permissions-windows.d.ts.map +1 -0
- package/dist/permissions-windows.js +285 -0
- package/dist/permissions.d.ts +3 -46
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +17 -328
- package/dist/pinned-write.js +2 -1
- package/dist/regular-file.d.ts.map +1 -1
- package/dist/regular-file.js +21 -2
- package/dist/replace-directory.d.ts.map +1 -1
- package/dist/replace-directory.js +10 -1
- package/dist/replace-file-copy-fallback.d.ts +3 -0
- package/dist/replace-file-copy-fallback.d.ts.map +1 -1
- package/dist/replace-file-copy-fallback.js +18 -11
- package/dist/replace-file-descriptor.d.ts +33 -0
- package/dist/replace-file-descriptor.d.ts.map +1 -0
- package/dist/replace-file-descriptor.js +96 -0
- package/dist/replace-file.d.ts +9 -2
- package/dist/replace-file.d.ts.map +1 -1
- package/dist/replace-file.js +64 -72
- package/dist/root-context.d.ts +8 -0
- package/dist/root-context.d.ts.map +1 -1
- package/dist/root-context.js +43 -2
- package/dist/root-errors.d.ts +7 -0
- package/dist/root-errors.d.ts.map +1 -1
- package/dist/root-errors.js +46 -1
- package/dist/root-file.d.ts +1 -0
- package/dist/root-file.d.ts.map +1 -1
- package/dist/root-file.js +2 -0
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +213 -168
- package/dist/root-path-symlink.d.ts +3 -0
- package/dist/root-path-symlink.d.ts.map +1 -0
- package/dist/root-path-symlink.js +35 -0
- package/dist/root-path.d.ts +1 -0
- package/dist/root-path.d.ts.map +1 -1
- package/dist/root-path.js +129 -190
- package/dist/root-walk.d.ts.map +1 -1
- package/dist/root-walk.js +15 -0
- package/dist/safe-path-segment.d.ts +2 -0
- package/dist/safe-path-segment.d.ts.map +1 -1
- package/dist/safe-path-segment.js +12 -0
- package/dist/secret-file.d.ts.map +1 -1
- package/dist/secret-file.js +19 -3
- package/dist/secure-temp-dir.d.ts.map +1 -1
- package/dist/secure-temp-dir.js +6 -1
- package/dist/short-path.d.ts +2 -0
- package/dist/short-path.d.ts.map +1 -0
- package/dist/short-path.js +7 -0
- package/dist/sidecar-lock-acquire.d.ts +30 -0
- package/dist/sidecar-lock-acquire.d.ts.map +1 -0
- package/dist/sidecar-lock-acquire.js +260 -0
- package/dist/sidecar-lock-policy.d.ts +3 -0
- package/dist/sidecar-lock-policy.d.ts.map +1 -1
- package/dist/sidecar-lock-policy.js +20 -3
- package/dist/sidecar-lock-reclaim.d.ts +5 -1
- package/dist/sidecar-lock-reclaim.d.ts.map +1 -1
- package/dist/sidecar-lock-reclaim.js +59 -9
- package/dist/sidecar-lock.d.ts.map +1 -1
- package/dist/sidecar-lock.js +27 -226
- package/dist/temp-target.d.ts.map +1 -1
- package/dist/temp-target.js +15 -4
- package/dist/test-hooks.d.ts +1 -0
- package/dist/test-hooks.d.ts.map +1 -1
- package/dist/timing.d.ts +2 -0
- package/dist/timing.d.ts.map +1 -1
- package/dist/timing.js +9 -0
- package/dist/walk.d.ts.map +1 -1
- package/dist/walk.js +15 -0
- package/docs/advanced.md +1 -1
- package/docs/archive.md +27 -20
- package/docs/assets/readme-banner.jpg +0 -0
- package/docs/atomic.md +48 -19
- package/docs/errors.md +31 -7
- package/docs/file-store.md +10 -1
- package/docs/filename.md +17 -16
- package/docs/index.md +1 -0
- package/docs/install-path.md +9 -6
- package/docs/json.md +8 -2
- package/docs/local-roots.md +79 -103
- package/docs/native-helper.md +11 -5
- package/docs/native.md +2 -2
- package/docs/output.md +8 -3
- package/docs/path-scope.md +4 -1
- package/docs/path.md +7 -3
- package/docs/permissions.md +8 -0
- package/docs/public-api.md +109 -0
- package/docs/quickstart.md +6 -2
- package/docs/reading.md +13 -10
- package/docs/root.md +26 -4
- package/docs/secret-file.md +17 -4
- package/docs/security-model.md +21 -4
- package/docs/sidecar-lock.md +15 -5
- package/docs/temp.md +37 -5
- package/docs/test-hooks.md +2 -0
- package/docs/types.md +32 -15
- package/docs/writing.md +25 -12
- package/package.json +7 -3
- package/dist/archive-file-io.d.ts +0 -9
- package/dist/archive-file-io.d.ts.map +0 -1
- package/dist/archive-file-io.js +0 -11
- package/dist/file-store-source.d.ts +0 -5
- package/dist/file-store-source.d.ts.map +0 -1
- package/dist/file-store-source.js +0 -30
- package/dist/file-url.d.ts +0 -2
- package/dist/file-url.d.ts.map +0 -1
- package/dist/file-url.js +0 -1
- package/dist/path-stat.d.ts +0 -4
- package/dist/path-stat.d.ts.map +0 -1
- package/dist/path-stat.js +0 -15
- package/dist/windows-permissions-native.d.ts +0 -9
- package/dist/windows-permissions-native.d.ts.map +0 -1
- package/dist/windows-permissions-native.js +0 -31
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root-path-symlink.d.ts","sourceRoot":"","sources":["../src/root-path-symlink.ts"],"names":[],"mappings":"AAmBA,wBAAsB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQhF;AAED,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAQrE"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import fsp from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { FsSafeError } from "./errors.js";
|
|
5
|
+
import { isNotFoundPathError, isSymlinkOpenError } from "./path.js";
|
|
6
|
+
import { resolvePathViaExistingAncestor, resolvePathViaExistingAncestorSync, } from "./root-path-existing.js";
|
|
7
|
+
function normalizeSymlinkResolutionError(error) {
|
|
8
|
+
if (isSymlinkOpenError(error)) {
|
|
9
|
+
throw new FsSafeError("symlink", "symlink path could not be resolved", {
|
|
10
|
+
cause: error instanceof Error ? error : undefined,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
if (!isNotFoundPathError(error))
|
|
14
|
+
throw error;
|
|
15
|
+
}
|
|
16
|
+
export async function resolveSymlinkHopPath(symlinkPath) {
|
|
17
|
+
try {
|
|
18
|
+
return path.resolve(await fsp.realpath(symlinkPath));
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
normalizeSymlinkResolutionError(error);
|
|
22
|
+
const linkTarget = await fsp.readlink(symlinkPath);
|
|
23
|
+
return resolvePathViaExistingAncestor(path.resolve(path.dirname(symlinkPath), linkTarget));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function resolveSymlinkHopPathSync(symlinkPath) {
|
|
27
|
+
try {
|
|
28
|
+
return path.resolve(fs.realpathSync(symlinkPath));
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
normalizeSymlinkResolutionError(error);
|
|
32
|
+
const linkTarget = fs.readlinkSync(symlinkPath);
|
|
33
|
+
return resolvePathViaExistingAncestorSync(path.resolve(path.dirname(symlinkPath), linkTarget));
|
|
34
|
+
}
|
|
35
|
+
}
|
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,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;AAsCD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,qBAAqB,GAAG,gBAAgB,CAMnF"}
|
package/dist/root-path.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import fsp from "node:fs/promises";
|
|
3
|
-
import os from "node:os";
|
|
4
3
|
import path from "node:path";
|
|
5
|
-
import {
|
|
4
|
+
import { formatErrorDetail } from "./error-detail.js";
|
|
5
|
+
import { FsSafeError } from "./errors.js";
|
|
6
|
+
import { assertNoNulPathInput, isNotFoundPathError, isPathInside, isPathRelativeEscape, } from "./path.js";
|
|
6
7
|
import { resolvePathViaExistingAncestor, resolvePathViaExistingAncestorSync, } from "./root-path-existing.js";
|
|
8
|
+
import { resolveSymlinkHopPath, resolveSymlinkHopPathSync } from "./root-path-symlink.js";
|
|
9
|
+
import { assertNoDriveRelativePathSegments } from "./safe-path-segment.js";
|
|
10
|
+
import { shortPath } from "./short-path.js";
|
|
7
11
|
export { resolvePathViaExistingAncestorSync } from "./root-path-existing.js";
|
|
8
12
|
export const ROOT_PATH_ALIAS_POLICIES = {
|
|
9
13
|
strict: Object.freeze({
|
|
@@ -16,6 +20,15 @@ export const ROOT_PATH_ALIAS_POLICIES = {
|
|
|
16
20
|
}),
|
|
17
21
|
};
|
|
18
22
|
export async function resolveRootPath(params) {
|
|
23
|
+
try {
|
|
24
|
+
return await resolveRootPathInternal(params);
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
throw sanitizeRootPathError(error);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function resolveRootPathInternal(params) {
|
|
31
|
+
assertValidRootPathInputs(params);
|
|
19
32
|
const rootPath = path.resolve(params.rootPath);
|
|
20
33
|
const absolutePath = path.resolve(params.absolutePath);
|
|
21
34
|
const rootCanonicalPath = params.rootCanonicalPath
|
|
@@ -46,6 +59,15 @@ export async function resolveRootPath(params) {
|
|
|
46
59
|
});
|
|
47
60
|
}
|
|
48
61
|
export function resolveRootPathSync(params) {
|
|
62
|
+
try {
|
|
63
|
+
return resolveRootPathSyncInternal(params);
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
throw sanitizeRootPathError(error);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function resolveRootPathSyncInternal(params) {
|
|
70
|
+
assertValidRootPathInputs(params);
|
|
49
71
|
const rootPath = path.resolve(params.rootPath);
|
|
50
72
|
const absolutePath = path.resolve(params.absolutePath);
|
|
51
73
|
const rootCanonicalPath = params.rootCanonicalPath
|
|
@@ -75,6 +97,29 @@ export function resolveRootPathSync(params) {
|
|
|
75
97
|
rootCanonicalPath: context.rootCanonicalPath,
|
|
76
98
|
});
|
|
77
99
|
}
|
|
100
|
+
function sanitizeRootPathError(error) {
|
|
101
|
+
if (error instanceof Error) {
|
|
102
|
+
error.message = formatErrorDetail(error.message);
|
|
103
|
+
}
|
|
104
|
+
return error;
|
|
105
|
+
}
|
|
106
|
+
function assertValidRootPathInputs(params) {
|
|
107
|
+
assertNoNulPathInput(params.rootPath, "root path contains a NUL byte");
|
|
108
|
+
assertNoNulPathInput(params.absolutePath, "absolute path contains a NUL byte");
|
|
109
|
+
assertNoEmbeddedDriveRelativeSegment(params.rootPath, "root path");
|
|
110
|
+
assertNoEmbeddedDriveRelativeSegment(params.absolutePath, "absolute path");
|
|
111
|
+
if (params.rootCanonicalPath !== undefined) {
|
|
112
|
+
assertNoNulPathInput(params.rootCanonicalPath, "canonical root path contains a NUL byte");
|
|
113
|
+
assertNoEmbeddedDriveRelativeSegment(params.rootCanonicalPath, "canonical root path");
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function assertNoEmbeddedDriveRelativeSegment(filePath, label) {
|
|
117
|
+
if (process.platform !== "win32") {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const root = path.parse(filePath).root;
|
|
121
|
+
assertNoDriveRelativePathSegments(filePath.slice(root.length).replaceAll("\\", "/"), label);
|
|
122
|
+
}
|
|
78
123
|
function isPromiseLike(value) {
|
|
79
124
|
return Boolean(value &&
|
|
80
125
|
(typeof value === "object" || typeof value === "function") &&
|
|
@@ -93,6 +138,15 @@ function createLexicalTraversalState(params) {
|
|
|
93
138
|
preserveFinalSymlink: false,
|
|
94
139
|
};
|
|
95
140
|
}
|
|
141
|
+
function createLexicalTraversalContext(params) {
|
|
142
|
+
return {
|
|
143
|
+
state: createLexicalTraversalState(params),
|
|
144
|
+
resolveParams: params.params,
|
|
145
|
+
rootPath: params.rootPath,
|
|
146
|
+
rootCanonicalPath: params.rootCanonicalPath,
|
|
147
|
+
absolutePath: params.absolutePath,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
96
150
|
function splitTraversalSegments(value) {
|
|
97
151
|
return value
|
|
98
152
|
.split(process.platform === "win32" ? /[\\/]+/ : /\/+/)
|
|
@@ -116,149 +170,97 @@ function rawPathRelativeToRoot(rootPath, candidatePath) {
|
|
|
116
170
|
: candidatePrefix === rootWithSep;
|
|
117
171
|
return prefixMatches ? candidate.slice(rootWithSep.length) : undefined;
|
|
118
172
|
}
|
|
119
|
-
function assertLexicalCursorInsideBoundary(
|
|
173
|
+
function assertLexicalCursorInsideBoundary(context, candidatePath) {
|
|
120
174
|
assertInsideBoundary({
|
|
121
|
-
boundaryLabel:
|
|
122
|
-
rootCanonicalPath:
|
|
123
|
-
candidatePath
|
|
124
|
-
absolutePath:
|
|
175
|
+
boundaryLabel: context.resolveParams.boundaryLabel,
|
|
176
|
+
rootCanonicalPath: context.rootCanonicalPath,
|
|
177
|
+
candidatePath,
|
|
178
|
+
absolutePath: context.absolutePath,
|
|
125
179
|
});
|
|
126
180
|
}
|
|
127
|
-
function applyMissingSuffixToCanonicalCursor(
|
|
128
|
-
const missingSuffix =
|
|
181
|
+
function applyMissingSuffixToCanonicalCursor(context, missingFromIndex) {
|
|
182
|
+
const missingSuffix = context.state.segments.slice(missingFromIndex);
|
|
129
183
|
for (const segment of missingSuffix) {
|
|
130
|
-
advanceCanonicalCursorForSegment(
|
|
131
|
-
state: params.state,
|
|
132
|
-
segment,
|
|
133
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
134
|
-
params: params.params,
|
|
135
|
-
absolutePath: params.absolutePath,
|
|
136
|
-
});
|
|
184
|
+
advanceCanonicalCursorForSegment(context, segment);
|
|
137
185
|
}
|
|
138
186
|
}
|
|
139
|
-
function advanceCanonicalCursorForSegment(
|
|
140
|
-
|
|
141
|
-
assertLexicalCursorInsideBoundary(
|
|
142
|
-
params: params.params,
|
|
143
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
144
|
-
candidatePath: params.state.canonicalCursor,
|
|
145
|
-
absolutePath: params.absolutePath,
|
|
146
|
-
});
|
|
187
|
+
function advanceCanonicalCursorForSegment(context, segment) {
|
|
188
|
+
context.state.canonicalCursor = path.resolve(context.state.canonicalCursor, segment);
|
|
189
|
+
assertLexicalCursorInsideBoundary(context, context.state.canonicalCursor);
|
|
147
190
|
}
|
|
148
|
-
function finalizeLexicalResolution(
|
|
149
|
-
assertLexicalCursorInsideBoundary(
|
|
150
|
-
params: params.params,
|
|
151
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
152
|
-
candidatePath: params.state.canonicalCursor,
|
|
153
|
-
absolutePath: params.absolutePath,
|
|
154
|
-
});
|
|
191
|
+
function finalizeLexicalResolution(context, kind) {
|
|
192
|
+
assertLexicalCursorInsideBoundary(context, context.state.canonicalCursor);
|
|
155
193
|
return buildResolvedRootPath({
|
|
156
|
-
absolutePath:
|
|
157
|
-
canonicalPath:
|
|
158
|
-
rootPath:
|
|
159
|
-
rootCanonicalPath:
|
|
160
|
-
kind
|
|
194
|
+
absolutePath: context.absolutePath,
|
|
195
|
+
canonicalPath: context.state.canonicalCursor,
|
|
196
|
+
rootPath: context.rootPath,
|
|
197
|
+
rootCanonicalPath: context.rootCanonicalPath,
|
|
198
|
+
kind,
|
|
161
199
|
});
|
|
162
200
|
}
|
|
163
|
-
function handleLexicalLstatFailure(
|
|
164
|
-
if (!isNotFoundPathError(
|
|
201
|
+
function handleLexicalLstatFailure(context, error, missingFromIndex) {
|
|
202
|
+
if (!isNotFoundPathError(error)) {
|
|
165
203
|
return false;
|
|
166
204
|
}
|
|
167
|
-
applyMissingSuffixToCanonicalCursor(
|
|
168
|
-
state: params.state,
|
|
169
|
-
missingFromIndex: params.missingFromIndex,
|
|
170
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
171
|
-
params: params.resolveParams,
|
|
172
|
-
absolutePath: params.absolutePath,
|
|
173
|
-
});
|
|
205
|
+
applyMissingSuffixToCanonicalCursor(context, missingFromIndex);
|
|
174
206
|
return true;
|
|
175
207
|
}
|
|
176
|
-
function handleLexicalStatReadFailure(
|
|
177
|
-
if (handleLexicalLstatFailure({
|
|
178
|
-
error: params.error,
|
|
179
|
-
state: params.state,
|
|
180
|
-
missingFromIndex: params.missingFromIndex,
|
|
181
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
182
|
-
resolveParams: params.resolveParams,
|
|
183
|
-
absolutePath: params.absolutePath,
|
|
184
|
-
})) {
|
|
208
|
+
function handleLexicalStatReadFailure(context, error, missingFromIndex) {
|
|
209
|
+
if (handleLexicalLstatFailure(context, error, missingFromIndex)) {
|
|
185
210
|
return null;
|
|
186
211
|
}
|
|
187
|
-
throw
|
|
212
|
+
throw error;
|
|
188
213
|
}
|
|
189
|
-
function handleLexicalStatDisposition(params) {
|
|
214
|
+
function handleLexicalStatDisposition(context, params) {
|
|
190
215
|
if (!params.isSymbolicLink) {
|
|
191
|
-
advanceCanonicalCursorForSegment(
|
|
192
|
-
state: params.state,
|
|
193
|
-
segment: params.segment,
|
|
194
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
195
|
-
params: params.resolveParams,
|
|
196
|
-
absolutePath: params.absolutePath,
|
|
197
|
-
});
|
|
216
|
+
advanceCanonicalCursorForSegment(context, params.segment);
|
|
198
217
|
return "continue";
|
|
199
218
|
}
|
|
200
|
-
if (
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
params: params.resolveParams,
|
|
207
|
-
absolutePath: params.absolutePath,
|
|
208
|
-
});
|
|
219
|
+
if (context.resolveParams.rejectSymlinks === true && params.isLast) {
|
|
220
|
+
throw new FsSafeError("symlink", "symlink path component not allowed");
|
|
221
|
+
}
|
|
222
|
+
if (context.state.allowFinalSymlink && params.isLast) {
|
|
223
|
+
context.state.preserveFinalSymlink = true;
|
|
224
|
+
advanceCanonicalCursorForSegment(context, params.segment);
|
|
209
225
|
return "break";
|
|
210
226
|
}
|
|
211
227
|
return "resolve-link";
|
|
212
228
|
}
|
|
213
|
-
function applyResolvedSymlinkHop(
|
|
214
|
-
if (!isPathInside(
|
|
229
|
+
function applyResolvedSymlinkHop(context, linkCanonical) {
|
|
230
|
+
if (!isPathInside(context.rootCanonicalPath, linkCanonical)) {
|
|
215
231
|
throw symlinkEscapeError({
|
|
216
|
-
boundaryLabel:
|
|
217
|
-
rootCanonicalPath:
|
|
218
|
-
symlinkPath:
|
|
232
|
+
boundaryLabel: context.resolveParams.boundaryLabel,
|
|
233
|
+
rootCanonicalPath: context.rootCanonicalPath,
|
|
234
|
+
symlinkPath: context.state.lexicalCursor,
|
|
219
235
|
});
|
|
220
236
|
}
|
|
221
|
-
|
|
222
|
-
|
|
237
|
+
context.state.canonicalCursor = linkCanonical;
|
|
238
|
+
context.state.lexicalCursor = linkCanonical;
|
|
223
239
|
}
|
|
224
|
-
function readLexicalStat(params) {
|
|
240
|
+
function readLexicalStat(context, params) {
|
|
225
241
|
try {
|
|
226
|
-
const stat = params.read(
|
|
242
|
+
const stat = params.read(context.state.lexicalCursor);
|
|
227
243
|
if (isPromiseLike(stat)) {
|
|
228
|
-
return Promise.resolve(stat).catch((error) => handleLexicalStatReadFailure(
|
|
244
|
+
return Promise.resolve(stat).catch((error) => handleLexicalStatReadFailure(context, error, params.missingFromIndex));
|
|
229
245
|
}
|
|
230
246
|
return stat;
|
|
231
247
|
}
|
|
232
248
|
catch (error) {
|
|
233
|
-
return handleLexicalStatReadFailure(
|
|
249
|
+
return handleLexicalStatReadFailure(context, error, params.missingFromIndex);
|
|
234
250
|
}
|
|
235
251
|
}
|
|
236
|
-
function resolveAndApplySymlinkHop(params) {
|
|
237
|
-
const linkCanonical = params.resolveLinkCanonical(
|
|
252
|
+
function resolveAndApplySymlinkHop(context, params) {
|
|
253
|
+
const linkCanonical = params.resolveLinkCanonical(context.state.lexicalCursor);
|
|
238
254
|
if (isPromiseLike(linkCanonical)) {
|
|
239
|
-
return Promise.resolve(linkCanonical).then((value) =>
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}));
|
|
245
|
-
}
|
|
246
|
-
applyResolvedSymlinkHop({
|
|
247
|
-
state: params.state,
|
|
248
|
-
linkCanonical,
|
|
249
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
250
|
-
boundaryLabel: params.boundaryLabel,
|
|
251
|
-
});
|
|
255
|
+
return Promise.resolve(linkCanonical).then((value) => {
|
|
256
|
+
applyResolvedSymlinkHop(context, value);
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
applyResolvedSymlinkHop(context, linkCanonical);
|
|
252
260
|
}
|
|
253
|
-
function applyParentTraversalStep(
|
|
254
|
-
|
|
255
|
-
advanceCanonicalCursorForSegment(
|
|
256
|
-
state: params.state,
|
|
257
|
-
segment: "..",
|
|
258
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
259
|
-
params: params.resolveParams,
|
|
260
|
-
absolutePath: params.absolutePath,
|
|
261
|
-
});
|
|
261
|
+
function applyParentTraversalStep(context) {
|
|
262
|
+
context.state.lexicalCursor = path.resolve(context.state.lexicalCursor, "..");
|
|
263
|
+
advanceCanonicalCursorForSegment(context, "..");
|
|
262
264
|
}
|
|
263
265
|
function* iterateLexicalTraversal(state) {
|
|
264
266
|
for (let idx = 0; idx < state.segments.length; idx += 1) {
|
|
@@ -268,32 +270,22 @@ function* iterateLexicalTraversal(state) {
|
|
|
268
270
|
}
|
|
269
271
|
}
|
|
270
272
|
async function resolveRootPathLexicalAsync(params) {
|
|
271
|
-
const
|
|
272
|
-
const
|
|
273
|
-
state,
|
|
274
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
275
|
-
resolveParams: params.params,
|
|
276
|
-
absolutePath: params.absolutePath,
|
|
277
|
-
};
|
|
273
|
+
const context = createLexicalTraversalContext(params);
|
|
274
|
+
const { state } = context;
|
|
278
275
|
for (const { idx, segment, isLast } of iterateLexicalTraversal(state)) {
|
|
279
276
|
if (segment === "..") {
|
|
280
|
-
applyParentTraversalStep(
|
|
281
|
-
...sharedStepParams,
|
|
282
|
-
resolveParams: params.params,
|
|
283
|
-
});
|
|
277
|
+
applyParentTraversalStep(context);
|
|
284
278
|
continue;
|
|
285
279
|
}
|
|
286
280
|
state.lexicalCursor = path.join(state.lexicalCursor, segment);
|
|
287
|
-
const stat = await readLexicalStat({
|
|
288
|
-
...sharedStepParams,
|
|
281
|
+
const stat = await readLexicalStat(context, {
|
|
289
282
|
missingFromIndex: idx,
|
|
290
283
|
read: (cursor) => fsp.lstat(cursor),
|
|
291
284
|
});
|
|
292
285
|
if (!stat) {
|
|
293
286
|
break;
|
|
294
287
|
}
|
|
295
|
-
const disposition = handleLexicalStatDisposition({
|
|
296
|
-
...sharedStepParams,
|
|
288
|
+
const disposition = handleLexicalStatDisposition(context, {
|
|
297
289
|
isSymbolicLink: stat.isSymbolicLink(),
|
|
298
290
|
segment,
|
|
299
291
|
isLast,
|
|
@@ -304,41 +296,29 @@ async function resolveRootPathLexicalAsync(params) {
|
|
|
304
296
|
if (disposition === "break") {
|
|
305
297
|
break;
|
|
306
298
|
}
|
|
307
|
-
await resolveAndApplySymlinkHop({
|
|
308
|
-
state,
|
|
309
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
310
|
-
boundaryLabel: params.params.boundaryLabel,
|
|
299
|
+
await resolveAndApplySymlinkHop(context, {
|
|
311
300
|
resolveLinkCanonical: (cursor) => resolveSymlinkHopPath(cursor),
|
|
312
301
|
});
|
|
302
|
+
if (context.resolveParams.rejectSymlinks === true) {
|
|
303
|
+
throw new FsSafeError("symlink", "symlink path component not allowed");
|
|
304
|
+
}
|
|
313
305
|
}
|
|
314
306
|
const kind = await getPathKind(state.canonicalCursor, state.preserveFinalSymlink);
|
|
315
|
-
return finalizeLexicalResolution(
|
|
316
|
-
...params,
|
|
317
|
-
state,
|
|
318
|
-
kind,
|
|
319
|
-
});
|
|
307
|
+
return finalizeLexicalResolution(context, kind);
|
|
320
308
|
}
|
|
321
309
|
function resolveRootPathLexicalSync(params) {
|
|
322
|
-
const
|
|
310
|
+
const context = createLexicalTraversalContext(params);
|
|
311
|
+
const { state } = context;
|
|
323
312
|
for (let idx = 0; idx < state.segments.length; idx += 1) {
|
|
324
313
|
const segment = state.segments[idx] ?? "";
|
|
325
314
|
const isLast = idx === state.segments.length - 1;
|
|
326
315
|
if (segment === "..") {
|
|
327
|
-
applyParentTraversalStep(
|
|
328
|
-
state,
|
|
329
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
330
|
-
resolveParams: params.params,
|
|
331
|
-
absolutePath: params.absolutePath,
|
|
332
|
-
});
|
|
316
|
+
applyParentTraversalStep(context);
|
|
333
317
|
continue;
|
|
334
318
|
}
|
|
335
319
|
state.lexicalCursor = path.join(state.lexicalCursor, segment);
|
|
336
|
-
const maybeStat = readLexicalStat({
|
|
337
|
-
state,
|
|
320
|
+
const maybeStat = readLexicalStat(context, {
|
|
338
321
|
missingFromIndex: idx,
|
|
339
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
340
|
-
resolveParams: params.params,
|
|
341
|
-
absolutePath: params.absolutePath,
|
|
342
322
|
read: (cursor) => fs.lstatSync(cursor),
|
|
343
323
|
});
|
|
344
324
|
if (isPromiseLike(maybeStat)) {
|
|
@@ -348,14 +328,10 @@ function resolveRootPathLexicalSync(params) {
|
|
|
348
328
|
if (!stat) {
|
|
349
329
|
break;
|
|
350
330
|
}
|
|
351
|
-
const disposition = handleLexicalStatDisposition({
|
|
352
|
-
state,
|
|
331
|
+
const disposition = handleLexicalStatDisposition(context, {
|
|
353
332
|
isSymbolicLink: stat.isSymbolicLink(),
|
|
354
333
|
segment,
|
|
355
334
|
isLast,
|
|
356
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
357
|
-
resolveParams: params.params,
|
|
358
|
-
absolutePath: params.absolutePath,
|
|
359
335
|
});
|
|
360
336
|
if (disposition === "continue") {
|
|
361
337
|
continue;
|
|
@@ -363,22 +339,18 @@ function resolveRootPathLexicalSync(params) {
|
|
|
363
339
|
if (disposition === "break") {
|
|
364
340
|
break;
|
|
365
341
|
}
|
|
366
|
-
const maybeApplied = resolveAndApplySymlinkHop({
|
|
367
|
-
state,
|
|
368
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
369
|
-
boundaryLabel: params.params.boundaryLabel,
|
|
342
|
+
const maybeApplied = resolveAndApplySymlinkHop(context, {
|
|
370
343
|
resolveLinkCanonical: (cursor) => resolveSymlinkHopPathSync(cursor),
|
|
371
344
|
});
|
|
372
345
|
if (isPromiseLike(maybeApplied)) {
|
|
373
346
|
throw new Error("Unexpected async symlink resolution");
|
|
374
347
|
}
|
|
348
|
+
if (context.resolveParams.rejectSymlinks === true) {
|
|
349
|
+
throw new FsSafeError("symlink", "symlink path component not allowed");
|
|
350
|
+
}
|
|
375
351
|
}
|
|
376
352
|
const kind = getPathKindSync(state.canonicalCursor, state.preserveFinalSymlink);
|
|
377
|
-
return finalizeLexicalResolution(
|
|
378
|
-
...params,
|
|
379
|
-
state,
|
|
380
|
-
kind,
|
|
381
|
-
});
|
|
353
|
+
return finalizeLexicalResolution(context, kind);
|
|
382
354
|
}
|
|
383
355
|
function resolveCanonicalOutsideLexicalPath(params) {
|
|
384
356
|
return params.outsideLexicalCanonicalPath ?? params.absolutePath;
|
|
@@ -549,36 +521,3 @@ function pathEscapeError(params) {
|
|
|
549
521
|
function symlinkEscapeError(params) {
|
|
550
522
|
return new Error(`Symlink escapes ${params.boundaryLabel} (${shortPath(params.rootCanonicalPath)}): ${shortPath(params.symlinkPath)}`);
|
|
551
523
|
}
|
|
552
|
-
function shortPath(value) {
|
|
553
|
-
const home = os.homedir();
|
|
554
|
-
if (value.startsWith(home)) {
|
|
555
|
-
return `~${value.slice(home.length)}`;
|
|
556
|
-
}
|
|
557
|
-
return value;
|
|
558
|
-
}
|
|
559
|
-
async function resolveSymlinkHopPath(symlinkPath) {
|
|
560
|
-
try {
|
|
561
|
-
return path.resolve(await fsp.realpath(symlinkPath));
|
|
562
|
-
}
|
|
563
|
-
catch (error) {
|
|
564
|
-
if (!isNotFoundPathError(error)) {
|
|
565
|
-
throw error;
|
|
566
|
-
}
|
|
567
|
-
const linkTarget = await fsp.readlink(symlinkPath);
|
|
568
|
-
const linkAbsolute = path.resolve(path.dirname(symlinkPath), linkTarget);
|
|
569
|
-
return resolvePathViaExistingAncestor(linkAbsolute);
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
function resolveSymlinkHopPathSync(symlinkPath) {
|
|
573
|
-
try {
|
|
574
|
-
return path.resolve(fs.realpathSync(symlinkPath));
|
|
575
|
-
}
|
|
576
|
-
catch (error) {
|
|
577
|
-
if (!isNotFoundPathError(error)) {
|
|
578
|
-
throw error;
|
|
579
|
-
}
|
|
580
|
-
const linkTarget = fs.readlinkSync(symlinkPath);
|
|
581
|
-
const linkAbsolute = path.resolve(path.dirname(symlinkPath), linkTarget);
|
|
582
|
-
return resolvePathViaExistingAncestorSync(linkAbsolute);
|
|
583
|
-
}
|
|
584
|
-
}
|
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;AAE3C,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,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,EAAE,OAAO,EAAE;QAAE,aAAa,EAAE,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;CACnF,CAAC;AAqBF,wBAAuB,QAAQ,CAC7B,IAAI,EAAE,kBAAkB,EACxB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,eAAe,GACvB,cAAc,CAAC,aAAa,CAAC,
|
|
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;AAE3C,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,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,EAAE,OAAO,EAAE;QAAE,aAAa,EAAE,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;CACnF,CAAC;AAqBF,wBAAuB,QAAQ,CAC7B,IAAI,EAAE,kBAAkB,EACxB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,eAAe,GACvB,cAAc,CAAC,aAAa,CAAC,CAsH/B"}
|
package/dist/root-walk.js
CHANGED
|
@@ -22,6 +22,17 @@ function limitEntry(relativePath) {
|
|
|
22
22
|
return { relativePath, kind: "truncated", size: 0 };
|
|
23
23
|
}
|
|
24
24
|
export async function* walkRoot(root, relativePath, options) {
|
|
25
|
+
if (!["skip", "follow-within-root"].includes(options.symlinkPolicy)) {
|
|
26
|
+
throw new TypeError(`invalid root walk symlink policy: ${String(options.symlinkPolicy)}`);
|
|
27
|
+
}
|
|
28
|
+
if (options.limitBehavior !== undefined &&
|
|
29
|
+
!["truncate", "throw"].includes(options.limitBehavior)) {
|
|
30
|
+
throw new TypeError(`invalid root walk limit behavior: ${String(options.limitBehavior)}`);
|
|
31
|
+
}
|
|
32
|
+
if (options.onDirectoryError !== undefined &&
|
|
33
|
+
!["throw", "skip-and-report"].includes(options.onDirectoryError)) {
|
|
34
|
+
throw new TypeError(`invalid root walk directory error behavior: ${String(options.onDirectoryError)}`);
|
|
35
|
+
}
|
|
25
36
|
const maxDepth = validateBudget("maxDepth", options.maxDepth);
|
|
26
37
|
const maxEntries = validateBudget("maxEntries", options.maxEntries);
|
|
27
38
|
const visitedDirectories = new Set();
|
|
@@ -49,6 +60,7 @@ export async function* walkRoot(root, relativePath, options) {
|
|
|
49
60
|
return;
|
|
50
61
|
}
|
|
51
62
|
visitedDirectories.add(resolvedDirectory.canonicalPath);
|
|
63
|
+
options.signal?.throwIfAborted();
|
|
52
64
|
const listingDirectory = path
|
|
53
65
|
.relative(root.rootReal, resolvedDirectory.canonicalPath)
|
|
54
66
|
.split(path.sep)
|
|
@@ -56,11 +68,14 @@ export async function* walkRoot(root, relativePath, options) {
|
|
|
56
68
|
entries = await root.list(listingDirectory, { withFileTypes: true });
|
|
57
69
|
}
|
|
58
70
|
catch (error) {
|
|
71
|
+
// Cancellation is never a recoverable directory read failure.
|
|
72
|
+
options.signal?.throwIfAborted();
|
|
59
73
|
if ((options.onDirectoryError ?? "throw") === "throw")
|
|
60
74
|
throw error;
|
|
61
75
|
yield { relativePath: directory, kind: "directory-error", size: 0, error };
|
|
62
76
|
return;
|
|
63
77
|
}
|
|
78
|
+
options.signal?.throwIfAborted();
|
|
64
79
|
for (const entry of entries) {
|
|
65
80
|
options.signal?.throwIfAborted();
|
|
66
81
|
const child = directory
|
|
@@ -2,6 +2,8 @@ export type SafePathSegmentOptions = {
|
|
|
2
2
|
allowDotPrefix?: boolean;
|
|
3
3
|
label?: string;
|
|
4
4
|
};
|
|
5
|
+
export declare function isDriveRelativePath(value: string): boolean;
|
|
6
|
+
export declare function assertNoDriveRelativePathSegments(value: string, label: string): string;
|
|
5
7
|
export declare function isSafePathSegment(segment: string, options?: SafePathSegmentOptions): boolean;
|
|
6
8
|
export declare function assertSafePathSegment(segment: string, options?: SafePathSegmentOptions): string;
|
|
7
9
|
export declare function sanitizeSafePathSegment(value: string, fallback: string, options?: SafePathSegmentOptions): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"safe-path-segment.d.ts","sourceRoot":"","sources":["../src/safe-path-segment.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"safe-path-segment.d.ts","sourceRoot":"","sources":["../src/safe-path-segment.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,sBAAsB,GAAG;IACnC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAKtF;AAcD,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAaT;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,sBAA2B,GACnC,MAAM,CAUR;AAED,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,sBAA2B,GACnC,MAAM,CAWR;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,sBAA2B,GACnC,MAAM,CAeR"}
|
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import { FsSafeError } from "./errors.js";
|
|
2
2
|
const SAFE_PATH_SEGMENT_PATTERN = /^[A-Za-z0-9_-][A-Za-z0-9._-]*$/;
|
|
3
3
|
const SAFE_DOT_PREFIX_PATH_SEGMENT_PATTERN = /^[A-Za-z0-9._-]+$/;
|
|
4
|
+
// Windows treats "C:name" as relative to the drive's current directory even
|
|
5
|
+
// though path.win32.isAbsolute() reports false.
|
|
6
|
+
const DRIVE_RELATIVE_PREFIX = /^[A-Za-z]:(?![\\/])/;
|
|
4
7
|
const HYPHEN_CHAR_CODE = 0x2d;
|
|
8
|
+
export function isDriveRelativePath(value) {
|
|
9
|
+
return DRIVE_RELATIVE_PREFIX.test(value);
|
|
10
|
+
}
|
|
11
|
+
export function assertNoDriveRelativePathSegments(value, label) {
|
|
12
|
+
if (value.split("/").some(isDriveRelativePath)) {
|
|
13
|
+
throw new FsSafeError("invalid-path", `${label} must not contain a drive letter`);
|
|
14
|
+
}
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
5
17
|
function trimHyphenEdges(value) {
|
|
6
18
|
let start = 0;
|
|
7
19
|
let end = value.length;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"secret-file.d.ts","sourceRoot":"","sources":["../src/secret-file.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"secret-file.d.ts","sourceRoot":"","sources":["../src/secret-file.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,6BAA6B,QAAY,CAAC;AACvD,eAAO,MAAM,uBAAuB,MAAQ,CAAC;AAC7C,eAAO,MAAM,wBAAwB,MAAQ,CAAC;AAE9C,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC;AAoIF,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,qBAA0B,GAClC,MAAM,CAQR;AAED,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,qBAA0B,GAClC,MAAM,GAAG,SAAS,CAcpB;AAoID,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;CAClB,CAAC;AAkEF,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"}
|