@openclaw/fs-safe 0.4.7 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +82 -43
  3. package/dist/archive-entry.d.ts.map +1 -1
  4. package/dist/archive-entry.js +5 -4
  5. package/dist/archive-errors.d.ts +11 -0
  6. package/dist/archive-errors.d.ts.map +1 -0
  7. package/dist/archive-errors.js +16 -0
  8. package/dist/archive-input.d.ts +12 -0
  9. package/dist/archive-input.d.ts.map +1 -0
  10. package/dist/archive-input.js +72 -0
  11. package/dist/archive-kind.d.ts +1 -1
  12. package/dist/archive-kind.d.ts.map +1 -1
  13. package/dist/archive-kind.js +18 -0
  14. package/dist/archive-limits.d.ts +10 -0
  15. package/dist/archive-limits.d.ts.map +1 -1
  16. package/dist/archive-limits.js +18 -0
  17. package/dist/archive-native.d.ts +18 -0
  18. package/dist/archive-native.d.ts.map +1 -0
  19. package/dist/archive-native.js +115 -0
  20. package/dist/archive-options.d.ts +21 -0
  21. package/dist/archive-options.d.ts.map +1 -0
  22. package/dist/archive-options.js +1 -0
  23. package/dist/archive-policy.d.ts +20 -0
  24. package/dist/archive-policy.d.ts.map +1 -0
  25. package/dist/archive-policy.js +29 -0
  26. package/dist/archive-read.d.ts +6 -0
  27. package/dist/archive-read.d.ts.map +1 -0
  28. package/dist/archive-read.js +200 -0
  29. package/dist/archive-staging.d.ts +2 -5
  30. package/dist/archive-staging.d.ts.map +1 -1
  31. package/dist/archive-staging.js +19 -10
  32. package/dist/archive-tar-meta.d.ts +6 -0
  33. package/dist/archive-tar-meta.d.ts.map +1 -0
  34. package/dist/archive-tar-meta.js +143 -0
  35. package/dist/archive-tar-runtime.d.ts +43 -0
  36. package/dist/archive-tar-runtime.d.ts.map +1 -0
  37. package/dist/archive-tar-runtime.js +8 -0
  38. package/dist/archive-tar.d.ts +5 -1
  39. package/dist/archive-tar.d.ts.map +1 -1
  40. package/dist/archive-tar.js +24 -6
  41. package/dist/archive-zip-entry.d.ts +15 -0
  42. package/dist/archive-zip-entry.d.ts.map +1 -0
  43. package/dist/archive-zip-entry.js +17 -0
  44. package/dist/archive.d.ts +7 -17
  45. package/dist/archive.d.ts.map +1 -1
  46. package/dist/archive.js +111 -108
  47. package/dist/atomic.d.ts +1 -1
  48. package/dist/atomic.d.ts.map +1 -1
  49. package/dist/config.d.ts +1 -1
  50. package/dist/config.d.ts.map +1 -1
  51. package/dist/config.js +1 -1
  52. package/dist/containment.d.ts +2 -0
  53. package/dist/containment.d.ts.map +1 -0
  54. package/dist/containment.js +1 -0
  55. package/dist/durability.d.ts +2 -0
  56. package/dist/durability.d.ts.map +1 -1
  57. package/dist/durability.js +2 -0
  58. package/dist/errors.d.ts +4 -1
  59. package/dist/errors.d.ts.map +1 -1
  60. package/dist/errors.js +2 -0
  61. package/dist/file-hash.d.ts +10 -0
  62. package/dist/file-hash.d.ts.map +1 -0
  63. package/dist/file-hash.js +73 -0
  64. package/dist/file-lock-sync.d.ts +35 -0
  65. package/dist/file-lock-sync.d.ts.map +1 -0
  66. package/dist/file-lock-sync.js +233 -0
  67. package/dist/file-lock.d.ts +2 -0
  68. package/dist/file-lock.d.ts.map +1 -1
  69. package/dist/file-lock.js +1 -0
  70. package/dist/filename.d.ts.map +1 -1
  71. package/dist/filename.js +4 -1
  72. package/dist/guarded-mkdir.d.ts +7 -1
  73. package/dist/guarded-mkdir.d.ts.map +1 -1
  74. package/dist/guarded-mkdir.js +45 -3
  75. package/dist/index.d.ts +4 -3
  76. package/dist/index.d.ts.map +1 -1
  77. package/dist/index.js +1 -1
  78. package/dist/json-document-store.d.ts.map +1 -1
  79. package/dist/json-document-store.js +30 -15
  80. package/dist/move-path.d.ts.map +1 -1
  81. package/dist/move-path.js +101 -17
  82. package/dist/native/darwin-arm64/fs-safe-native.node +0 -0
  83. package/dist/native/darwin-x64/fs-safe-native.node +0 -0
  84. package/dist/native/linux-arm64-gnu/fs-safe-native.node +0 -0
  85. package/dist/native/linux-arm64-musl/fs-safe-native.node +0 -0
  86. package/dist/native/linux-x64-gnu/fs-safe-native.node +0 -0
  87. package/dist/native/linux-x64-musl/fs-safe-native.node +0 -0
  88. package/dist/native/win32-x64-msvc/fs-safe-native.node +0 -0
  89. package/dist/native-binding.d.ts +79 -0
  90. package/dist/native-binding.d.ts.map +1 -0
  91. package/dist/native-binding.js +1 -0
  92. package/dist/native-config.d.ts +17 -0
  93. package/dist/native-config.d.ts.map +1 -0
  94. package/dist/native-config.js +71 -0
  95. package/dist/native-operations.d.ts +21 -0
  96. package/dist/native-operations.d.ts.map +1 -0
  97. package/dist/native-operations.js +115 -0
  98. package/dist/native-pinned-write.d.ts +5 -0
  99. package/dist/native-pinned-write.d.ts.map +1 -0
  100. package/dist/native-pinned-write.js +115 -0
  101. package/dist/native.d.ts +14 -0
  102. package/dist/native.d.ts.map +1 -0
  103. package/dist/native.js +204 -0
  104. package/dist/output-sibling.d.ts +8 -0
  105. package/dist/output-sibling.d.ts.map +1 -0
  106. package/dist/output-sibling.js +122 -0
  107. package/dist/output.d.ts +2 -0
  108. package/dist/output.d.ts.map +1 -1
  109. package/dist/output.js +32 -5
  110. package/dist/owner-dacl.d.ts +31 -0
  111. package/dist/owner-dacl.d.ts.map +1 -0
  112. package/dist/owner-dacl.js +27 -0
  113. package/dist/path-policy.js +1 -1
  114. package/dist/permissions-public.d.ts +2 -0
  115. package/dist/permissions-public.d.ts.map +1 -1
  116. package/dist/permissions-public.js +2 -0
  117. package/dist/permissions.d.ts.map +1 -1
  118. package/dist/permissions.js +6 -0
  119. package/dist/pinned-operation.d.ts +2 -0
  120. package/dist/pinned-operation.d.ts.map +1 -0
  121. package/dist/pinned-operation.js +35 -0
  122. package/dist/pinned-write.d.ts +3 -16
  123. package/dist/pinned-write.d.ts.map +1 -1
  124. package/dist/pinned-write.js +20 -85
  125. package/dist/private-directory.d.ts +5 -0
  126. package/dist/private-directory.d.ts.map +1 -0
  127. package/dist/private-directory.js +13 -0
  128. package/dist/private-temp-workspace.d.ts +6 -2
  129. package/dist/private-temp-workspace.d.ts.map +1 -1
  130. package/dist/private-temp-workspace.js +40 -13
  131. package/dist/publish-file-failure.d.ts +27 -0
  132. package/dist/publish-file-failure.d.ts.map +1 -0
  133. package/dist/publish-file-failure.js +21 -0
  134. package/dist/publish-file.d.ts +21 -0
  135. package/dist/publish-file.d.ts.map +1 -0
  136. package/dist/publish-file.js +351 -0
  137. package/dist/read-opened-file.d.ts +3 -0
  138. package/dist/read-opened-file.d.ts.map +1 -1
  139. package/dist/read-opened-file.js +1 -0
  140. package/dist/replace-file-copy-fallback.d.ts +34 -0
  141. package/dist/replace-file-copy-fallback.d.ts.map +1 -0
  142. package/dist/replace-file-copy-fallback.js +339 -0
  143. package/dist/replace-file.d.ts +6 -2
  144. package/dist/replace-file.d.ts.map +1 -1
  145. package/dist/replace-file.js +39 -90
  146. package/dist/root-context.d.ts +4 -1
  147. package/dist/root-context.d.ts.map +1 -1
  148. package/dist/root-context.js +20 -1
  149. package/dist/root-impl.d.ts +5 -0
  150. package/dist/root-impl.d.ts.map +1 -1
  151. package/dist/root-impl.js +128 -225
  152. package/dist/root-path-existing.d.ts +3 -0
  153. package/dist/root-path-existing.d.ts.map +1 -0
  154. package/dist/root-path-existing.js +69 -0
  155. package/dist/root-path.d.ts +1 -2
  156. package/dist/root-path.d.ts.map +1 -1
  157. package/dist/root-path.js +67 -81
  158. package/dist/root-walk.d.ts +41 -0
  159. package/dist/root-walk.d.ts.map +1 -0
  160. package/dist/root-walk.js +114 -0
  161. package/dist/root.d.ts +2 -0
  162. package/dist/root.d.ts.map +1 -1
  163. package/dist/secret-file.d.ts +5 -2
  164. package/dist/secret-file.d.ts.map +1 -1
  165. package/dist/secret-file.js +20 -2
  166. package/dist/secret-read-async.d.ts +4 -0
  167. package/dist/secret-read-async.d.ts.map +1 -0
  168. package/dist/secret-read-async.js +95 -0
  169. package/dist/secret.d.ts +2 -1
  170. package/dist/secret.d.ts.map +1 -1
  171. package/dist/secret.js +2 -1
  172. package/dist/sibling-temp.d.ts.map +1 -1
  173. package/dist/sibling-temp.js +1 -0
  174. package/dist/sidecar-lock-handle.d.ts +20 -0
  175. package/dist/sidecar-lock-handle.d.ts.map +1 -0
  176. package/dist/sidecar-lock-handle.js +28 -0
  177. package/dist/sidecar-lock-policy.d.ts +10 -0
  178. package/dist/sidecar-lock-policy.d.ts.map +1 -0
  179. package/dist/sidecar-lock-policy.js +29 -0
  180. package/dist/sidecar-lock-reclaim.d.ts +22 -6
  181. package/dist/sidecar-lock-reclaim.d.ts.map +1 -1
  182. package/dist/sidecar-lock-reclaim.js +106 -20
  183. package/dist/sidecar-lock-types.d.ts +56 -0
  184. package/dist/sidecar-lock-types.d.ts.map +1 -0
  185. package/dist/sidecar-lock-types.js +1 -0
  186. package/dist/sidecar-lock.d.ts +2 -45
  187. package/dist/sidecar-lock.d.ts.map +1 -1
  188. package/dist/sidecar-lock.js +94 -55
  189. package/dist/temp-cleanup.d.ts +7 -1
  190. package/dist/temp-cleanup.d.ts.map +1 -1
  191. package/dist/temp-cleanup.js +35 -5
  192. package/dist/temp.d.ts +2 -1
  193. package/dist/temp.d.ts.map +1 -1
  194. package/dist/test-hooks.d.ts +3 -0
  195. package/dist/test-hooks.d.ts.map +1 -1
  196. package/dist/windows-permissions-native.d.ts +9 -0
  197. package/dist/windows-permissions-native.d.ts.map +1 -0
  198. package/dist/windows-permissions-native.js +31 -0
  199. package/docs/archive.md +131 -13
  200. package/docs/atomic.md +30 -1
  201. package/docs/config.md +51 -21
  202. package/docs/durability.md +211 -1
  203. package/docs/errors.md +36 -6
  204. package/docs/filename.md +0 -0
  205. package/docs/index.md +11 -8
  206. package/docs/install.md +27 -26
  207. package/docs/json-store.md +18 -3
  208. package/docs/migrating-to-0.5.md +196 -0
  209. package/docs/native-helper.md +85 -0
  210. package/docs/native.md +134 -0
  211. package/docs/output.md +37 -11
  212. package/docs/path.md +1 -1
  213. package/docs/permissions.md +84 -1
  214. package/docs/quickstart.md +5 -2
  215. package/docs/reading.md +4 -4
  216. package/docs/root.md +31 -15
  217. package/docs/secret-file.md +50 -1
  218. package/docs/security-model.md +29 -8
  219. package/docs/sidecar-lock.md +92 -7
  220. package/docs/temp.md +24 -1
  221. package/docs/test-hooks.md +22 -2
  222. package/docs/testing.md +18 -7
  223. package/docs/types.md +3 -1
  224. package/docs/walk.md +55 -0
  225. package/docs/writing.md +3 -3
  226. package/package.json +8 -2
  227. package/dist/pinned-helper.d.ts +0 -9
  228. package/dist/pinned-helper.d.ts.map +0 -1
  229. package/dist/pinned-helper.js +0 -19
  230. package/dist/pinned-path.d.ts +0 -7
  231. package/dist/pinned-path.d.ts.map +0 -1
  232. package/dist/pinned-path.js +0 -21
  233. package/dist/pinned-python-config.d.ts +0 -9
  234. package/dist/pinned-python-config.d.ts.map +0 -1
  235. package/dist/pinned-python-config.js +0 -38
  236. package/dist/pinned-python.d.ts +0 -12
  237. package/dist/pinned-python.d.ts.map +0 -1
  238. package/dist/pinned-python.js +0 -688
  239. package/docs/python-helper.md +0 -103
@@ -1,15 +1,18 @@
1
1
  import type { Stats } from "node:fs";
2
2
  import type { FileHandle } from "node:fs/promises";
3
+ import type { ContainmentGuarantee } from "./containment.js";
3
4
  import { type DenyMutationPolicy } from "./deny-mutations.js";
4
5
  import { type RenameIdentityPolicy } from "./pinned-write.js";
5
6
  import { type ReadResult } from "./read-opened-file.js";
6
7
  import type { DirEntry, PathStat } from "./types.js";
8
+ import { type RootWalkEntry, type RootWalkOptions } from "./root-walk.js";
7
9
  export type { DenyMutationPolicy } from "./deny-mutations.js";
8
10
  export type { RenameIdentityPolicy } from "./pinned-write.js";
9
11
  export { resolveOpenedFileRealPathForHandle } from "./opened-realpath.js";
10
12
  export type { ReadResult } from "./read-opened-file.js";
11
13
  export type OpenResult = {
12
14
  handle: FileHandle;
15
+ containment: ContainmentGuarantee;
13
16
  realPath: string;
14
17
  stat: Stats;
15
18
  [Symbol.asyncDispose](): Promise<void>;
@@ -95,6 +98,7 @@ export interface Root {
95
98
  withFileTypes: true;
96
99
  }): Promise<DirEntry[]>;
97
100
  move(fromRelative: string, toRelative: string, options?: RootMoveOptions): Promise<void>;
101
+ walk(relativePath: string, options: RootWalkOptions): AsyncIterableIterator<RootWalkEntry>;
98
102
  }
99
103
  export declare function root(rootDir: string, defaults?: RootDefaults): Promise<Root>;
100
104
  export declare function readLocalFileSafely(params: {
@@ -106,6 +110,7 @@ export declare function openLocalFileSafely(params: {
106
110
  }): Promise<OpenResult>;
107
111
  export type WritableOpenResult = {
108
112
  handle: FileHandle;
113
+ containment: ContainmentGuarantee;
109
114
  createdForWrite: boolean;
110
115
  realPath: string;
111
116
  stat: Stats;
@@ -1 +1 @@
1
- {"version":3,"file":"root-impl.d.ts","sourceRoot":"","sources":["../src/root-impl.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"root-impl.d.ts","sourceRoot":"","sources":["../src/root-impl.ts"],"names":[],"mappings":"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"}
package/dist/root-impl.js CHANGED
@@ -2,8 +2,6 @@ import { randomUUID } from "node:crypto";
2
2
  import { constants as fsConstants } from "node:fs";
3
3
  import fs from "node:fs/promises";
4
4
  import path from "node:path";
5
- import { pipeline } from "node:stream/promises";
6
- import { createBoundedReadStream } from "./bounded-read-stream.js";
7
5
  import { assertAsyncDirectoryGuard, createAsyncDirectoryGuard, createNearestExistingDirectoryGuard } from "./directory-guard.js";
8
6
  import { FsSafeError } from "./errors.js";
9
7
  import { syncDirectoryBestEffort } from "./fsync.js";
@@ -12,19 +10,18 @@ import { mkdirPathComponentsWithGuards } from "./guarded-mkdir.js";
12
10
  import { withAsyncDirectoryGuards } from "./guarded-mutation.js";
13
11
  import { assertMutationNotDenied, mergeDenyMutationPolicies, } from "./deny-mutations.js";
14
12
  import { resolveOpenedFileRealPathForHandle } from "./opened-realpath.js";
15
- import { isPinnedPathHelperSpawnError, runPinnedPathHelper } from "./pinned-path.js";
16
- import { runPinnedCopyHelper, runPinnedWriteWithRenamePolicy, } from "./pinned-write.js";
17
- import { canFallbackFromPythonError, getFsSafePythonConfig } from "./pinned-python-config.js";
13
+ import { runPinnedWriteHelper, runPinnedWriteWithRenamePolicy, } from "./pinned-write.js";
14
+ import { validatePinnedOperationPayload } from "./pinned-operation.js";
18
15
  import { assertNoPathAliasEscape, PATH_ALIAS_POLICIES } from "./path-policy.js";
19
16
  import { assertNoNulPathInput, assertNoUnsafeDeviceReadPath, hasNodeErrorCode, isNotFoundPathError, isPathInside, isSymlinkOpenError, } from "./path.js";
20
17
  import { readOpenedFileSafely } from "./read-opened-file.js";
21
- import { helperReaddir, helperStat, runPinnedHelper, } from "./pinned-helper.js";
22
18
  import { pathStatFromStats } from "./path-stat.js";
23
19
  import { resolveRootPath } from "./root-path.js";
24
20
  import { assertValidRootRelativePath, ensureTrailingSep, expandRelativePathWithHome, resolvePathInRoot, resolveRootContext, } from "./root-context.js";
25
21
  import { isAlreadyExistsError, normalizePinnedPathError, normalizePinnedWriteError, } from "./root-errors.js";
26
22
  import { getFsSafeTestHooks } from "./test-hooks.js";
27
23
  import { stringifyJsonDocument } from "./json-stringify.js";
24
+ import { walkRoot } from "./root-walk.js";
28
25
  import { registerTempPathForExit } from "./temp-cleanup.js";
29
26
  import { serializePathWrite } from "./write-queue.js";
30
27
  export { resolveOpenedFileRealPathForHandle } from "./opened-realpath.js";
@@ -54,6 +51,7 @@ export const DEFAULT_ROOT_MAX_BYTES = 16 * 1024 * 1024;
54
51
  function openResult(params) {
55
52
  return {
56
53
  handle: params.handle,
54
+ containment: "best-effort",
57
55
  realPath: params.realPath,
58
56
  stat: params.stat,
59
57
  [Symbol.asyncDispose]: () => params.handle.close().catch(() => undefined),
@@ -172,7 +170,7 @@ class RootHandle {
172
170
  };
173
171
  }
174
172
  async resolve(relativePath) {
175
- return (await resolvePathInRoot(this.context, relativePath)).resolved;
173
+ return (await resolvePathInRoot(this.context, relativePath, { allowFinalSymlink: true })).resolved;
176
174
  }
177
175
  async open(relativePath, options = {}) {
178
176
  return await openFileInRoot(this.context, {
@@ -311,58 +309,34 @@ class RootHandle {
311
309
  }
312
310
  async stat(relativePath) {
313
311
  assertValidRootRelativePath(relativePath);
314
- try {
315
- return await helperStat(this.rootReal, relativePath);
316
- }
317
- catch (error) {
318
- if (canFallbackFromPythonError(error)) {
319
- return await statPathFallback(this.context, relativePath);
320
- }
321
- throw error;
322
- }
312
+ validatePinnedOperationPayload({ relativePath });
313
+ return await statPathFallback(this.context, relativePath);
323
314
  }
324
315
  async list(relativePath, options = {}) {
325
316
  assertValidRootRelativePath(relativePath);
326
- try {
327
- return options.withFileTypes === true
328
- ? await helperReaddir(this.rootReal, relativePath, true)
329
- : await helperReaddir(this.rootReal, relativePath, false);
330
- }
331
- catch (error) {
332
- if (canFallbackFromPythonError(error)) {
333
- return await listPathFallback(this.context, relativePath, options.withFileTypes === true);
334
- }
335
- throw error;
336
- }
317
+ validatePinnedOperationPayload({ relativePath });
318
+ return await listPathFallback(this.context, relativePath, options.withFileTypes === true);
337
319
  }
338
320
  async move(fromRelative, toRelative, options = {}) {
339
321
  assertValidRootRelativePath(fromRelative);
340
322
  assertValidRootRelativePath(toRelative);
323
+ validatePinnedOperationPayload({ from: fromRelative, to: toRelative });
341
324
  const denyMutations = mergeDenyMutationPolicies(this.defaults.denyMutations, options.denyMutations);
342
325
  await assertMoveMutationAllowed(this.context, {
343
326
  fromRelative,
344
327
  toRelative,
345
328
  denyMutations,
346
329
  });
347
- try {
348
- await runPinnedHelper("rename", this.rootReal, {
349
- from: fromRelative,
350
- overwrite: options.overwrite ?? false,
351
- to: toRelative,
352
- });
353
- }
354
- catch (error) {
355
- if (canFallbackFromPythonError(error)) {
356
- await movePathFallback(this.context, {
357
- fromRelative,
358
- denyMutations,
359
- overwrite: options.overwrite ?? false,
360
- toRelative,
361
- });
362
- return;
363
- }
364
- throw error;
365
- }
330
+ await movePathFallback(this.context, {
331
+ fromRelative,
332
+ denyMutations,
333
+ overwrite: options.overwrite ?? false,
334
+ toRelative,
335
+ });
336
+ }
337
+ walk(relativePath, options) {
338
+ assertValidRootRelativePath(relativePath);
339
+ return walkRoot(this, relativePath, options);
366
340
  }
367
341
  }
368
342
  function readDefaults(defaults) {
@@ -377,7 +351,9 @@ export async function root(rootDir, defaults = {}) {
377
351
  return new RootHandle(await resolveRootContext(rootDir), defaults);
378
352
  }
379
353
  async function openFileInRoot(root, params) {
380
- const { rootWithSep, resolved } = await resolvePathInRoot(root, params.relativePath);
354
+ const { rootWithSep, resolved } = await resolvePathInRoot(root, params.relativePath, {
355
+ allowFinalSymlink: true,
356
+ });
381
357
  let opened;
382
358
  try {
383
359
  opened = await openVerifiedLocalFile(resolved, {
@@ -448,6 +424,15 @@ function buildAtomicWriteTempPath(targetPath) {
448
424
  function rootWriteQueueKey(root, relativePath) {
449
425
  return `${root.rootReal}\0${relativePath}`;
450
426
  }
427
+ async function prepareRootWriteTarget(rootReal, targetPath) {
428
+ const parentPath = await mkdirPathComponentsWithGuards({
429
+ rootReal,
430
+ targetPath: path.dirname(targetPath),
431
+ });
432
+ // Continue through the guarded walk's real parent instead of re-entering
433
+ // the original path through a symlinked component.
434
+ return path.join(parentPath, path.basename(targetPath));
435
+ }
451
436
  async function writeTempFileForAtomicReplace(params) {
452
437
  const tempHandle = await fs.open(params.tempPath, OPEN_WRITE_CREATE_FLAGS, params.mode);
453
438
  try {
@@ -478,7 +463,7 @@ async function verifyAtomicWriteResult(params) {
478
463
  }
479
464
  }
480
465
  async function openWritableFileInRoot(root, params) {
481
- const { rootReal, rootWithSep, resolved } = await resolvePathInRoot(root, params.relativePath);
466
+ const { rootReal, rootWithSep, resolved } = await resolvePathInRoot(root, params.relativePath, { aliasErrorCode: "path-alias" });
482
467
  await assertMutationNotDenied(resolved, params.denyMutations);
483
468
  try {
484
469
  await assertNoPathAliasEscape({
@@ -490,15 +475,11 @@ async function openWritableFileInRoot(root, params) {
490
475
  catch (err) {
491
476
  throw new FsSafeError("path-alias", "path alias escape blocked", { cause: err });
492
477
  }
493
- if (params.mkdir !== false) {
494
- const parentGuard = await createNearestExistingDirectoryGuard(rootReal, path.dirname(resolved));
495
- await withAsyncDirectoryGuards([parentGuard], async () => {
496
- await fs.mkdir(path.dirname(resolved), { recursive: true });
497
- });
498
- }
499
- let ioPath = resolved;
478
+ let ioPath = params.mkdir === false
479
+ ? resolved
480
+ : await prepareRootWriteTarget(rootReal, resolved);
500
481
  try {
501
- const resolvedRealPath = await fs.realpath(resolved);
482
+ const resolvedRealPath = await fs.realpath(ioPath);
502
483
  if (!isPathInside(rootWithSep, resolvedRealPath)) {
503
484
  throw new FsSafeError("outside-workspace", "file is outside workspace root");
504
485
  }
@@ -583,6 +564,7 @@ async function openWritableFileInRoot(root, params) {
583
564
  }
584
565
  return {
585
566
  handle,
567
+ containment: "best-effort",
586
568
  createdForWrite,
587
569
  realPath,
588
570
  stat,
@@ -638,44 +620,22 @@ async function appendFileInRoot(root, params) {
638
620
  }
639
621
  }
640
622
  async function removePathInRoot(root, params) {
623
+ validatePinnedOperationPayload({ relativePath: params.relativePath });
641
624
  const resolved = await resolvePinnedRemovePathInRoot(root, params.relativePath, params.denyMutations);
642
- if (process.platform === "win32") {
643
- await removePathFallback(resolved);
644
- return;
645
- }
646
625
  try {
647
- await runPinnedPathHelper({
648
- operation: "remove",
649
- rootPath: resolved.rootReal,
650
- relativePath: resolved.relativePosix,
651
- });
626
+ await removePathFallback(resolved);
652
627
  }
653
628
  catch (error) {
654
- if (isPinnedPathHelperSpawnError(error)) {
655
- await removePathFallback(resolved);
656
- return;
657
- }
658
629
  throw normalizePinnedPathError(error);
659
630
  }
660
631
  }
661
632
  async function mkdirPathInRoot(root, params) {
633
+ validatePinnedOperationPayload({ relativePath: params.relativePath });
662
634
  const resolved = await resolvePinnedPathInRoot(root, params);
663
- if (process.platform === "win32") {
664
- await mkdirPathFallback(resolved);
665
- return;
666
- }
667
635
  try {
668
- await runPinnedPathHelper({
669
- operation: "mkdirp",
670
- rootPath: resolved.rootReal,
671
- relativePath: resolved.relativePosix,
672
- });
636
+ await mkdirPathFallback(resolved);
673
637
  }
674
638
  catch (error) {
675
- if (isPinnedPathHelperSpawnError(error)) {
676
- await mkdirPathFallback(resolved);
677
- return;
678
- }
679
639
  throw normalizePinnedPathError(error);
680
640
  }
681
641
  }
@@ -741,49 +701,25 @@ async function copyFileInRoot(root, params) {
741
701
  throw new FsSafeError("too-large", `file exceeds limit of ${params.maxBytes} bytes (got ${source.stat.size})`);
742
702
  }
743
703
  try {
744
- if (process.platform === "win32") {
745
- await serializePathWrite(rootWriteQueueKey(root, params.relativePath), async () => {
746
- await copyFileFallback(root, params, source);
747
- });
748
- return;
749
- }
750
704
  const pinned = await resolvePinnedWriteTargetInRoot(root, params.relativePath, params.mode, params.denyMutations);
751
705
  await serializePathWrite(pinned.targetPath, async () => {
752
- let identity;
706
+ await assertCopySourceCurrent(source);
707
+ const identity = await runPinnedWriteHelper({
708
+ rootPath: pinned.rootReal,
709
+ relativeParentPath: pinned.relativeParentPath,
710
+ basename: pinned.basename,
711
+ mkdir: params.mkdir !== false,
712
+ mode: pinned.mode,
713
+ overwrite: true,
714
+ maxBytes: params.maxBytes,
715
+ input: { kind: "stream", stream: source.handle.createReadStream() },
716
+ });
753
717
  try {
754
- if (getFsSafePythonConfig().mode === "off") {
755
- await copyFileFallback(root, params, source);
756
- return;
757
- }
758
- identity = await runPinnedCopyHelper({
759
- rootPath: pinned.rootReal,
760
- relativeParentPath: pinned.relativeParentPath,
761
- basename: pinned.basename,
762
- mkdir: params.mkdir !== false,
763
- mode: pinned.mode,
764
- overwrite: true,
765
- maxBytes: params.maxBytes,
766
- sourcePath: source.realPath,
767
- sourceIdentity: { dev: source.stat.dev, ino: source.stat.ino },
768
- });
718
+ await assertCopySourcePathCurrent(source);
769
719
  }
770
720
  catch (error) {
771
- if (canFallbackFromPythonError(error)) {
772
- await copyFileFallback(root, params, source);
773
- return;
774
- }
775
- throw normalizePinnedWriteError(error);
776
- }
777
- try {
778
- await verifyAtomicWriteResult({
779
- root,
780
- targetPath: pinned.targetPath,
781
- expectedIdentity: identity,
782
- });
783
- }
784
- catch (err) {
785
- emitWriteBoundaryWarning(`post-copy verification failed: ${String(err)}`);
786
- throw err;
721
+ await removeCopyTargetIfUnchanged(pinned.targetPath, identity).catch(() => undefined);
722
+ throw error;
787
723
  }
788
724
  });
789
725
  }
@@ -791,8 +727,33 @@ async function copyFileInRoot(root, params) {
791
727
  await source.handle.close().catch(() => { });
792
728
  }
793
729
  }
730
+ async function assertCopySourceCurrent(source) {
731
+ const opened = await source.handle.stat();
732
+ if (!sameFileIdentity(opened, source.stat)) {
733
+ throw new FsSafeError("path-mismatch", "copy source descriptor changed");
734
+ }
735
+ await assertCopySourcePathCurrent(source);
736
+ }
737
+ async function assertCopySourcePathCurrent(source) {
738
+ const current = await fs.lstat(source.realPath);
739
+ if (current.isSymbolicLink() || !current.isFile() || !sameFileIdentity(current, source.stat)) {
740
+ throw new FsSafeError("path-mismatch", "copy source path changed");
741
+ }
742
+ }
743
+ async function removeCopyTargetIfUnchanged(targetPath, identity) {
744
+ const parentGuard = await createAsyncDirectoryGuard(path.dirname(targetPath));
745
+ const current = await fs.lstat(targetPath);
746
+ if (current.isSymbolicLink() || !sameFileIdentity(current, identity)) {
747
+ return;
748
+ }
749
+ await withAsyncDirectoryGuards([parentGuard], async () => {
750
+ await fs.rm(targetPath);
751
+ });
752
+ }
794
753
  async function resolvePinnedWriteTargetInRoot(root, relativePath, requestedMode, denyMutations) {
795
- const { rootReal, rootWithSep, resolved } = await resolvePathInRoot(root, relativePath);
754
+ const { rootReal, rootWithSep, resolved } = await resolvePathInRoot(root, relativePath, {
755
+ aliasErrorCode: "path-alias",
756
+ });
796
757
  await assertMutationNotDenied(resolved, denyMutations);
797
758
  try {
798
759
  await assertNoPathAliasEscape({
@@ -894,8 +855,11 @@ async function resolvePinnedRootPathInRoot(root, params) {
894
855
  const rootReal = root.rootReal;
895
856
  let resolved;
896
857
  try {
858
+ const expandedPath = await expandRelativePathWithHome(params.relativePath);
897
859
  resolved = await resolveRootPath({
898
- absolutePath: path.resolve(rootReal, await expandRelativePathWithHome(params.relativePath)),
860
+ absolutePath: path.isAbsolute(expandedPath)
861
+ ? expandedPath
862
+ : `${ensureTrailingSep(rootReal)}${expandedPath}`,
899
863
  rootPath: rootReal,
900
864
  rootCanonicalPath: rootReal,
901
865
  boundaryLabel: "root",
@@ -966,35 +930,51 @@ async function listPathFallback(root, relativePath, withFileTypes) {
966
930
  }
967
931
  }
968
932
  async function assertMoveMutationAllowed(root, params) {
969
- const source = await resolvePathInRoot(root, params.fromRelative);
933
+ const source = await resolvePathInRoot(root, params.fromRelative, {
934
+ aliasErrorCode: "path-alias",
935
+ allowFinalSymlink: true,
936
+ });
970
937
  await assertMutationNotDenied(source.resolved, params.denyMutations, { protectAncestors: true });
971
- const target = await resolvePathInRoot(root, params.toRelative);
938
+ const target = await resolvePathInRoot(root, params.toRelative, {
939
+ aliasErrorCode: "path-alias",
940
+ allowFinalSymlink: true,
941
+ });
972
942
  await assertMutationNotDenied(target.resolved, params.denyMutations, { protectAncestors: true });
973
943
  }
974
944
  async function movePathFallback(root, params) {
975
- const source = await resolvePathInRoot(root, params.fromRelative);
945
+ const source = await resolvePathInRoot(root, params.fromRelative, {
946
+ aliasErrorCode: "path-alias",
947
+ allowFinalSymlink: true,
948
+ });
976
949
  await assertMutationNotDenied(source.resolved, params.denyMutations, { protectAncestors: true });
977
950
  await resolvePinnedRootPathInRoot(root, {
978
951
  relativePath: params.fromRelative,
979
952
  policy: PATH_ALIAS_POLICIES.strict,
980
953
  });
981
- const target = await resolvePathInRoot(root, params.toRelative);
954
+ const target = await resolvePathInRoot(root, params.toRelative, {
955
+ aliasErrorCode: "path-alias",
956
+ allowFinalSymlink: true,
957
+ });
982
958
  await assertMutationNotDenied(target.resolved, params.denyMutations, { protectAncestors: true });
983
959
  await resolvePinnedRootPathInRoot(root, {
984
960
  relativePath: params.toRelative,
985
961
  policy: PATH_ALIAS_POLICIES.unlinkTarget,
986
962
  });
987
- try {
988
- await assertNoPathAliasEscape({
989
- absolutePath: target.resolved,
990
- rootPath: target.rootReal,
991
- boundaryLabel: "root",
992
- });
993
- }
994
- catch (error) {
995
- throw new FsSafeError("path-alias", "path alias escape blocked", {
996
- cause: error instanceof Error ? error : undefined,
997
- });
963
+ const targetStat = await fs.lstat(target.resolved).catch(() => undefined);
964
+ const replacesFinalSymlink = process.platform !== "win32" && params.overwrite && targetStat?.isSymbolicLink() === true;
965
+ if (!replacesFinalSymlink) {
966
+ try {
967
+ await assertNoPathAliasEscape({
968
+ absolutePath: target.resolved,
969
+ rootPath: target.rootReal,
970
+ boundaryLabel: "root",
971
+ });
972
+ }
973
+ catch (error) {
974
+ throw new FsSafeError("path-alias", "path alias escape blocked", {
975
+ cause: error instanceof Error ? error : undefined,
976
+ });
977
+ }
998
978
  }
999
979
  let sourceStat;
1000
980
  try {
@@ -1081,6 +1061,7 @@ async function writeFileFallback(root, params) {
1081
1061
  encoding: params.encoding,
1082
1062
  mode: mode || 0o600,
1083
1063
  });
1064
+ unregisterTempPath.setIdentity(writtenStat);
1084
1065
  const commitTempPath = tempPath;
1085
1066
  await withAsyncDirectoryGuards([destinationGuard], async () => {
1086
1067
  await fs.rename(commitTempPath, destinationPath);
@@ -1108,7 +1089,9 @@ async function writeFileFallback(root, params) {
1108
1089
  }
1109
1090
  }
1110
1091
  async function writeMissingFileFallback(root, params) {
1111
- const { rootReal, resolved } = await resolvePathInRoot(root, params.relativePath);
1092
+ const { rootReal, resolved } = await resolvePathInRoot(root, params.relativePath, {
1093
+ aliasErrorCode: "path-alias",
1094
+ });
1112
1095
  await assertMutationNotDenied(resolved, params.denyMutations);
1113
1096
  try {
1114
1097
  await assertNoPathAliasEscape({
@@ -1120,14 +1103,14 @@ async function writeMissingFileFallback(root, params) {
1120
1103
  catch (err) {
1121
1104
  throw new FsSafeError("path-alias", "path alias escape blocked", { cause: err });
1122
1105
  }
1123
- if (params.mkdir !== false) {
1124
- await fs.mkdir(path.dirname(resolved), { recursive: true });
1125
- }
1126
- const parentGuard = await createAsyncDirectoryGuard(path.dirname(resolved));
1106
+ const targetPath = params.mkdir === false
1107
+ ? resolved
1108
+ : await prepareRootWriteTarget(rootReal, resolved);
1109
+ const parentGuard = await createAsyncDirectoryGuard(path.dirname(targetPath));
1127
1110
  let created = false;
1128
1111
  try {
1129
1112
  const { handle, writtenStat } = await withAsyncDirectoryGuards([parentGuard], async () => {
1130
- const handle = await fs.open(resolved, OPEN_WRITE_CREATE_FLAGS, params.mode ?? 0o600);
1113
+ const handle = await fs.open(targetPath, OPEN_WRITE_CREATE_FLAGS, params.mode ?? 0o600);
1131
1114
  created = true;
1132
1115
  try {
1133
1116
  if (typeof params.data === "string") {
@@ -1151,7 +1134,7 @@ async function writeMissingFileFallback(root, params) {
1151
1134
  await handle.close();
1152
1135
  await verifyAtomicWriteResult({
1153
1136
  root,
1154
- targetPath: resolved,
1137
+ targetPath,
1155
1138
  expectedIdentity: writtenStat,
1156
1139
  });
1157
1140
  created = false;
@@ -1166,87 +1149,7 @@ async function writeMissingFileFallback(root, params) {
1166
1149
  }
1167
1150
  finally {
1168
1151
  if (created) {
1169
- await fs.rm(resolved, { force: true }).catch(() => undefined);
1170
- }
1171
- }
1172
- }
1173
- async function copyFileFallback(root, params, source) {
1174
- let target = null;
1175
- let sourceClosedByStream = false;
1176
- let targetClosedByUs = false;
1177
- let tempHandle = null;
1178
- let tempPath = null;
1179
- let unregisterTempPath = null;
1180
- let tempClosedByStream = false;
1181
- try {
1182
- target = await openWritableFileInRoot(root, {
1183
- relativePath: params.relativePath,
1184
- mkdir: params.mkdir,
1185
- mode: params.mode,
1186
- denyMutations: params.denyMutations,
1187
- truncateExisting: false,
1188
- });
1189
- const destinationPath = target.realPath;
1190
- const mode = params.mode ?? (target.stat.mode & 0o777);
1191
- await target.handle.close().catch(() => { });
1192
- targetClosedByUs = true;
1193
- const destinationGuard = await createAsyncDirectoryGuard(path.dirname(destinationPath));
1194
- tempPath = buildAtomicWriteTempPath(destinationPath);
1195
- unregisterTempPath = registerTempPathForExit(tempPath);
1196
- tempHandle = await fs.open(tempPath, OPEN_WRITE_CREATE_FLAGS, mode || 0o600);
1197
- const sourceStream = createBoundedReadStream(source, params.maxBytes);
1198
- const targetStream = tempHandle.createWriteStream();
1199
- sourceStream.once("close", () => {
1200
- sourceClosedByStream = true;
1201
- });
1202
- targetStream.once("close", () => {
1203
- tempClosedByStream = true;
1204
- });
1205
- await pipeline(sourceStream, targetStream);
1206
- const writtenStat = await fs.stat(tempPath);
1207
- if (!tempClosedByStream) {
1208
- await tempHandle.close().catch(() => { });
1209
- tempClosedByStream = true;
1210
- }
1211
- tempHandle = null;
1212
- const commitTempPath = tempPath;
1213
- await withAsyncDirectoryGuards([destinationGuard], async () => {
1214
- await fs.rename(commitTempPath, destinationPath);
1215
- });
1216
- tempPath = null;
1217
- unregisterTempPath();
1218
- unregisterTempPath = null;
1219
- try {
1220
- await verifyAtomicWriteResult({
1221
- root,
1222
- targetPath: destinationPath,
1223
- expectedIdentity: writtenStat,
1224
- });
1225
- }
1226
- catch (err) {
1227
- emitWriteBoundaryWarning(`post-copy verification failed: ${String(err)}`);
1228
- throw err;
1229
- }
1230
- }
1231
- catch (err) {
1232
- if (target?.createdForWrite) {
1233
- await fs.rm(target.realPath, { force: true }).catch(() => { });
1234
- }
1235
- throw err;
1236
- }
1237
- finally {
1238
- if (!sourceClosedByStream) {
1239
- await source.handle.close().catch(() => { });
1240
- }
1241
- if (tempHandle && !tempClosedByStream) {
1242
- await tempHandle.close().catch(() => { });
1243
- }
1244
- if (tempPath) {
1245
- await fs.rm(tempPath, { force: true }).catch(() => { });
1246
- }
1247
- unregisterTempPath?.();
1248
- if (target && !targetClosedByUs) {
1249
- await target.handle.close().catch(() => { });
1152
+ await fs.rm(targetPath, { force: true }).catch(() => undefined);
1250
1153
  }
1251
1154
  }
1252
1155
  }
@@ -0,0 +1,3 @@
1
+ export declare function resolvePathViaExistingAncestor(targetPath: string): Promise<string>;
2
+ export declare function resolvePathViaExistingAncestorSync(targetPath: string): string;
3
+ //# sourceMappingURL=root-path-existing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"root-path-existing.d.ts","sourceRoot":"","sources":["../src/root-path-existing.ts"],"names":[],"mappings":"AAqBA,wBAAsB,8BAA8B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA0BxF;AAED,wBAAgB,kCAAkC,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CA0B7E"}