@openclaw/fs-safe 0.8.3 → 0.8.5
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 +47 -1
- package/dist/absolute-path.d.ts.map +1 -1
- package/dist/absolute-path.js +8 -7
- package/dist/archive-native.d.ts +1 -0
- package/dist/archive-native.d.ts.map +1 -1
- package/dist/archive-native.js +15 -68
- package/dist/archive-plan.d.ts +20 -0
- package/dist/archive-plan.d.ts.map +1 -0
- package/dist/archive-plan.js +55 -0
- package/dist/archive-tar-inspect.d.ts +7 -0
- package/dist/archive-tar-inspect.d.ts.map +1 -0
- package/dist/archive-tar-inspect.js +59 -0
- package/dist/archive-tar.d.ts +3 -1
- package/dist/archive-tar.d.ts.map +1 -1
- package/dist/archive-tar.js +12 -49
- package/dist/archive.d.ts +1 -0
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +20 -36
- package/dist/bounded-read.d.ts.map +1 -1
- package/dist/bounded-read.js +31 -4
- package/dist/directory-durability.js +5 -5
- package/dist/directory-guard.d.ts.map +1 -1
- package/dist/directory-guard.js +5 -6
- package/dist/file-store-boundary.js +1 -1
- package/dist/file-store-prune.js +17 -8
- package/dist/file-store.js +2 -2
- package/dist/guarded-mkdir.d.ts.map +1 -1
- package/dist/guarded-mkdir.js +5 -4
- package/dist/move-path.js +1 -1
- package/dist/native-binding.d.ts +2 -1
- package/dist/native-binding.d.ts.map +1 -1
- package/dist/native-pinned-write-windows.js +6 -3
- package/dist/native-pinned-write.js +3 -3
- package/dist/native-staged-file.d.ts +2 -2
- package/dist/native-staged-file.d.ts.map +1 -1
- package/dist/native-staged-file.js +12 -8
- package/dist/opened-realpath.d.ts.map +1 -1
- package/dist/opened-realpath.js +10 -9
- package/dist/path-policy.js +2 -2
- package/dist/pinned-write.d.ts +1 -0
- package/dist/pinned-write.d.ts.map +1 -1
- package/dist/pinned-write.js +15 -11
- package/dist/regular-file.js +8 -8
- package/dist/replace-file-descriptor.d.ts.map +1 -1
- package/dist/replace-file-descriptor.js +8 -4
- package/dist/replace-file-temp-owner.d.ts.map +1 -1
- package/dist/replace-file-temp-owner.js +13 -7
- package/dist/root-context.js +5 -5
- package/dist/root-impl.d.ts +2 -1
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +35 -26
- package/dist/root-path-existing.d.ts.map +1 -1
- package/dist/root-path-existing.js +2 -3
- package/dist/root-path-symlink.d.ts.map +1 -1
- package/dist/root-path-symlink.js +2 -3
- package/dist/root-path.d.ts.map +1 -1
- package/dist/root-path.js +3 -4
- package/dist/root-paths.d.ts.map +1 -1
- package/dist/root-paths.js +16 -15
- package/dist/root-write-mode.d.ts.map +1 -1
- package/dist/root-write-mode.js +5 -4
- package/dist/root-write-verification.js +6 -6
- package/dist/secret-file.js +2 -2
- package/dist/strict-file-identity.d.ts +1 -1
- package/dist/strict-file-identity.d.ts.map +1 -1
- package/dist/symlink-parents.d.ts.map +1 -1
- package/dist/symlink-parents.js +1 -2
- package/docs/archive.md +57 -0
- package/docs/atomic.md +2 -0
- package/docs/reading.md +2 -0
- package/docs/root.md +10 -1
- package/docs/types.md +2 -1
- package/docs/writing.md +26 -3
- package/package.json +8 -8
package/dist/root-impl.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { constants as fsConstants } from "node:fs";
|
|
2
|
+
import fsSync, { constants as fsConstants } from "node:fs";
|
|
3
3
|
import fs from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { normalizeMaxBytes } from "./byte-budget.js";
|
|
@@ -87,7 +87,7 @@ async function openVerifiedLocalFile(filePath, options) {
|
|
|
87
87
|
// results that get sent to messaging channels). See openclaw/openclaw#31186.
|
|
88
88
|
try {
|
|
89
89
|
preOpenStat = await inspectFileIdentity(async () => {
|
|
90
|
-
const stat =
|
|
90
|
+
const stat = fsSync.lstatSync(filePath, { bigint: true });
|
|
91
91
|
observedBeforeOpen = true;
|
|
92
92
|
if (stat.isSymbolicLink() && options?.symlinks !== "follow-within-root") {
|
|
93
93
|
throw new FsSafeError("symlink", "symlink not allowed");
|
|
@@ -127,13 +127,13 @@ async function openVerifiedLocalFile(filePath, options) {
|
|
|
127
127
|
}
|
|
128
128
|
try {
|
|
129
129
|
await fsSafeTestHooks?.afterOpen?.(filePath, handle);
|
|
130
|
-
const stat =
|
|
130
|
+
const stat = fsSync.fstatSync(handle.fd);
|
|
131
131
|
if (!stat.isFile()) {
|
|
132
132
|
throw new FsSafeError("not-file", "not a file");
|
|
133
133
|
}
|
|
134
134
|
// Keep numeric Stats for the public receipt, never for identity verification.
|
|
135
135
|
let openedIdentity;
|
|
136
|
-
const identity = await inspectFileIdentity(async () => (openedIdentity =
|
|
136
|
+
const identity = await inspectFileIdentity(async () => (openedIdentity = fsSync.fstatSync(handle.fd, { bigint: true })), preOpenStat && !preOpenStat.isSymbolicLink() ? preOpenStat : undefined).catch(async (error) => {
|
|
137
137
|
if ([0, 1].includes(stat.nlink)) {
|
|
138
138
|
await recordPreOpenFileChange(error, handle, filePath, preOpenStat, openedIdentity);
|
|
139
139
|
}
|
|
@@ -156,8 +156,8 @@ async function openVerifiedLocalFile(filePath, options) {
|
|
|
156
156
|
};
|
|
157
157
|
await inspectPathIdentity(async () => {
|
|
158
158
|
const pathStat = options?.symlinks === "follow-within-root"
|
|
159
|
-
?
|
|
160
|
-
:
|
|
159
|
+
? fsSync.statSync(filePath, { bigint: true })
|
|
160
|
+
: fsSync.lstatSync(filePath, { bigint: true });
|
|
161
161
|
if (pathStat.isSymbolicLink() && options?.symlinks !== "follow-within-root") {
|
|
162
162
|
throw new FsSafeError("symlink", "symlink not allowed");
|
|
163
163
|
}
|
|
@@ -176,7 +176,7 @@ async function openVerifiedLocalFile(filePath, options) {
|
|
|
176
176
|
await inspectPathIdentity(async () => {
|
|
177
177
|
// Reuse the post-realpath observation; unknown Windows identities still
|
|
178
178
|
// get a fresh observation on inspectFileIdentity's retry.
|
|
179
|
-
const realStat = resolvedStat ??
|
|
179
|
+
const realStat = resolvedStat ?? fsSync.statSync(realPath, { bigint: true });
|
|
180
180
|
resolvedStat = undefined;
|
|
181
181
|
if (options?.hardlinks === "reject" && realStat.nlink > 1n) {
|
|
182
182
|
throw hardlinkedPathNotAllowedError();
|
|
@@ -275,6 +275,7 @@ export class RootHandle {
|
|
|
275
275
|
mkdir: this.defaults.mkdir,
|
|
276
276
|
mode: this.defaults.mode,
|
|
277
277
|
...this.mutationOptions(options),
|
|
278
|
+
durable: options.durable ?? this.defaults.durable ?? true,
|
|
278
279
|
});
|
|
279
280
|
}
|
|
280
281
|
async remove(relativePath, options = {}) {
|
|
@@ -307,6 +308,7 @@ export class RootHandle {
|
|
|
307
308
|
mode: this.defaults.mode,
|
|
308
309
|
renameIdentity: this.defaults.renameIdentity,
|
|
309
310
|
...this.mutationOptions(options),
|
|
311
|
+
durable: options.durable ?? this.defaults.durable ?? true,
|
|
310
312
|
});
|
|
311
313
|
}
|
|
312
314
|
async create(relativePath, data, options = {}) {
|
|
@@ -317,6 +319,7 @@ export class RootHandle {
|
|
|
317
319
|
mkdir: this.defaults.mkdir,
|
|
318
320
|
mode: this.defaults.mode,
|
|
319
321
|
...this.mutationOptions(options),
|
|
322
|
+
durable: options.durable ?? this.defaults.durable ?? true,
|
|
320
323
|
overwrite: false,
|
|
321
324
|
});
|
|
322
325
|
}
|
|
@@ -486,7 +489,7 @@ async function writeTempFileForAtomicReplace(params) {
|
|
|
486
489
|
}
|
|
487
490
|
return {
|
|
488
491
|
handle: tempHandle,
|
|
489
|
-
identity:
|
|
492
|
+
identity: fsSync.fstatSync(tempHandle.fd, { bigint: true }),
|
|
490
493
|
};
|
|
491
494
|
}
|
|
492
495
|
catch (error) {
|
|
@@ -525,7 +528,7 @@ async function openWritableFileInRoot(root, params) {
|
|
|
525
528
|
? resolved
|
|
526
529
|
: await prepareRootWriteTarget(rootReal, resolved);
|
|
527
530
|
try {
|
|
528
|
-
const resolvedRealPath =
|
|
531
|
+
const resolvedRealPath = fsSync.realpathSync.native(ioPath);
|
|
529
532
|
if (!isPathInside(rootWithSep, resolvedRealPath)) {
|
|
530
533
|
throw outsideWorkspaceError();
|
|
531
534
|
}
|
|
@@ -574,7 +577,7 @@ async function openWritableFileInRoot(root, params) {
|
|
|
574
577
|
let realPathForCleanup = null;
|
|
575
578
|
let createdIdentity = null;
|
|
576
579
|
try {
|
|
577
|
-
const stat =
|
|
580
|
+
const stat = fsSync.fstatSync(handle.fd);
|
|
578
581
|
if (createdForWrite) {
|
|
579
582
|
createdIdentity = stat;
|
|
580
583
|
}
|
|
@@ -585,7 +588,7 @@ async function openWritableFileInRoot(root, params) {
|
|
|
585
588
|
throw hardlinkedPathNotAllowedError();
|
|
586
589
|
}
|
|
587
590
|
try {
|
|
588
|
-
const lstat =
|
|
591
|
+
const lstat = fsSync.lstatSync(ioPath);
|
|
589
592
|
if (lstat.isSymbolicLink() || !lstat.isFile()) {
|
|
590
593
|
throw new FsSafeError(lstat.isSymbolicLink() ? "symlink" : "not-file", "path is not a regular file under root");
|
|
591
594
|
}
|
|
@@ -600,7 +603,7 @@ async function openWritableFileInRoot(root, params) {
|
|
|
600
603
|
}
|
|
601
604
|
const realPath = await resolveOpenedFileRealPathForHandle(handle, ioPath);
|
|
602
605
|
realPathForCleanup = realPath;
|
|
603
|
-
const realStat =
|
|
606
|
+
const realStat = fsSync.statSync(realPath);
|
|
604
607
|
if (!sameFileIdentity(stat, realStat)) {
|
|
605
608
|
throw new FsSafeError("path-mismatch", "path mismatch");
|
|
606
609
|
}
|
|
@@ -663,8 +666,9 @@ async function appendFileInRoot(root, params) {
|
|
|
663
666
|
const payload = prefix.length > 0 ? Buffer.concat([Buffer.from(prefix, "utf8"), params.data]) : params.data;
|
|
664
667
|
await target.handle.appendFile(payload);
|
|
665
668
|
}
|
|
666
|
-
|
|
667
|
-
|
|
669
|
+
if (params.durable !== false)
|
|
670
|
+
await target.handle.sync();
|
|
671
|
+
if (params.durable !== false && target.createdForWrite) {
|
|
668
672
|
await syncDirectoryBestEffort(path.dirname(target.realPath));
|
|
669
673
|
}
|
|
670
674
|
}
|
|
@@ -720,6 +724,7 @@ async function commitPinnedWriteInRoot(root, pinned, params) {
|
|
|
720
724
|
renameIdentity: params.renameIdentity,
|
|
721
725
|
mkdir: params.mkdir !== false,
|
|
722
726
|
mode: params.mode ?? pinned.mode,
|
|
727
|
+
sync: params.durable !== false,
|
|
723
728
|
overwrite: params.overwrite,
|
|
724
729
|
input: { kind: "buffer", data: params.data, encoding: params.encoding },
|
|
725
730
|
rootIdentity: root.rootIdentity,
|
|
@@ -803,12 +808,12 @@ async function copyFileInRoot(root, params) {
|
|
|
803
808
|
}
|
|
804
809
|
}
|
|
805
810
|
async function assertCopySourceCurrent(source, identity) {
|
|
806
|
-
await inspectFileIdentity(() => source.handle.
|
|
811
|
+
await inspectFileIdentity(() => fsSync.fstatSync(source.handle.fd, { bigint: true }), identity);
|
|
807
812
|
await assertCopySourcePathCurrent(source, identity);
|
|
808
813
|
}
|
|
809
814
|
async function assertCopySourcePathCurrent(source, identity) {
|
|
810
815
|
await inspectFileIdentity(async () => {
|
|
811
|
-
const current =
|
|
816
|
+
const current = fsSync.lstatSync(source.realPath, { bigint: true });
|
|
812
817
|
if (current.isSymbolicLink() || !current.isFile()) {
|
|
813
818
|
throw new FsSafeError("path-mismatch", "copy source path changed");
|
|
814
819
|
}
|
|
@@ -817,7 +822,7 @@ async function assertCopySourcePathCurrent(source, identity) {
|
|
|
817
822
|
}
|
|
818
823
|
async function removePathIfIdentityUnchanged(targetPath, identity) {
|
|
819
824
|
const parentGuard = await createAsyncDirectoryGuard(path.dirname(targetPath));
|
|
820
|
-
const current =
|
|
825
|
+
const current = fsSync.lstatSync(targetPath);
|
|
821
826
|
if (current.isSymbolicLink() || !sameFileIdentityForCleanup(current, identity)) {
|
|
822
827
|
return;
|
|
823
828
|
}
|
|
@@ -847,7 +852,7 @@ async function resolvePinnedWriteTargetInRoot(root, relativePath, requestedMode,
|
|
|
847
852
|
// and alias checks, but never open a competing owner's record just to reject it.
|
|
848
853
|
if (!overwrite) {
|
|
849
854
|
try {
|
|
850
|
-
const existing =
|
|
855
|
+
const existing = fsSync.statSync(resolved);
|
|
851
856
|
if (!existing.isFile())
|
|
852
857
|
throw new FsSafeError("not-file", "not a file");
|
|
853
858
|
if (existing.nlink > 1)
|
|
@@ -945,7 +950,7 @@ async function prepareRemoveGuard(targetPath) {
|
|
|
945
950
|
async function removePathFallback(resolved) {
|
|
946
951
|
const guard = await prepareRemoveGuard(resolved.resolved);
|
|
947
952
|
try {
|
|
948
|
-
await (
|
|
953
|
+
await (fsSync.lstatSync(resolved.resolved).isDirectory() ? fs.rmdir(resolved.resolved) : fs.rm(resolved.resolved));
|
|
949
954
|
}
|
|
950
955
|
catch (error) {
|
|
951
956
|
throw normalizeRemovePathError(error);
|
|
@@ -961,7 +966,7 @@ async function mkdirPathFallback(resolved) {
|
|
|
961
966
|
async function statPathFallback(root, relativePath) {
|
|
962
967
|
const resolved = await resolvePinnedPathInRoot(root, { relativePath, allowRoot: true });
|
|
963
968
|
try {
|
|
964
|
-
const stat = pathStatFromStats(
|
|
969
|
+
const stat = pathStatFromStats(fsSync.lstatSync(resolved.resolved));
|
|
965
970
|
await assertRootIdentityCurrent(root);
|
|
966
971
|
return stat;
|
|
967
972
|
}
|
|
@@ -985,7 +990,7 @@ async function listPathFallback(root, relativePath, withFileTypes) {
|
|
|
985
990
|
for (const name of sortedNames) {
|
|
986
991
|
entries.push({
|
|
987
992
|
name,
|
|
988
|
-
...pathStatFromStats(
|
|
993
|
+
...pathStatFromStats(fsSync.lstatSync(path.join(resolved.resolved, name))),
|
|
989
994
|
});
|
|
990
995
|
}
|
|
991
996
|
await assertRootIdentityCurrent(root);
|
|
@@ -1034,7 +1039,11 @@ async function movePathFallback(root, params) {
|
|
|
1034
1039
|
relativePath: params.toRelative,
|
|
1035
1040
|
policy: PATH_ALIAS_POLICIES.unlinkTarget,
|
|
1036
1041
|
});
|
|
1037
|
-
|
|
1042
|
+
let targetStat;
|
|
1043
|
+
try {
|
|
1044
|
+
targetStat = fsSync.lstatSync(resolvedTarget.resolved);
|
|
1045
|
+
}
|
|
1046
|
+
catch { /* Advisory lookup. */ }
|
|
1038
1047
|
return !(process.platform !== "win32" &&
|
|
1039
1048
|
params.overwrite &&
|
|
1040
1049
|
targetStat?.isSymbolicLink() === true);
|
|
@@ -1042,7 +1051,7 @@ async function movePathFallback(root, params) {
|
|
|
1042
1051
|
});
|
|
1043
1052
|
let sourceStat;
|
|
1044
1053
|
try {
|
|
1045
|
-
sourceStat =
|
|
1054
|
+
sourceStat = fsSync.lstatSync(source.resolved);
|
|
1046
1055
|
}
|
|
1047
1056
|
catch (error) {
|
|
1048
1057
|
if (isNotFoundPathError(error)) {
|
|
@@ -1061,7 +1070,7 @@ async function movePathFallback(root, params) {
|
|
|
1061
1070
|
}
|
|
1062
1071
|
if (!params.overwrite) {
|
|
1063
1072
|
try {
|
|
1064
|
-
|
|
1073
|
+
fsSync.lstatSync(target.resolved);
|
|
1065
1074
|
throw new FsSafeError("already-exists", "destination exists");
|
|
1066
1075
|
}
|
|
1067
1076
|
catch (error) {
|
|
@@ -1171,8 +1180,8 @@ async function writeMissingFileFallback(root, params) {
|
|
|
1171
1180
|
const handle = await fs.open(targetPath, OPEN_WRITE_CREATE_FLAGS, params.mode ?? 0o600).catch((error) => recordExclusiveCreateFailure(error, targetPath));
|
|
1172
1181
|
created = true;
|
|
1173
1182
|
try {
|
|
1174
|
-
createdIdentity =
|
|
1175
|
-
const writtenStat =
|
|
1183
|
+
createdIdentity = fsSync.fstatSync(handle.fd);
|
|
1184
|
+
const writtenStat = fsSync.fstatSync(handle.fd, { bigint: true });
|
|
1176
1185
|
if (typeof params.data === "string") {
|
|
1177
1186
|
await handle.writeFile(params.data, params.encoding ?? "utf8");
|
|
1178
1187
|
}
|
|
@@ -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":"AAoBA,wBAAsB,8BAA8B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA0BxF;AAED,wBAAgB,kCAAkC,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CA0B7E"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
-
import fsp from "node:fs/promises";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
import { isNotFoundPathError } from "./path.js";
|
|
5
4
|
function isFilesystemRoot(candidate) {
|
|
@@ -7,7 +6,7 @@ function isFilesystemRoot(candidate) {
|
|
|
7
6
|
}
|
|
8
7
|
async function pathExists(targetPath) {
|
|
9
8
|
try {
|
|
10
|
-
|
|
9
|
+
fs.lstatSync(targetPath);
|
|
11
10
|
return true;
|
|
12
11
|
}
|
|
13
12
|
catch (error) {
|
|
@@ -33,7 +32,7 @@ export async function resolvePathViaExistingAncestor(targetPath) {
|
|
|
33
32
|
return normalized;
|
|
34
33
|
}
|
|
35
34
|
try {
|
|
36
|
-
const resolvedAncestor = path.resolve(
|
|
35
|
+
const resolvedAncestor = path.resolve(fs.realpathSync.native(cursor));
|
|
37
36
|
return missingSuffix.length === 0
|
|
38
37
|
? resolvedAncestor
|
|
39
38
|
: path.resolve(resolvedAncestor, ...missingSuffix);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root-path-symlink.d.ts","sourceRoot":"","sources":["../src/root-path-symlink.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"root-path-symlink.d.ts","sourceRoot":"","sources":["../src/root-path-symlink.ts"],"names":[],"mappings":"AASA,KAAK,wBAAwB,GAAG;IAC9B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAiBF,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,MAAM,CAAC,CAQjB;AAED,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,wBAA6B,GACrC,MAAM,CAQR"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
-
import fsp from "node:fs/promises";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
import { FsSafeError } from "./errors.js";
|
|
5
4
|
import { isNotFoundPathError, isSymlinkOpenError } from "./path.js";
|
|
@@ -16,11 +15,11 @@ function normalizeSymlinkResolutionError(error, options) {
|
|
|
16
15
|
}
|
|
17
16
|
export async function resolveSymlinkHopPath(symlinkPath, options = {}) {
|
|
18
17
|
try {
|
|
19
|
-
return path.resolve(
|
|
18
|
+
return path.resolve(fs.realpathSync.native(symlinkPath));
|
|
20
19
|
}
|
|
21
20
|
catch (error) {
|
|
22
21
|
normalizeSymlinkResolutionError(error, options);
|
|
23
|
-
const linkTarget =
|
|
22
|
+
const linkTarget = fs.readlinkSync(symlinkPath);
|
|
24
23
|
return resolvePathViaExistingAncestor(path.resolve(path.dirname(symlinkPath), linkTarget));
|
|
25
24
|
}
|
|
26
25
|
}
|
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":"AAiBA,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,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;AAsCD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,qBAAqB,GAAG,gBAAgB,CAMnF"}
|
package/dist/root-path.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
-
import fsp from "node:fs/promises";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
import { formatErrorDetail, shortPath } from "./error-detail.js";
|
|
5
4
|
import { FsSafeError } from "./errors.js";
|
|
@@ -234,7 +233,7 @@ async function resolveRootPathLexicalAsync(params) {
|
|
|
234
233
|
state.lexicalCursor = path.join(state.lexicalCursor, segment);
|
|
235
234
|
let stat;
|
|
236
235
|
try {
|
|
237
|
-
stat =
|
|
236
|
+
stat = fs.lstatSync(state.lexicalCursor);
|
|
238
237
|
}
|
|
239
238
|
catch (error) {
|
|
240
239
|
if (handleLexicalLstatFailure(context, error, idx))
|
|
@@ -423,8 +422,8 @@ function buildResolvedRootPath(params) {
|
|
|
423
422
|
async function getPathKind(absolutePath, preserveFinalSymlink) {
|
|
424
423
|
try {
|
|
425
424
|
const stat = preserveFinalSymlink
|
|
426
|
-
?
|
|
427
|
-
:
|
|
425
|
+
? fs.lstatSync(absolutePath)
|
|
426
|
+
: fs.statSync(absolutePath);
|
|
428
427
|
return { exists: true, kind: toResolvedKind(stat) };
|
|
429
428
|
}
|
|
430
429
|
catch (error) {
|
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":"
|
|
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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import fsSync from "node:fs";
|
|
1
2
|
import fs from "node:fs/promises";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { formatErrorDetail } from "./error-detail.js";
|
|
@@ -16,7 +17,7 @@ function pathStaysWithinRoot(rootDir, candidatePath) {
|
|
|
16
17
|
}
|
|
17
18
|
async function resolveRealPathIfExists(targetPath) {
|
|
18
19
|
try {
|
|
19
|
-
return
|
|
20
|
+
return fsSync.realpathSync.native(targetPath);
|
|
20
21
|
}
|
|
21
22
|
catch {
|
|
22
23
|
return undefined;
|
|
@@ -24,11 +25,11 @@ async function resolveRealPathIfExists(targetPath) {
|
|
|
24
25
|
}
|
|
25
26
|
async function resolveTrustedRootRealPath(rootDir) {
|
|
26
27
|
try {
|
|
27
|
-
const rootLstat =
|
|
28
|
+
const rootLstat = fsSync.lstatSync(rootDir);
|
|
28
29
|
if (!rootLstat.isDirectory() || rootLstat.isSymbolicLink()) {
|
|
29
30
|
return undefined;
|
|
30
31
|
}
|
|
31
|
-
return
|
|
32
|
+
return fsSync.realpathSync.native(rootDir);
|
|
32
33
|
}
|
|
33
34
|
catch {
|
|
34
35
|
return undefined;
|
|
@@ -36,7 +37,7 @@ async function resolveTrustedRootRealPath(rootDir) {
|
|
|
36
37
|
}
|
|
37
38
|
async function validateCanonicalPathWithinRoot(params) {
|
|
38
39
|
try {
|
|
39
|
-
const candidateLstat =
|
|
40
|
+
const candidateLstat = fsSync.lstatSync(params.candidatePath);
|
|
40
41
|
if (candidateLstat.isSymbolicLink()) {
|
|
41
42
|
return "invalid";
|
|
42
43
|
}
|
|
@@ -49,7 +50,7 @@ async function validateCanonicalPathWithinRoot(params) {
|
|
|
49
50
|
if (params.expect === "file" && candidateLstat.nlink > 1) {
|
|
50
51
|
return "invalid";
|
|
51
52
|
}
|
|
52
|
-
const candidateRealPath =
|
|
53
|
+
const candidateRealPath = fsSync.realpathSync.native(params.candidatePath);
|
|
53
54
|
return isPathInside(params.rootRealPath, candidateRealPath) ? "ok" : "invalid";
|
|
54
55
|
}
|
|
55
56
|
catch (err) {
|
|
@@ -109,7 +110,7 @@ async function resolveNearestExistingPath(targetPath) {
|
|
|
109
110
|
let current = path.resolve(targetPath);
|
|
110
111
|
while (true) {
|
|
111
112
|
try {
|
|
112
|
-
|
|
113
|
+
fsSync.lstatSync(current);
|
|
113
114
|
return current;
|
|
114
115
|
}
|
|
115
116
|
catch (err) {
|
|
@@ -133,7 +134,7 @@ async function assertNoSymlinkSegments(params) {
|
|
|
133
134
|
for (const segment of relative.split(path.sep).filter(Boolean)) {
|
|
134
135
|
current = path.join(current, segment);
|
|
135
136
|
try {
|
|
136
|
-
const stat =
|
|
137
|
+
const stat = fsSync.lstatSync(current);
|
|
137
138
|
if (stat.isSymbolicLink()) {
|
|
138
139
|
throw new FsSafeError("symlink", `Invalid path: must not traverse symlinks within ${params.scopeLabel}`);
|
|
139
140
|
}
|
|
@@ -163,14 +164,14 @@ export async function ensureDirectoryWithinRoot(params) {
|
|
|
163
164
|
return lexical;
|
|
164
165
|
const rootDir = path.resolve(params.rootDir);
|
|
165
166
|
const targetPath = lexical.path;
|
|
166
|
-
const rootStat =
|
|
167
|
+
const rootStat = fsSync.lstatSync(rootDir);
|
|
167
168
|
if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) {
|
|
168
169
|
return invalidPath(scopeLabel);
|
|
169
170
|
}
|
|
170
171
|
await assertNoSymlinkSegments({ rootDir, targetPath, scopeLabel });
|
|
171
|
-
const rootReal =
|
|
172
|
+
const rootReal = fsSync.realpathSync.native(rootDir);
|
|
172
173
|
const nearestExistingPath = await resolveNearestExistingPath(targetPath);
|
|
173
|
-
const nearestExistingReal =
|
|
174
|
+
const nearestExistingReal = fsSync.realpathSync.native(nearestExistingPath);
|
|
174
175
|
if (!isPathInside(rootReal, nearestExistingReal)) {
|
|
175
176
|
return invalidPath(scopeLabel);
|
|
176
177
|
}
|
|
@@ -180,7 +181,7 @@ export async function ensureDirectoryWithinRoot(params) {
|
|
|
180
181
|
current = path.join(current, segment);
|
|
181
182
|
while (true) {
|
|
182
183
|
try {
|
|
183
|
-
const stat =
|
|
184
|
+
const stat = fsSync.lstatSync(current);
|
|
184
185
|
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
185
186
|
return invalidPath(scopeLabel);
|
|
186
187
|
}
|
|
@@ -201,12 +202,12 @@ export async function ensureDirectoryWithinRoot(params) {
|
|
|
201
202
|
}
|
|
202
203
|
}
|
|
203
204
|
}
|
|
204
|
-
const currentReal =
|
|
205
|
+
const currentReal = fsSync.realpathSync.native(current);
|
|
205
206
|
if (!isPathInside(rootReal, currentReal)) {
|
|
206
207
|
return invalidPath(scopeLabel);
|
|
207
208
|
}
|
|
208
209
|
}
|
|
209
|
-
const targetReal =
|
|
210
|
+
const targetReal = fsSync.realpathSync.native(targetPath);
|
|
210
211
|
if (!isPathInside(rootReal, targetReal)) {
|
|
211
212
|
return invalidPath(scopeLabel);
|
|
212
213
|
}
|
|
@@ -308,7 +309,7 @@ async function resolveCheckedPathsWithinRoot(params, allowMissingFallback) {
|
|
|
308
309
|
return lexicalPathResult;
|
|
309
310
|
}
|
|
310
311
|
try {
|
|
311
|
-
const resolvedExistingPath =
|
|
312
|
+
const resolvedExistingPath = fsSync.realpathSync.native(raw);
|
|
312
313
|
const relativePath = path.relative(rootRealPath, resolvedExistingPath);
|
|
313
314
|
if (!isInRoot(relativePath)) {
|
|
314
315
|
return lexicalPathResult;
|
|
@@ -350,7 +351,7 @@ async function resolveCheckedPathsWithinRoot(params, allowMissingFallback) {
|
|
|
350
351
|
scopeLabel: params.scopeLabel,
|
|
351
352
|
});
|
|
352
353
|
const existingPath = await resolveNearestExistingPath(pathResult.fallbackPath);
|
|
353
|
-
const existingRealPath =
|
|
354
|
+
const existingRealPath = fsSync.realpathSync.native(existingPath);
|
|
354
355
|
if (!isPathInside(rootRealPath, existingRealPath)) {
|
|
355
356
|
return invalidPath(params.scopeLabel);
|
|
356
357
|
}
|
|
@@ -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,CAwClB"}
|
package/dist/root-write-mode.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import fsSync from "node:fs";
|
|
1
2
|
import fs from "node:fs/promises";
|
|
2
3
|
import { FsSafeError } from "./errors.js";
|
|
3
4
|
import { sameFileIdentity } from "./file-identity.js";
|
|
@@ -8,7 +9,7 @@ import { inspectFileIdentity } from "./strict-file-identity.js";
|
|
|
8
9
|
// The caller has already resolved and guarded this write target.
|
|
9
10
|
export async function inheritWriteTargetMode(params) {
|
|
10
11
|
try {
|
|
11
|
-
const existing = await inspectFileIdentity(() =>
|
|
12
|
+
const existing = await inspectFileIdentity(() => fsSync.lstatSync(params.targetPath, { bigint: true }));
|
|
12
13
|
if (existing.isSymbolicLink())
|
|
13
14
|
throw new FsSafeError("path-alias", "path alias escape blocked");
|
|
14
15
|
if (!existing.isFile())
|
|
@@ -22,7 +23,7 @@ export async function inheritWriteTargetMode(params) {
|
|
|
22
23
|
const handle = await fs.open(params.targetPath, resolveReadOpenFlags());
|
|
23
24
|
try {
|
|
24
25
|
// Bind admission to the inode whose metadata is inherited.
|
|
25
|
-
if (!sameFileIdentity(
|
|
26
|
+
if (!sameFileIdentity(fsSync.fstatSync(handle.fd, { bigint: true }), existing)) {
|
|
26
27
|
throw new FsSafeError("path-mismatch", "write target changed during mode inheritance");
|
|
27
28
|
}
|
|
28
29
|
}
|
|
@@ -32,11 +33,11 @@ export async function inheritWriteTargetMode(params) {
|
|
|
32
33
|
try {
|
|
33
34
|
// A parent can change after guarded resolution. Do not inherit metadata
|
|
34
35
|
// from an outside inode, even if the parent is restored before publication.
|
|
35
|
-
const realPath =
|
|
36
|
+
const realPath = fsSync.realpathSync.native(params.targetPath);
|
|
36
37
|
if (!isPathInside(params.rootWithSep, realPath))
|
|
37
38
|
throw outsideWorkspaceError();
|
|
38
39
|
await inspectFileIdentity(async () => {
|
|
39
|
-
const current =
|
|
40
|
+
const current = fsSync.statSync(realPath, { bigint: true });
|
|
40
41
|
if (!current.isFile())
|
|
41
42
|
throw new FsSafeError("not-file", "not a file");
|
|
42
43
|
if (current.nlink > 1n)
|
|
@@ -46,16 +46,16 @@ export async function verifyAtomicWriteResult(params) {
|
|
|
46
46
|
try {
|
|
47
47
|
// This descriptor remains owned by the writer, even when final mode forbids opens.
|
|
48
48
|
const stat = assertDescriptor();
|
|
49
|
-
assertPath(
|
|
49
|
+
assertPath(fsSync.lstatSync(params.targetPath, { bigint: true }));
|
|
50
50
|
const { realPath } = await resolveOpenedFileRealPathForFd(params.fd, stat, params.targetPath);
|
|
51
|
-
assertPath(
|
|
51
|
+
assertPath(fsSync.statSync(realPath, { bigint: true }));
|
|
52
52
|
if (!isPathInside(params.root.rootWithSep, realPath)) {
|
|
53
53
|
throw outsideWorkspaceError();
|
|
54
54
|
}
|
|
55
55
|
await assertAsyncDirectoryGuard(params.parentGuard);
|
|
56
56
|
await assertRootIdentityCurrent(params.root);
|
|
57
57
|
// Recheck after canonical resolution and directory checks, including late links.
|
|
58
|
-
assertPath(
|
|
58
|
+
assertPath(fsSync.lstatSync(params.targetPath, { bigint: true }));
|
|
59
59
|
assertDescriptor();
|
|
60
60
|
if (needsPathOpen) {
|
|
61
61
|
// A retained fd cannot prove that an opaque Windows pathname still names it.
|
|
@@ -73,15 +73,15 @@ export async function verifyAtomicWriteResult(params) {
|
|
|
73
73
|
});
|
|
74
74
|
try {
|
|
75
75
|
const reopenedStat = assertDescriptor(opened.fd);
|
|
76
|
-
assertPath(
|
|
76
|
+
assertPath(fsSync.lstatSync(params.targetPath, { bigint: true }));
|
|
77
77
|
const { realPath: reopenedPath } = await resolveOpenedFileRealPathForFd(opened.fd, reopenedStat, params.targetPath);
|
|
78
|
-
assertPath(
|
|
78
|
+
assertPath(fsSync.statSync(reopenedPath, { bigint: true }));
|
|
79
79
|
if (!isPathInside(params.root.rootWithSep, reopenedPath)) {
|
|
80
80
|
throw outsideWorkspaceError();
|
|
81
81
|
}
|
|
82
82
|
await assertAsyncDirectoryGuard(params.parentGuard);
|
|
83
83
|
await assertRootIdentityCurrent(params.root);
|
|
84
|
-
assertPath(
|
|
84
|
+
assertPath(fsSync.lstatSync(params.targetPath, { bigint: true }));
|
|
85
85
|
assertDescriptor(opened.fd);
|
|
86
86
|
}
|
|
87
87
|
finally {
|
package/dist/secret-file.js
CHANGED
|
@@ -140,7 +140,7 @@ async function enforcePrivateDirectoryMode(params) {
|
|
|
140
140
|
}
|
|
141
141
|
async function inspectPrivateDirectory(directory, kind) {
|
|
142
142
|
return await inspectFileIdentity(async () => {
|
|
143
|
-
const stat =
|
|
143
|
+
const stat = fs.lstatSync(directory, { bigint: true });
|
|
144
144
|
if (stat.isSymbolicLink()) {
|
|
145
145
|
throw new Error(`Private secret ${kind} ${directory} must not be a symlink.`);
|
|
146
146
|
}
|
|
@@ -260,7 +260,7 @@ export async function prepareSecretFileWrite(params) {
|
|
|
260
260
|
async function materializeSecretFileAtomic(params, createOnly) {
|
|
261
261
|
const { mode, rootGuard, parentGuard, fileName, finalFilePath } = await prepareSecretFileWrite(params);
|
|
262
262
|
try {
|
|
263
|
-
const stat =
|
|
263
|
+
const stat = fs.lstatSync(finalFilePath);
|
|
264
264
|
if (createOnly) {
|
|
265
265
|
throw new FsSafeError("secret-exists", `Private secret file ${finalFilePath} already exists.`);
|
|
266
266
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { BigIntStats } from "node:fs";
|
|
2
2
|
type ExactFileIdentity = Pick<BigIntStats, "dev" | "ino">;
|
|
3
|
-
export declare function inspectFileIdentity<T extends ExactFileIdentity>(inspect: () => Promise<T>, expected?: ExactFileIdentity, platform?: NodeJS.Platform): Promise<T>;
|
|
3
|
+
export declare function inspectFileIdentity<T extends ExactFileIdentity>(inspect: () => T | Promise<T>, expected?: ExactFileIdentity, platform?: NodeJS.Platform): Promise<T>;
|
|
4
4
|
export declare function inspectFileIdentitySync<T extends ExactFileIdentity>(inspect: () => T, expected?: ExactFileIdentity, platform?: NodeJS.Platform): T;
|
|
5
5
|
export {};
|
|
6
6
|
//# sourceMappingURL=strict-file-identity.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"strict-file-identity.d.ts","sourceRoot":"","sources":["../src/strict-file-identity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAI3C,KAAK,iBAAiB,GAAG,IAAI,CAAC,WAAW,EAAE,KAAK,GAAG,KAAK,CAAC,CAAC;AA+B1D,wBAAsB,mBAAmB,CAAC,CAAC,SAAS,iBAAiB,EACnE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"strict-file-identity.d.ts","sourceRoot":"","sources":["../src/strict-file-identity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAI3C,KAAK,iBAAiB,GAAG,IAAI,CAAC,WAAW,EAAE,KAAK,GAAG,KAAK,CAAC,CAAC;AA+B1D,wBAAsB,mBAAmB,CAAC,CAAC,SAAS,iBAAiB,EACnE,OAAO,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EAC7B,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,OAAO,CAAC,CAAC,CAAC,CAOZ;AAED,wBAAgB,uBAAuB,CAAC,CAAC,SAAS,iBAAiB,EACjE,OAAO,EAAE,MAAM,CAAC,EAChB,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,CAAC,CAOH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"symlink-parents.d.ts","sourceRoot":"","sources":["../src/symlink-parents.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"symlink-parents.d.ts","sourceRoot":"","sources":["../src/symlink-parents.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,6BAA6B,GAAG;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAyBF,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,6BAA6B,GACpC,OAAO,CAAC,IAAI,CAAC,CA6Bf;AAED,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,6BAA6B,GACpC,IAAI,CA6BN"}
|
package/dist/symlink-parents.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import fsSync from "node:fs";
|
|
2
|
-
import fs from "node:fs/promises";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
import { FsSafeError } from "./errors.js";
|
|
5
4
|
import { hasNodeErrorCode, isPathRelativeEscape } from "./path.js";
|
|
@@ -30,7 +29,7 @@ export async function assertNoSymlinkParents(params) {
|
|
|
30
29
|
for (const [index, segment] of walk.segments.entries()) {
|
|
31
30
|
current = path.join(current, segment);
|
|
32
31
|
try {
|
|
33
|
-
const stat =
|
|
32
|
+
const stat = fsSync.lstatSync(current);
|
|
34
33
|
if (stat.isSymbolicLink()) {
|
|
35
34
|
if (params.allowRootChildSymlink && path.dirname(current) === walk.root) {
|
|
36
35
|
continue;
|