@openclaw/fs-safe 0.2.4 → 0.2.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.
Files changed (90) hide show
  1. package/CHANGELOG.md +15 -1
  2. package/README.md +2 -0
  3. package/dist/archive-staging.d.ts.map +1 -1
  4. package/dist/archive-staging.js +12 -1
  5. package/dist/file-store.d.ts.map +1 -1
  6. package/dist/file-store.js +6 -5
  7. package/dist/json-document-store.d.ts +1 -1
  8. package/dist/json-document-store.d.ts.map +1 -1
  9. package/dist/json-document-store.js +5 -3
  10. package/dist/json-durable-queue.d.ts.map +1 -1
  11. package/dist/json-durable-queue.js +157 -4
  12. package/dist/json-stringify.d.ts +2 -0
  13. package/dist/json-stringify.d.ts.map +1 -0
  14. package/dist/json-stringify.js +7 -0
  15. package/dist/json.d.ts.map +1 -1
  16. package/dist/json.js +3 -2
  17. package/dist/local-roots.d.ts.map +1 -1
  18. package/dist/local-roots.js +4 -1
  19. package/dist/path.js +1 -1
  20. package/dist/permissions.d.ts.map +1 -1
  21. package/dist/permissions.js +7 -10
  22. package/dist/pinned-python.d.ts.map +1 -1
  23. package/dist/pinned-python.js +125 -43
  24. package/dist/root-impl.d.ts.map +1 -1
  25. package/dist/root-impl.js +6 -2
  26. package/dist/root-paths.js +25 -3
  27. package/dist/secure-file.d.ts.map +1 -1
  28. package/dist/secure-file.js +3 -1
  29. package/dist/timing.d.ts.map +1 -1
  30. package/dist/timing.js +8 -1
  31. package/docs/index.md +1 -1
  32. package/docs/json-store.md +3 -1
  33. package/package.json +4 -4
  34. package/dist/archive-utils.d.ts +0 -3
  35. package/dist/archive-utils.d.ts.map +0 -1
  36. package/dist/archive-utils.js +0 -26
  37. package/dist/boundary-file-read.d.ts +0 -44
  38. package/dist/boundary-file-read.d.ts.map +0 -1
  39. package/dist/boundary-file-read.js +0 -129
  40. package/dist/boundary-path.d.ts +0 -39
  41. package/dist/boundary-path.d.ts.map +0 -1
  42. package/dist/boundary-path.js +0 -598
  43. package/dist/fs-pinned-path-helper.d.ts +0 -7
  44. package/dist/fs-pinned-path-helper.d.ts.map +0 -1
  45. package/dist/fs-pinned-path-helper.js +0 -182
  46. package/dist/fs-pinned-write-helper.d.ts +0 -21
  47. package/dist/fs-pinned-write-helper.d.ts.map +0 -1
  48. package/dist/fs-pinned-write-helper.js +0 -263
  49. package/dist/hardlink-guards.d.ts +0 -7
  50. package/dist/hardlink-guards.d.ts.map +0 -1
  51. package/dist/hardlink-guards.js +0 -30
  52. package/dist/install-safe-path.d.ts +0 -20
  53. package/dist/install-safe-path.d.ts.map +0 -1
  54. package/dist/install-safe-path.js +0 -94
  55. package/dist/json-file.d.ts +0 -3
  56. package/dist/json-file.d.ts.map +0 -1
  57. package/dist/json-file.js +0 -123
  58. package/dist/json-files.d.ts +0 -20
  59. package/dist/json-files.d.ts.map +0 -1
  60. package/dist/json-files.js +0 -153
  61. package/dist/path-alias-guards.d.ts +0 -19
  62. package/dist/path-alias-guards.d.ts.map +0 -1
  63. package/dist/path-alias-guards.js +0 -21
  64. package/dist/path-guards.d.ts +0 -7
  65. package/dist/path-guards.d.ts.map +0 -1
  66. package/dist/path-guards.js +0 -49
  67. package/dist/path-safety.d.ts +0 -12
  68. package/dist/path-safety.d.ts.map +0 -1
  69. package/dist/path-safety.js +0 -50
  70. package/dist/path-scope.d.ts +0 -2
  71. package/dist/path-scope.d.ts.map +0 -1
  72. package/dist/path-scope.js +0 -1
  73. package/dist/private-file-store.d.ts +0 -60
  74. package/dist/private-file-store.d.ts.map +0 -1
  75. package/dist/private-file-store.js +0 -233
  76. package/dist/safe-open-sync.d.ts +0 -24
  77. package/dist/safe-open-sync.d.ts.map +0 -1
  78. package/dist/safe-open-sync.js +0 -71
  79. package/dist/safe-root.d.ts +0 -123
  80. package/dist/safe-root.d.ts.map +0 -1
  81. package/dist/safe-root.js +0 -1060
  82. package/dist/secure-temp-workspace.d.ts +0 -25
  83. package/dist/secure-temp-workspace.d.ts.map +0 -1
  84. package/dist/secure-temp-workspace.js +0 -136
  85. package/dist/sibling-temp-file.d.ts +0 -16
  86. package/dist/sibling-temp-file.d.ts.map +0 -1
  87. package/dist/sibling-temp-file.js +0 -73
  88. package/dist/sibling-temp-write.d.ts +0 -8
  89. package/dist/sibling-temp-write.d.ts.map +0 -1
  90. package/dist/sibling-temp-write.js +0 -40
package/dist/json-file.js DELETED
@@ -1,123 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- import fs from "node:fs";
3
- import path from "node:path";
4
- const JSON_FILE_MODE = 0o600;
5
- const JSON_DIR_MODE = 0o700;
6
- function trySetSecureMode(pathname) {
7
- try {
8
- fs.chmodSync(pathname, JSON_FILE_MODE);
9
- }
10
- catch {
11
- // best-effort on platforms without chmod support
12
- }
13
- }
14
- function trySyncDirectory(pathname) {
15
- let fd;
16
- try {
17
- fd = fs.openSync(path.dirname(pathname), "r");
18
- fs.fsyncSync(fd);
19
- }
20
- catch {
21
- // best-effort; some platforms/filesystems do not support syncing directories.
22
- }
23
- finally {
24
- if (fd !== undefined) {
25
- try {
26
- fs.closeSync(fd);
27
- }
28
- catch {
29
- // best-effort cleanup
30
- }
31
- }
32
- }
33
- }
34
- function readSymlinkTargetPath(linkPath) {
35
- const target = fs.readlinkSync(linkPath);
36
- return path.resolve(path.dirname(linkPath), target);
37
- }
38
- function resolveJsonWriteTarget(pathname) {
39
- let currentPath = pathname;
40
- const visited = new Set();
41
- let followsSymlink = false;
42
- for (;;) {
43
- let stat;
44
- try {
45
- stat = fs.lstatSync(currentPath);
46
- }
47
- catch (error) {
48
- if (error.code !== "ENOENT") {
49
- throw error;
50
- }
51
- return { targetPath: currentPath, followsSymlink };
52
- }
53
- if (!stat.isSymbolicLink()) {
54
- return { targetPath: currentPath, followsSymlink };
55
- }
56
- if (visited.has(currentPath)) {
57
- const err = new Error(`Too many symlink levels while resolving ${pathname}`);
58
- err.code = "ELOOP";
59
- throw err;
60
- }
61
- visited.add(currentPath);
62
- followsSymlink = true;
63
- currentPath = readSymlinkTargetPath(currentPath);
64
- }
65
- }
66
- function renameJsonFileWithFallback(tmpPath, pathname) {
67
- try {
68
- fs.renameSync(tmpPath, pathname);
69
- return;
70
- }
71
- catch (error) {
72
- const code = error.code;
73
- // Windows does not reliably support rename-based overwrite for existing files.
74
- if (code === "EPERM" || code === "EEXIST") {
75
- fs.copyFileSync(tmpPath, pathname);
76
- fs.rmSync(tmpPath, { force: true });
77
- return;
78
- }
79
- throw error;
80
- }
81
- }
82
- function writeTempJsonFile(pathname, payload) {
83
- const fd = fs.openSync(pathname, "w", JSON_FILE_MODE);
84
- try {
85
- fs.writeFileSync(fd, payload, "utf8");
86
- fs.fsyncSync(fd);
87
- }
88
- finally {
89
- fs.closeSync(fd);
90
- }
91
- }
92
- export function loadJsonFile(pathname) {
93
- try {
94
- const raw = fs.readFileSync(pathname, "utf8");
95
- return JSON.parse(raw);
96
- }
97
- catch {
98
- return undefined;
99
- }
100
- }
101
- export function saveJsonFile(pathname, data) {
102
- const { targetPath, followsSymlink } = resolveJsonWriteTarget(pathname);
103
- const tmpPath = `${targetPath}.${randomUUID()}.tmp`;
104
- const payload = `${JSON.stringify(data, null, 2)}\n`;
105
- if (!followsSymlink) {
106
- fs.mkdirSync(path.dirname(targetPath), { recursive: true, mode: JSON_DIR_MODE });
107
- }
108
- try {
109
- writeTempJsonFile(tmpPath, payload);
110
- trySetSecureMode(tmpPath);
111
- renameJsonFileWithFallback(tmpPath, targetPath);
112
- trySetSecureMode(targetPath);
113
- trySyncDirectory(targetPath);
114
- }
115
- finally {
116
- try {
117
- fs.rmSync(tmpPath, { force: true });
118
- }
119
- catch {
120
- // best-effort cleanup when rename does not happen
121
- }
122
- }
123
- }
@@ -1,20 +0,0 @@
1
- export declare class JsonFileReadError extends Error {
2
- readonly filePath: string;
3
- readonly reason: "read" | "parse";
4
- constructor(filePath: string, reason: "read" | "parse", cause: unknown);
5
- }
6
- export declare function readJsonFile<T>(filePath: string): Promise<T | null>;
7
- export declare function readDurableJsonFile<T>(filePath: string): Promise<T | null>;
8
- export declare function readJsonFileSync(filePath: string): unknown;
9
- export declare function writeJsonAtomic(filePath: string, value: unknown, options?: {
10
- mode?: number;
11
- trailingNewline?: boolean;
12
- ensureDirMode?: number;
13
- }): Promise<void>;
14
- export declare function writeTextAtomic(filePath: string, content: string, options?: {
15
- mode?: number;
16
- ensureDirMode?: number;
17
- appendTrailingNewline?: boolean;
18
- }): Promise<void>;
19
- export declare function createAsyncLock(): <T>(fn: () => Promise<T>) => Promise<T>;
20
- //# sourceMappingURL=json-files.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"json-files.d.ts","sourceRoot":"","sources":["../src/json-files.ts"],"names":[],"mappings":"AASA,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;gBAEtB,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO;CAMvE;AA6BD,wBAAsB,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAOzE;AAED,wBAAsB,mBAAmB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAehF;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAO1D;AAED,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,EACd,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,iBAQ/E;AAED,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,qBAAqB,CAAC,EAAE,OAAO,CAAA;CAAE,iBA4CrF;AAED,wBAAgB,eAAe,KAEE,CAAC,EAAE,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC,KAAG,OAAO,CAAC,CAAC,CAAC,CAapE"}
@@ -1,153 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- import { readFileSync } from "node:fs";
3
- import fs from "node:fs/promises";
4
- import path from "node:path";
5
- function getErrorCode(err) {
6
- return err instanceof Error ? err.code : undefined;
7
- }
8
- export class JsonFileReadError extends Error {
9
- filePath;
10
- reason;
11
- constructor(filePath, reason, cause) {
12
- super(`Failed to ${reason} JSON file: ${filePath}`, { cause });
13
- this.name = "JsonFileReadError";
14
- this.filePath = filePath;
15
- this.reason = reason;
16
- }
17
- }
18
- async function replaceFileWithWindowsFallback(tempPath, filePath, mode) {
19
- try {
20
- await fs.rename(tempPath, filePath);
21
- return;
22
- }
23
- catch (err) {
24
- const code = getErrorCode(err);
25
- if (process.platform !== "win32" || (code !== "EPERM" && code !== "EEXIST")) {
26
- throw err;
27
- }
28
- }
29
- const existing = await fs.lstat(filePath).catch(() => null);
30
- if (existing?.isSymbolicLink()) {
31
- await fs.rm(filePath, { force: true });
32
- await fs.rename(tempPath, filePath);
33
- return;
34
- }
35
- await fs.copyFile(tempPath, filePath);
36
- try {
37
- await fs.chmod(filePath, mode);
38
- }
39
- catch {
40
- // best-effort; ignore on platforms without chmod
41
- }
42
- await fs.rm(tempPath, { force: true }).catch(() => undefined);
43
- }
44
- export async function readJsonFile(filePath) {
45
- try {
46
- const raw = await fs.readFile(filePath, "utf8");
47
- return JSON.parse(raw);
48
- }
49
- catch {
50
- return null;
51
- }
52
- }
53
- export async function readDurableJsonFile(filePath) {
54
- let raw;
55
- try {
56
- raw = await fs.readFile(filePath, "utf8");
57
- }
58
- catch (err) {
59
- if (getErrorCode(err) === "ENOENT") {
60
- return null;
61
- }
62
- throw new JsonFileReadError(filePath, "read", err);
63
- }
64
- try {
65
- return JSON.parse(raw);
66
- }
67
- catch (err) {
68
- throw new JsonFileReadError(filePath, "parse", err);
69
- }
70
- }
71
- export function readJsonFileSync(filePath) {
72
- try {
73
- const raw = readFileSync(filePath, "utf8");
74
- return JSON.parse(raw);
75
- }
76
- catch {
77
- return null;
78
- }
79
- }
80
- export async function writeJsonAtomic(filePath, value, options) {
81
- const text = JSON.stringify(value, null, 2);
82
- await writeTextAtomic(filePath, text, {
83
- mode: options?.mode,
84
- ensureDirMode: options?.ensureDirMode,
85
- appendTrailingNewline: options?.trailingNewline,
86
- });
87
- }
88
- export async function writeTextAtomic(filePath, content, options) {
89
- const mode = options?.mode ?? 0o600;
90
- const payload = options?.appendTrailingNewline && !content.endsWith("\n") ? `${content}\n` : content;
91
- const mkdirOptions = { recursive: true };
92
- if (typeof options?.ensureDirMode === "number") {
93
- mkdirOptions.mode = options.ensureDirMode;
94
- }
95
- await fs.mkdir(path.dirname(filePath), mkdirOptions);
96
- const parentDir = path.dirname(filePath);
97
- const tmp = `${filePath}.${randomUUID()}.tmp`;
98
- try {
99
- const tmpHandle = await fs.open(tmp, "w", mode);
100
- try {
101
- await tmpHandle.writeFile(payload, { encoding: "utf8" });
102
- await tmpHandle.sync();
103
- }
104
- finally {
105
- await tmpHandle.close().catch(() => undefined);
106
- }
107
- try {
108
- await fs.chmod(tmp, mode);
109
- }
110
- catch {
111
- // best-effort; ignore on platforms without chmod
112
- }
113
- await replaceFileWithWindowsFallback(tmp, filePath, mode);
114
- try {
115
- const dirHandle = await fs.open(parentDir, "r");
116
- try {
117
- await dirHandle.sync();
118
- }
119
- finally {
120
- await dirHandle.close().catch(() => undefined);
121
- }
122
- }
123
- catch {
124
- // best-effort; some platforms/filesystems do not support syncing directories.
125
- }
126
- try {
127
- await fs.chmod(filePath, mode);
128
- }
129
- catch {
130
- // best-effort; ignore on platforms without chmod
131
- }
132
- }
133
- finally {
134
- await fs.rm(tmp, { force: true }).catch(() => undefined);
135
- }
136
- }
137
- export function createAsyncLock() {
138
- let lock = Promise.resolve();
139
- return async function withLock(fn) {
140
- const prev = lock;
141
- let release;
142
- lock = new Promise((resolve) => {
143
- release = resolve;
144
- });
145
- await prev;
146
- try {
147
- return await fn();
148
- }
149
- finally {
150
- release?.();
151
- }
152
- };
153
- }
@@ -1,19 +0,0 @@
1
- import { type RootPathAliasPolicy } from "./root-path.js";
2
- export type PathAliasPolicy = RootPathAliasPolicy;
3
- export declare const PATH_ALIAS_POLICIES: {
4
- readonly strict: Readonly<{
5
- allowFinalSymlinkForUnlink: false;
6
- allowFinalHardlinkForUnlink: false;
7
- }>;
8
- readonly unlinkTarget: Readonly<{
9
- allowFinalSymlinkForUnlink: true;
10
- allowFinalHardlinkForUnlink: true;
11
- }>;
12
- };
13
- export declare function assertNoPathAliasEscape(params: {
14
- absolutePath: string;
15
- rootPath: string;
16
- boundaryLabel: string;
17
- policy?: PathAliasPolicy;
18
- }): Promise<void>;
19
- //# sourceMappingURL=path-alias-guards.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"path-alias-guards.d.ts","sourceRoot":"","sources":["../src/path-alias-guards.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,gBAAgB,CAAC;AAGxB,MAAM,MAAM,eAAe,GAAG,mBAAmB,CAAC;AAElD,eAAO,MAAM,mBAAmB;;;;;;;;;CAA2B,CAAC;AAE5D,wBAAsB,uBAAuB,CAAC,MAAM,EAAE;IACpD,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBhB"}
@@ -1,21 +0,0 @@
1
- import { ROOT_PATH_ALIAS_POLICIES, resolveRootPath, } from "./root-path.js";
2
- import { assertNoHardlinkedFinalPath } from "./hardlink-guards.js";
3
- export const PATH_ALIAS_POLICIES = ROOT_PATH_ALIAS_POLICIES;
4
- export async function assertNoPathAliasEscape(params) {
5
- const resolved = await resolveRootPath({
6
- absolutePath: params.absolutePath,
7
- rootPath: params.rootPath,
8
- boundaryLabel: params.boundaryLabel,
9
- policy: params.policy,
10
- });
11
- const allowFinalSymlink = params.policy?.allowFinalSymlinkForUnlink === true;
12
- if (allowFinalSymlink && resolved.kind === "symlink") {
13
- return;
14
- }
15
- await assertNoHardlinkedFinalPath({
16
- filePath: resolved.absolutePath,
17
- root: resolved.rootPath,
18
- boundaryLabel: params.boundaryLabel,
19
- allowFinalHardlinkForUnlink: params.policy?.allowFinalHardlinkForUnlink,
20
- });
21
- }
@@ -1,7 +0,0 @@
1
- export declare function normalizeWindowsPathForComparison(input: string): string;
2
- export declare function isNodeError(value: unknown): value is NodeJS.ErrnoException;
3
- export declare function hasNodeErrorCode(value: unknown, code: string): boolean;
4
- export declare function isNotFoundPathError(value: unknown): boolean;
5
- export declare function isSymlinkOpenError(value: unknown): boolean;
6
- export declare function isPathInside(root: string, target: string): boolean;
7
- //# sourceMappingURL=path-guards.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"path-guards.d.ts","sourceRoot":"","sources":["../src/path-guards.ts"],"names":[],"mappings":"AAQA,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CASvE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,cAAc,CAI1E;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE3D;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE1D;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CA0BlE"}
@@ -1,49 +0,0 @@
1
- import path from "node:path";
2
- import { normalizeLowercaseStringOrEmpty } from "./string-coerce.js";
3
- const NOT_FOUND_CODES = new Set(["ENOENT", "ENOTDIR"]);
4
- const SYMLINK_OPEN_CODES = new Set(["ELOOP", "EINVAL", "ENOTSUP"]);
5
- const PARENT_SEGMENT_PREFIX = /^\.\.(?:[\\/]|$)/u;
6
- const POSIX_SEPARATOR_CHAR_CODE = 0x2f;
7
- export function normalizeWindowsPathForComparison(input) {
8
- let normalized = path.win32.normalize(input);
9
- if (normalized.startsWith("\\\\?\\")) {
10
- normalized = normalized.slice(4);
11
- if (normalized.toUpperCase().startsWith("UNC\\")) {
12
- normalized = `\\\\${normalized.slice(4)}`;
13
- }
14
- }
15
- return normalizeLowercaseStringOrEmpty(normalized.replaceAll("/", "\\"));
16
- }
17
- export function isNodeError(value) {
18
- return Boolean(value && typeof value === "object" && "code" in value);
19
- }
20
- export function hasNodeErrorCode(value, code) {
21
- return isNodeError(value) && value.code === code;
22
- }
23
- export function isNotFoundPathError(value) {
24
- return isNodeError(value) && typeof value.code === "string" && NOT_FOUND_CODES.has(value.code);
25
- }
26
- export function isSymlinkOpenError(value) {
27
- return isNodeError(value) && typeof value.code === "string" && SYMLINK_OPEN_CODES.has(value.code);
28
- }
29
- export function isPathInside(root, target) {
30
- if (process.platform === "win32") {
31
- const rootForCompare = normalizeWindowsPathForComparison(path.win32.resolve(root));
32
- const targetForCompare = normalizeWindowsPathForComparison(path.win32.resolve(target));
33
- const relative = path.win32.relative(rootForCompare, targetForCompare);
34
- return (relative === "" || (!PARENT_SEGMENT_PREFIX.test(relative) && !path.win32.isAbsolute(relative)));
35
- }
36
- if (root.length > 0 &&
37
- root.charCodeAt(0) === POSIX_SEPARATOR_CHAR_CODE &&
38
- target.length >= root.length &&
39
- target.charCodeAt(0) === POSIX_SEPARATOR_CHAR_CODE &&
40
- !target.includes("/..") &&
41
- (target === root ||
42
- (target.startsWith(root) && target.charCodeAt(root.length) === POSIX_SEPARATOR_CHAR_CODE))) {
43
- return true;
44
- }
45
- const resolvedRoot = path.resolve(root);
46
- const resolvedTarget = path.resolve(target);
47
- const relative = path.relative(resolvedRoot, resolvedTarget);
48
- return relative === "" || (!PARENT_SEGMENT_PREFIX.test(relative) && !path.isAbsolute(relative));
49
- }
@@ -1,12 +0,0 @@
1
- import fs from "node:fs";
2
- export declare function resolveSafeBaseDir(rootDir: string): string;
3
- export declare function isWithinDir(rootDir: string, targetPath: string): boolean;
4
- export declare function isPathInside(baseDir: string, targetPath: string): boolean;
5
- export declare function safeRealpathSync(targetPath: string, cache?: Map<string, string>): string | null;
6
- export declare function isPathInsideWithRealpath(basePath: string, candidatePath: string, opts?: {
7
- requireRealpath?: boolean;
8
- cache?: Map<string, string>;
9
- }): boolean;
10
- export declare function safeStatSync(targetPath: string): fs.Stats | null;
11
- export declare function formatPosixMode(mode: number): string;
12
- //# sourceMappingURL=path-safety.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"path-safety.d.ts","sourceRoot":"","sources":["../src/path-safety.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAIzB,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAExE;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAEzE;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,GAAG,IAAI,CAa/F;AAED,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,IAAI,CAAC,EAAE;IAAE,eAAe,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAChE,OAAO,CAUT;AAED,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,EAAE,CAAC,KAAK,GAAG,IAAI,CAMhE;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD"}
@@ -1,50 +0,0 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import { isPathInside as isRootPathInside } from "./path-guards.js";
4
- export function resolveSafeBaseDir(rootDir) {
5
- const resolved = path.resolve(rootDir);
6
- return resolved.endsWith(path.sep) ? resolved : `${resolved}${path.sep}`;
7
- }
8
- export function isWithinDir(rootDir, targetPath) {
9
- return isRootPathInside(rootDir, targetPath);
10
- }
11
- export function isPathInside(baseDir, targetPath) {
12
- return isRootPathInside(baseDir, targetPath);
13
- }
14
- export function safeRealpathSync(targetPath, cache) {
15
- const cached = cache?.get(targetPath);
16
- if (cached) {
17
- return cached;
18
- }
19
- try {
20
- const resolved = fs.realpathSync(targetPath);
21
- cache?.set(targetPath, resolved);
22
- cache?.set(resolved, resolved);
23
- return resolved;
24
- }
25
- catch {
26
- return null;
27
- }
28
- }
29
- export function isPathInsideWithRealpath(basePath, candidatePath, opts) {
30
- if (!isPathInside(basePath, candidatePath)) {
31
- return false;
32
- }
33
- const baseReal = safeRealpathSync(basePath, opts?.cache);
34
- const candidateReal = safeRealpathSync(candidatePath, opts?.cache);
35
- if (!baseReal || !candidateReal) {
36
- return opts?.requireRealpath === false;
37
- }
38
- return isPathInside(baseReal, candidateReal);
39
- }
40
- export function safeStatSync(targetPath) {
41
- try {
42
- return fs.statSync(targetPath);
43
- }
44
- catch {
45
- return null;
46
- }
47
- }
48
- export function formatPosixMode(mode) {
49
- return (mode & 0o777).toString(8).padStart(3, "0");
50
- }
@@ -1,2 +0,0 @@
1
- export { pathScope, type PathScope, type PathScopeOptions, type PathScopeResolveOptions, } from "./root-paths.js";
2
- //# sourceMappingURL=path-scope.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"path-scope.d.ts","sourceRoot":"","sources":["../src/path-scope.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,KAAK,SAAS,EACd,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,GAC7B,MAAM,iBAAiB,CAAC"}
@@ -1 +0,0 @@
1
- export { pathScope, } from "./root-paths.js";
@@ -1,60 +0,0 @@
1
- import { type FileStore } from "./file-store.js";
2
- export type PrivateStateStoreOptions = {
3
- rootDir: string;
4
- };
5
- export type PrivateStateStore = Omit<FileStore, "readText" | "readJson" | "writeText" | "writeJson"> & {
6
- readText(relativePath: string, options?: {
7
- maxBytes?: number;
8
- }): Promise<string | null>;
9
- readJson<T = unknown>(relativePath: string, options?: {
10
- maxBytes?: number;
11
- }): Promise<T | null>;
12
- writeText(relativePath: string, content: string | Uint8Array): Promise<string>;
13
- writeJson(relativePath: string, value: unknown, options?: {
14
- trailingNewline?: boolean;
15
- }): Promise<string>;
16
- };
17
- export declare function writePrivateTextAtomic(params: {
18
- rootDir: string;
19
- filePath: string;
20
- content: string | Uint8Array;
21
- }): Promise<void>;
22
- export declare function readPrivateText(params: {
23
- rootDir: string;
24
- filePath: string;
25
- maxBytes?: number;
26
- }): Promise<string | null>;
27
- export declare function readPrivateTextSync(params: {
28
- rootDir: string;
29
- filePath: string;
30
- maxBytes?: number;
31
- }): string | null;
32
- export declare function readPrivateJson<T = unknown>(params: {
33
- rootDir: string;
34
- filePath: string;
35
- maxBytes?: number;
36
- }): Promise<T | null>;
37
- export declare function readPrivateJsonSync<T = unknown>(params: {
38
- rootDir: string;
39
- filePath: string;
40
- maxBytes?: number;
41
- }): T | null;
42
- export declare function writePrivateTextAtomicSync(params: {
43
- rootDir: string;
44
- filePath: string;
45
- content: string | Uint8Array;
46
- }): void;
47
- export declare function writePrivateJsonAtomic(params: {
48
- rootDir: string;
49
- filePath: string;
50
- value: unknown;
51
- trailingNewline?: boolean;
52
- }): Promise<void>;
53
- export declare function writePrivateJsonAtomicSync(params: {
54
- rootDir: string;
55
- filePath: string;
56
- value: unknown;
57
- trailingNewline?: boolean;
58
- }): void;
59
- export declare function privateStateStore(options: PrivateStateStoreOptions): PrivateStateStore;
60
- //# sourceMappingURL=private-file-store.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"private-file-store.d.ts","sourceRoot":"","sources":["../src/private-file-store.ts"],"names":[],"mappings":"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"}