@openclaw/fs-safe 0.2.1 → 0.2.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.
Files changed (58) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/archive-utils.d.ts +3 -0
  3. package/dist/archive-utils.d.ts.map +1 -0
  4. package/dist/archive-utils.js +26 -0
  5. package/dist/boundary-file-read.d.ts +44 -0
  6. package/dist/boundary-file-read.d.ts.map +1 -0
  7. package/dist/boundary-file-read.js +129 -0
  8. package/dist/boundary-path.d.ts +39 -0
  9. package/dist/boundary-path.d.ts.map +1 -0
  10. package/dist/boundary-path.js +598 -0
  11. package/dist/fs-pinned-path-helper.d.ts +7 -0
  12. package/dist/fs-pinned-path-helper.d.ts.map +1 -0
  13. package/dist/fs-pinned-path-helper.js +182 -0
  14. package/dist/fs-pinned-write-helper.d.ts +21 -0
  15. package/dist/fs-pinned-write-helper.d.ts.map +1 -0
  16. package/dist/fs-pinned-write-helper.js +263 -0
  17. package/dist/hardlink-guards.d.ts +7 -0
  18. package/dist/hardlink-guards.d.ts.map +1 -0
  19. package/dist/hardlink-guards.js +30 -0
  20. package/dist/install-safe-path.d.ts +20 -0
  21. package/dist/install-safe-path.d.ts.map +1 -0
  22. package/dist/install-safe-path.js +94 -0
  23. package/dist/json-file.d.ts +3 -0
  24. package/dist/json-file.d.ts.map +1 -0
  25. package/dist/json-file.js +123 -0
  26. package/dist/json-files.d.ts +20 -0
  27. package/dist/json-files.d.ts.map +1 -0
  28. package/dist/json-files.js +153 -0
  29. package/dist/path-alias-guards.d.ts +19 -0
  30. package/dist/path-alias-guards.d.ts.map +1 -0
  31. package/dist/path-alias-guards.js +21 -0
  32. package/dist/path-guards.d.ts +7 -0
  33. package/dist/path-guards.d.ts.map +1 -0
  34. package/dist/path-guards.js +49 -0
  35. package/dist/path-safety.d.ts +12 -0
  36. package/dist/path-safety.d.ts.map +1 -0
  37. package/dist/path-safety.js +50 -0
  38. package/dist/pinned-python-config.d.ts.map +1 -1
  39. package/dist/pinned-python-config.js +2 -3
  40. package/dist/private-file-store.d.ts +7 -5
  41. package/dist/private-file-store.d.ts.map +1 -1
  42. package/dist/private-file-store.js +34 -21
  43. package/dist/safe-open-sync.d.ts +24 -0
  44. package/dist/safe-open-sync.d.ts.map +1 -0
  45. package/dist/safe-open-sync.js +71 -0
  46. package/dist/safe-root.d.ts +123 -0
  47. package/dist/safe-root.d.ts.map +1 -0
  48. package/dist/safe-root.js +1060 -0
  49. package/dist/secure-temp-workspace.d.ts +25 -0
  50. package/dist/secure-temp-workspace.d.ts.map +1 -0
  51. package/dist/secure-temp-workspace.js +136 -0
  52. package/dist/sibling-temp-file.d.ts +16 -0
  53. package/dist/sibling-temp-file.d.ts.map +1 -0
  54. package/dist/sibling-temp-file.js +73 -0
  55. package/dist/sibling-temp-write.d.ts +8 -0
  56. package/dist/sibling-temp-write.d.ts.map +1 -0
  57. package/dist/sibling-temp-write.js +40 -0
  58. package/package.json +1 -1
@@ -0,0 +1,71 @@
1
+ import fs from "node:fs";
2
+ import { sameFileIdentity as hasSameFileIdentity } from "./file-identity.js";
3
+ function isExpectedPathError(error) {
4
+ const code = typeof error === "object" && error !== null && "code" in error ? String(error.code) : "";
5
+ return code === "ENOENT" || code === "ENOTDIR" || code === "ELOOP";
6
+ }
7
+ function sameFileIdentity(left, right) {
8
+ return hasSameFileIdentity(left, right);
9
+ }
10
+ export function openVerifiedFileSync(params) {
11
+ const ioFs = params.ioFs ?? fs;
12
+ const allowedType = params.allowedType ?? "file";
13
+ const openReadFlags = ioFs.constants.O_RDONLY |
14
+ (typeof ioFs.constants.O_NOFOLLOW === "number" ? ioFs.constants.O_NOFOLLOW : 0);
15
+ let fd = null;
16
+ try {
17
+ if (params.rejectPathSymlink) {
18
+ const candidateStat = ioFs.lstatSync(params.filePath);
19
+ if (candidateStat.isSymbolicLink()) {
20
+ return { ok: false, reason: "validation" };
21
+ }
22
+ }
23
+ const realPath = params.resolvedPath ?? ioFs.realpathSync(params.filePath);
24
+ const preOpenStat = ioFs.lstatSync(realPath);
25
+ if (!isAllowedType(preOpenStat, allowedType)) {
26
+ return { ok: false, reason: "validation" };
27
+ }
28
+ if (params.rejectHardlinks && preOpenStat.isFile() && preOpenStat.nlink > 1) {
29
+ return { ok: false, reason: "validation" };
30
+ }
31
+ if (params.maxBytes !== undefined &&
32
+ preOpenStat.isFile() &&
33
+ preOpenStat.size > params.maxBytes) {
34
+ return { ok: false, reason: "validation" };
35
+ }
36
+ fd = ioFs.openSync(realPath, openReadFlags);
37
+ const openedStat = ioFs.fstatSync(fd);
38
+ if (!isAllowedType(openedStat, allowedType)) {
39
+ return { ok: false, reason: "validation" };
40
+ }
41
+ if (params.rejectHardlinks && openedStat.isFile() && openedStat.nlink > 1) {
42
+ return { ok: false, reason: "validation" };
43
+ }
44
+ if (params.maxBytes !== undefined && openedStat.isFile() && openedStat.size > params.maxBytes) {
45
+ return { ok: false, reason: "validation" };
46
+ }
47
+ if (!sameFileIdentity(preOpenStat, openedStat)) {
48
+ return { ok: false, reason: "validation" };
49
+ }
50
+ const opened = { ok: true, path: realPath, fd, stat: openedStat };
51
+ fd = null;
52
+ return opened;
53
+ }
54
+ catch (error) {
55
+ if (isExpectedPathError(error)) {
56
+ return { ok: false, reason: "path", error };
57
+ }
58
+ return { ok: false, reason: "io", error };
59
+ }
60
+ finally {
61
+ if (fd !== null) {
62
+ ioFs.closeSync(fd);
63
+ }
64
+ }
65
+ }
66
+ function isAllowedType(stat, allowedType) {
67
+ if (allowedType === "directory") {
68
+ return stat.isDirectory();
69
+ }
70
+ return stat.isFile();
71
+ }
@@ -0,0 +1,123 @@
1
+ import type { Stats } from "node:fs";
2
+ import type { FileHandle } from "node:fs/promises";
3
+ import type { SafeDirEntry, SafePathStat } from "./types.js";
4
+ export type SafeOpenResult = {
5
+ handle: FileHandle;
6
+ realPath: string;
7
+ stat: Stats;
8
+ };
9
+ export type SafeLocalReadResult = {
10
+ buffer: Buffer;
11
+ realPath: string;
12
+ stat: Stats;
13
+ };
14
+ export type SafeRootContext = {
15
+ rootDir: string;
16
+ rootReal: string;
17
+ rootWithSep: string;
18
+ };
19
+ export type SafeRootOptions = {
20
+ rootDir: string;
21
+ defaults?: SafeRootDefaults;
22
+ };
23
+ export type SafeRootDefaults = {
24
+ append?: SafeRootAppendOptions;
25
+ copy?: SafeRootCopyOptions;
26
+ /** openFile merges read defaults first, then open defaults, then call options. */
27
+ open?: SafeRootOpenOptions;
28
+ openWritable?: SafeRootOpenWritableOptions;
29
+ read?: SafeRootReadOptions;
30
+ write?: SafeRootWriteOptions;
31
+ };
32
+ export type SafeRootSymlinkPolicy = "reject" | "follow-within-root";
33
+ export type SafeRootReadOptions = {
34
+ rejectHardlinks?: boolean;
35
+ nonBlockingRead?: boolean;
36
+ symlinkPolicy?: SafeRootSymlinkPolicy;
37
+ maxBytes?: number;
38
+ };
39
+ export type SafeRootOpenOptions = Omit<SafeRootReadOptions, "maxBytes">;
40
+ export type SafeRootWriteOptions = {
41
+ encoding?: BufferEncoding;
42
+ mkdir?: boolean;
43
+ };
44
+ export type SafeRootOpenWritableOptions = {
45
+ mkdir?: boolean;
46
+ mode?: number;
47
+ truncateExisting?: boolean;
48
+ append?: boolean;
49
+ };
50
+ export type SafeRootCopyOptions = {
51
+ maxBytes?: number;
52
+ mkdir?: boolean;
53
+ rejectSourceHardlinks?: boolean;
54
+ };
55
+ export type SafeRootWriteJsonOptions = SafeRootWriteOptions & {
56
+ replacer?: Parameters<typeof JSON.stringify>[1];
57
+ space?: Parameters<typeof JSON.stringify>[2];
58
+ trailingNewline?: boolean;
59
+ };
60
+ export type SafeRootCreateOptions = SafeRootWriteOptions;
61
+ export type SafeRootCreateJsonOptions = SafeRootWriteJsonOptions;
62
+ export type SafeRootAppendOptions = {
63
+ encoding?: BufferEncoding;
64
+ mkdir?: boolean;
65
+ prependNewlineIfNeeded?: boolean;
66
+ };
67
+ export declare class SafeRoot {
68
+ readonly rootDir: string;
69
+ readonly rootReal: string;
70
+ readonly rootWithSep: string;
71
+ readonly defaults: SafeRootDefaults;
72
+ constructor(context: SafeRootContext, defaults?: SafeRootDefaults);
73
+ private get context();
74
+ resolve(relativePath: string): Promise<string>;
75
+ openFile(relativePath: string, options?: SafeRootOpenOptions): Promise<SafeOpenResult>;
76
+ readFile(relativePath: string, options?: SafeRootReadOptions): Promise<SafeLocalReadResult>;
77
+ readFileBytes(relativePath: string, options?: SafeRootReadOptions): Promise<Buffer>;
78
+ readText(relativePath: string, options?: SafeRootReadOptions & {
79
+ encoding?: BufferEncoding;
80
+ }): Promise<string>;
81
+ readJson<T = unknown>(relativePath: string, options?: SafeRootReadOptions & {
82
+ encoding?: BufferEncoding;
83
+ }): Promise<T>;
84
+ readPath(filePath: string, options?: Pick<SafeRootReadOptions, "rejectHardlinks" | "maxBytes">): Promise<SafeLocalReadResult>;
85
+ reader(options?: Pick<SafeRootReadOptions, "rejectHardlinks" | "maxBytes">): (filePath: string) => Promise<Buffer>;
86
+ openWritable(relativePath: string, options?: SafeRootOpenWritableOptions): Promise<SafeWritableOpenResult>;
87
+ append(relativePath: string, data: string | Buffer, options?: SafeRootAppendOptions): Promise<void>;
88
+ removeFile(relativePath: string): Promise<void>;
89
+ mkdirp(relativePath: string, options?: {
90
+ allowRoot?: boolean;
91
+ }): Promise<void>;
92
+ writeFile(relativePath: string, data: string | Buffer, options?: SafeRootWriteOptions): Promise<void>;
93
+ createFile(relativePath: string, data: string | Buffer, options?: SafeRootCreateOptions): Promise<boolean>;
94
+ writeJson(relativePath: string, data: unknown, options?: SafeRootWriteJsonOptions): Promise<void>;
95
+ createJson(relativePath: string, data: unknown, options?: SafeRootCreateJsonOptions): Promise<boolean>;
96
+ copyFileFrom(sourcePath: string, relativePath: string, options?: SafeRootCopyOptions): Promise<void>;
97
+ statPath(relativePath: string): Promise<SafePathStat>;
98
+ listDir(relativePath: string, options?: {
99
+ withFileTypes?: false;
100
+ }): Promise<string[]>;
101
+ listDir(relativePath: string, options: {
102
+ withFileTypes: true;
103
+ }): Promise<SafeDirEntry[]>;
104
+ move(from: string, to: string, options?: {
105
+ overwrite?: boolean;
106
+ }): Promise<void>;
107
+ }
108
+ export declare function safeRoot(rootDir: string, defaults?: SafeRootDefaults): Promise<SafeRoot>;
109
+ export declare function readLocalFileSafely(params: {
110
+ filePath: string;
111
+ maxBytes?: number;
112
+ }): Promise<SafeLocalReadResult>;
113
+ export declare function openLocalFileSafely(params: {
114
+ filePath: string;
115
+ }): Promise<SafeOpenResult>;
116
+ export type SafeWritableOpenResult = {
117
+ handle: FileHandle;
118
+ createdForWrite: boolean;
119
+ realPath: string;
120
+ stat: Stats;
121
+ };
122
+ export declare function resolveOpenedFileRealPathForHandle(handle: FileHandle, ioPath: string): Promise<string>;
123
+ //# sourceMappingURL=safe-root.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safe-root.d.ts","sourceRoot":"","sources":["../src/safe-root.ts"],"names":[],"mappings":"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"}