@openclaw/fs-safe 0.5.1 → 0.5.3

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 (231) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/README.md +19 -6
  3. package/dist/absolute-path.d.ts.map +1 -1
  4. package/dist/absolute-path.js +20 -12
  5. package/dist/archive-entry.d.ts +1 -0
  6. package/dist/archive-entry.d.ts.map +1 -1
  7. package/dist/archive-entry.js +37 -6
  8. package/dist/archive-errors.d.ts +1 -0
  9. package/dist/archive-errors.d.ts.map +1 -1
  10. package/dist/archive-errors.js +4 -0
  11. package/dist/archive-input.d.ts +7 -0
  12. package/dist/archive-input.d.ts.map +1 -1
  13. package/dist/archive-input.js +11 -1
  14. package/dist/archive-limits.js +1 -1
  15. package/dist/archive-native.d.ts.map +1 -1
  16. package/dist/archive-native.js +12 -6
  17. package/dist/archive-policy.d.ts.map +1 -1
  18. package/dist/archive-policy.js +2 -1
  19. package/dist/archive-read.d.ts.map +1 -1
  20. package/dist/archive-read.js +62 -37
  21. package/dist/archive-staging.d.ts.map +1 -1
  22. package/dist/archive-staging.js +3 -2
  23. package/dist/archive-tar-meta.d.ts.map +1 -1
  24. package/dist/archive-tar-meta.js +15 -1
  25. package/dist/archive-tar-runtime.d.ts +1 -0
  26. package/dist/archive-tar-runtime.d.ts.map +1 -1
  27. package/dist/archive-tar-runtime.js +11 -0
  28. package/dist/archive-tar.d.ts.map +1 -1
  29. package/dist/archive-tar.js +8 -4
  30. package/dist/archive-zip-entry.d.ts +7 -1
  31. package/dist/archive-zip-entry.d.ts.map +1 -1
  32. package/dist/archive-zip-entry.js +11 -1
  33. package/dist/archive-zip-integrity.d.ts +5 -0
  34. package/dist/archive-zip-integrity.d.ts.map +1 -0
  35. package/dist/archive-zip-integrity.js +54 -0
  36. package/dist/archive-zip-preflight.d.ts.map +1 -1
  37. package/dist/archive-zip-preflight.js +12 -1
  38. package/dist/archive.d.ts.map +1 -1
  39. package/dist/archive.js +9 -5
  40. package/dist/bounded-read-stream.d.ts.map +1 -1
  41. package/dist/bounded-read-stream.js +13 -2
  42. package/dist/deny-mutations.d.ts +1 -0
  43. package/dist/deny-mutations.d.ts.map +1 -1
  44. package/dist/deny-mutations.js +6 -39
  45. package/dist/device-path.d.ts +1 -0
  46. package/dist/device-path.d.ts.map +1 -1
  47. package/dist/device-path.js +6 -6
  48. package/dist/directory-guard.d.ts.map +1 -1
  49. package/dist/directory-guard.js +5 -4
  50. package/dist/error-detail.d.ts +2 -0
  51. package/dist/error-detail.d.ts.map +1 -0
  52. package/dist/error-detail.js +4 -0
  53. package/dist/errors.d.ts +1 -1
  54. package/dist/errors.d.ts.map +1 -1
  55. package/dist/errors.js +4 -0
  56. package/dist/file-hash.d.ts.map +1 -1
  57. package/dist/file-hash.js +4 -1
  58. package/dist/file-lock-sync.d.ts.map +1 -1
  59. package/dist/file-lock-sync.js +88 -35
  60. package/dist/file-store-boundary.d.ts +6 -0
  61. package/dist/file-store-boundary.d.ts.map +1 -1
  62. package/dist/file-store-boundary.js +20 -5
  63. package/dist/file-store-sync-write.d.ts +9 -0
  64. package/dist/file-store-sync-write.d.ts.map +1 -0
  65. package/dist/file-store-sync-write.js +97 -0
  66. package/dist/file-store.d.ts.map +1 -1
  67. package/dist/file-store.js +62 -139
  68. package/dist/filename.d.ts.map +1 -1
  69. package/dist/filename.js +27 -0
  70. package/dist/guarded-mkdir.d.ts.map +1 -1
  71. package/dist/guarded-mkdir.js +5 -6
  72. package/dist/install-path.js +1 -1
  73. package/dist/json-document-store.d.ts.map +1 -1
  74. package/dist/json-document-store.js +19 -13
  75. package/dist/json.d.ts.map +1 -1
  76. package/dist/json.js +1 -3
  77. package/dist/local-file-access.d.ts +3 -2
  78. package/dist/local-file-access.d.ts.map +1 -1
  79. package/dist/local-file-access.js +12 -6
  80. package/dist/local-roots.d.ts.map +1 -1
  81. package/dist/local-roots.js +35 -30
  82. package/dist/move-path.d.ts.map +1 -1
  83. package/dist/move-path.js +51 -12
  84. package/dist/native/darwin-arm64/fs-safe-native.node +0 -0
  85. package/dist/native/darwin-x64/fs-safe-native.node +0 -0
  86. package/dist/native/linux-arm64-gnu/fs-safe-native.node +0 -0
  87. package/dist/native/linux-arm64-musl/fs-safe-native.node +0 -0
  88. package/dist/native/linux-x64-gnu/fs-safe-native.node +0 -0
  89. package/dist/native/linux-x64-musl/fs-safe-native.node +0 -0
  90. package/dist/native/win32-x64-msvc/fs-safe-native.node +0 -0
  91. package/dist/native-binding.d.ts +1 -0
  92. package/dist/native-binding.d.ts.map +1 -1
  93. package/dist/native-operations.d.ts.map +1 -1
  94. package/dist/native-operations.js +14 -1
  95. package/dist/native-pinned-write.d.ts.map +1 -1
  96. package/dist/native-pinned-write.js +44 -17
  97. package/dist/output-sibling.d.ts.map +1 -1
  98. package/dist/output-sibling.js +6 -5
  99. package/dist/path-policy.d.ts.map +1 -1
  100. package/dist/path-policy.js +1 -7
  101. package/dist/path.d.ts.map +1 -1
  102. package/dist/path.js +4 -0
  103. package/dist/permission-exec.d.ts +6 -0
  104. package/dist/permission-exec.d.ts.map +1 -0
  105. package/dist/permission-exec.js +28 -0
  106. package/dist/permissions-windows.d.ts +56 -0
  107. package/dist/permissions-windows.d.ts.map +1 -0
  108. package/dist/permissions-windows.js +285 -0
  109. package/dist/permissions.d.ts +3 -46
  110. package/dist/permissions.d.ts.map +1 -1
  111. package/dist/permissions.js +17 -328
  112. package/dist/pinned-open.d.ts.map +1 -1
  113. package/dist/pinned-open.js +21 -4
  114. package/dist/pinned-write.js +2 -1
  115. package/dist/private-temp-workspace.d.ts.map +1 -1
  116. package/dist/private-temp-workspace.js +45 -4
  117. package/dist/read-error.d.ts +2 -0
  118. package/dist/read-error.d.ts.map +1 -0
  119. package/dist/read-error.js +11 -0
  120. package/dist/regular-file.d.ts.map +1 -1
  121. package/dist/regular-file.js +21 -2
  122. package/dist/replace-directory.d.ts.map +1 -1
  123. package/dist/replace-directory.js +10 -1
  124. package/dist/replace-file-copy-fallback.d.ts +3 -0
  125. package/dist/replace-file-copy-fallback.d.ts.map +1 -1
  126. package/dist/replace-file-copy-fallback.js +18 -11
  127. package/dist/replace-file-descriptor.d.ts +33 -0
  128. package/dist/replace-file-descriptor.d.ts.map +1 -0
  129. package/dist/replace-file-descriptor.js +96 -0
  130. package/dist/replace-file.d.ts +9 -2
  131. package/dist/replace-file.d.ts.map +1 -1
  132. package/dist/replace-file.js +64 -72
  133. package/dist/root-context.d.ts +8 -0
  134. package/dist/root-context.d.ts.map +1 -1
  135. package/dist/root-context.js +43 -2
  136. package/dist/root-errors.d.ts +7 -0
  137. package/dist/root-errors.d.ts.map +1 -1
  138. package/dist/root-errors.js +46 -1
  139. package/dist/root-file.d.ts +1 -0
  140. package/dist/root-file.d.ts.map +1 -1
  141. package/dist/root-file.js +2 -0
  142. package/dist/root-impl.d.ts.map +1 -1
  143. package/dist/root-impl.js +216 -169
  144. package/dist/root-path-symlink.d.ts +3 -0
  145. package/dist/root-path-symlink.d.ts.map +1 -0
  146. package/dist/root-path-symlink.js +35 -0
  147. package/dist/root-path.d.ts +1 -0
  148. package/dist/root-path.d.ts.map +1 -1
  149. package/dist/root-path.js +129 -190
  150. package/dist/root-walk.d.ts.map +1 -1
  151. package/dist/root-walk.js +15 -0
  152. package/dist/safe-path-segment.d.ts +2 -0
  153. package/dist/safe-path-segment.d.ts.map +1 -1
  154. package/dist/safe-path-segment.js +12 -0
  155. package/dist/secret-file.d.ts.map +1 -1
  156. package/dist/secret-file.js +29 -6
  157. package/dist/secret-read-async.d.ts.map +1 -1
  158. package/dist/secret-read-async.js +5 -1
  159. package/dist/secure-temp-dir.d.ts.map +1 -1
  160. package/dist/secure-temp-dir.js +6 -1
  161. package/dist/short-path.d.ts +2 -0
  162. package/dist/short-path.d.ts.map +1 -0
  163. package/dist/short-path.js +7 -0
  164. package/dist/sidecar-lock-acquire.d.ts +30 -0
  165. package/dist/sidecar-lock-acquire.d.ts.map +1 -0
  166. package/dist/sidecar-lock-acquire.js +260 -0
  167. package/dist/sidecar-lock-policy.d.ts +3 -0
  168. package/dist/sidecar-lock-policy.d.ts.map +1 -1
  169. package/dist/sidecar-lock-policy.js +20 -3
  170. package/dist/sidecar-lock-reclaim.d.ts +5 -1
  171. package/dist/sidecar-lock-reclaim.d.ts.map +1 -1
  172. package/dist/sidecar-lock-reclaim.js +59 -9
  173. package/dist/sidecar-lock.d.ts.map +1 -1
  174. package/dist/sidecar-lock.js +27 -226
  175. package/dist/symlink-parents.d.ts.map +1 -1
  176. package/dist/symlink-parents.js +9 -8
  177. package/dist/temp-target.d.ts.map +1 -1
  178. package/dist/temp-target.js +15 -4
  179. package/dist/test-hooks.d.ts +1 -0
  180. package/dist/test-hooks.d.ts.map +1 -1
  181. package/dist/timing.d.ts +2 -0
  182. package/dist/timing.d.ts.map +1 -1
  183. package/dist/timing.js +9 -0
  184. package/dist/trash.d.ts.map +1 -1
  185. package/dist/trash.js +10 -1
  186. package/dist/walk.d.ts.map +1 -1
  187. package/dist/walk.js +15 -0
  188. package/docs/advanced.md +7 -1
  189. package/docs/archive.md +27 -20
  190. package/docs/assets/readme-banner.jpg +0 -0
  191. package/docs/atomic.md +48 -19
  192. package/docs/errors.md +34 -8
  193. package/docs/file-store.md +14 -1
  194. package/docs/filename.md +17 -16
  195. package/docs/index.md +1 -0
  196. package/docs/install-path.md +9 -6
  197. package/docs/json.md +8 -2
  198. package/docs/local-roots.md +79 -103
  199. package/docs/native-helper.md +11 -5
  200. package/docs/native.md +2 -2
  201. package/docs/output.md +8 -3
  202. package/docs/path-scope.md +4 -1
  203. package/docs/path.md +7 -3
  204. package/docs/permissions.md +8 -0
  205. package/docs/public-api.md +109 -0
  206. package/docs/quickstart.md +6 -2
  207. package/docs/reading.md +13 -10
  208. package/docs/root.md +26 -4
  209. package/docs/secret-file.md +21 -4
  210. package/docs/security-model.md +21 -4
  211. package/docs/sidecar-lock.md +15 -5
  212. package/docs/temp.md +42 -5
  213. package/docs/test-hooks.md +2 -0
  214. package/docs/types.md +32 -15
  215. package/docs/writing.md +25 -12
  216. package/package.json +15 -4
  217. package/dist/archive-file-io.d.ts +0 -9
  218. package/dist/archive-file-io.d.ts.map +0 -1
  219. package/dist/archive-file-io.js +0 -11
  220. package/dist/file-store-source.d.ts +0 -5
  221. package/dist/file-store-source.d.ts.map +0 -1
  222. package/dist/file-store-source.js +0 -30
  223. package/dist/file-url.d.ts +0 -2
  224. package/dist/file-url.d.ts.map +0 -1
  225. package/dist/file-url.js +0 -1
  226. package/dist/path-stat.d.ts +0 -4
  227. package/dist/path-stat.d.ts.map +0 -1
  228. package/dist/path-stat.js +0 -15
  229. package/dist/windows-permissions-native.d.ts +0 -9
  230. package/dist/windows-permissions-native.d.ts.map +0 -1
  231. package/dist/windows-permissions-native.js +0 -31
@@ -2,11 +2,11 @@ import fsSync from "node:fs";
2
2
  import path from "node:path";
3
3
  import { FsSafeError } from "./errors.js";
4
4
  import { expandHomePrefix, resolveUserPath } from "./home-dir.js";
5
- import { safeFileURLToPath } from "./local-file-access.js";
5
+ import { isFileUrl, safeFileURLToPath } from "./local-file-access.js";
6
6
  import { isPathInside } from "./path.js";
7
7
  import { root } from "./root.js";
8
8
  function resolveLocalPathInput(input, label) {
9
- if (input.startsWith("file://")) {
9
+ if (isFileUrl(input)) {
10
10
  try {
11
11
  return safeFileURLToPath(input);
12
12
  }
@@ -25,7 +25,7 @@ function resolveLocalRootInput(input, label) {
25
25
  if (!trimmed) {
26
26
  throw new FsSafeError("invalid-path", `${label} entry is required`);
27
27
  }
28
- const resolved = trimmed.startsWith("file://")
28
+ const resolved = isFileUrl(trimmed)
29
29
  ? resolveLocalPathInput(trimmed, label)
30
30
  : expandHomePrefix(trimmed);
31
31
  if (resolved.includes("\0")) {
@@ -41,7 +41,9 @@ function isPathInsideRoot(candidate, rootDir) {
41
41
  }
42
42
  function resolveRootRealSync(rootDir) {
43
43
  try {
44
- const stat = fsSync.lstatSync(rootDir);
44
+ // Configured roots may themselves be symlinks. Follow only this trusted
45
+ // root entry, then use its canonical directory for containment checks.
46
+ const stat = fsSync.statSync(rootDir);
45
47
  if (!stat.isDirectory()) {
46
48
  return null;
47
49
  }
@@ -101,8 +103,8 @@ function resolveCandidateCanonicalSync(filePath) {
101
103
  export function resolveLocalPathFromRootsSync(options) {
102
104
  const label = options.label ?? "local roots";
103
105
  const requestedPath = path.resolve(resolveLocalPathInput(options.filePath, "file path"));
104
- for (const rootEntry of options.roots) {
105
- const rootDir = resolveLocalRootInput(rootEntry, label);
106
+ const rootDirs = options.roots.map((rootEntry) => resolveLocalRootInput(rootEntry, label));
107
+ for (const rootDir of rootDirs) {
106
108
  const rootReal = resolveRootRealSync(rootDir);
107
109
  if (!rootReal) {
108
110
  continue;
@@ -129,8 +131,8 @@ export function resolveLocalPathFromRootsSync(options) {
129
131
  export async function readLocalFileFromRoots(options) {
130
132
  const label = options.label ?? "local roots";
131
133
  const requestedPath = path.resolve(resolveLocalPathInput(options.filePath, "file path"));
132
- for (const rootEntry of options.roots) {
133
- const rootDir = resolveLocalRootInput(rootEntry, label);
134
+ const rootDirs = options.roots.map((rootEntry) => resolveLocalRootInput(rootEntry, label));
135
+ for (const rootDir of rootDirs) {
134
136
  let scopedRoot;
135
137
  try {
136
138
  scopedRoot = await root(rootDir);
@@ -138,29 +140,32 @@ export async function readLocalFileFromRoots(options) {
138
140
  catch {
139
141
  continue;
140
142
  }
141
- const relativePath = path.relative(scopedRoot.rootDir, requestedPath);
142
- if (!relativePath ||
143
- relativePath === ".." ||
144
- relativePath.startsWith(`..${path.sep}`) ||
145
- path.isAbsolute(relativePath)) {
146
- continue;
147
- }
148
- try {
149
- const readOptions = {
150
- hardlinks: options.hardlinks,
151
- nonBlockingRead: options.nonBlockingRead,
152
- symlinks: options.symlinks,
153
- };
154
- // Leave maxBytes absent when the caller omits it so Root's own default
155
- // cap remains in force instead of being overwritten by undefined.
156
- if (options.maxBytes !== undefined) {
157
- readOptions.maxBytes = options.maxBytes;
143
+ const readOptions = {
144
+ hardlinks: options.hardlinks,
145
+ nonBlockingRead: options.nonBlockingRead,
146
+ symlinks: options.symlinks,
147
+ };
148
+ // Leave maxBytes absent when the caller omits it so Root's own default
149
+ // cap remains in force instead of being overwritten by undefined.
150
+ if (options.maxBytes !== undefined) {
151
+ readOptions.maxBytes = options.maxBytes;
152
+ }
153
+ // A trusted root symlink has two valid spellings. Preserve the caller's
154
+ // lexical spelling when possible so Root.read() still enforces its
155
+ // symlink policy, while also accepting a path expressed below rootReal.
156
+ const relativePaths = [scopedRoot.rootDir, scopedRoot.rootReal]
157
+ .filter((rootPath, index, roots) => roots.indexOf(rootPath) === index)
158
+ .filter((rootPath) => isPathInside(rootPath, requestedPath))
159
+ .map((rootPath) => path.relative(rootPath, requestedPath))
160
+ .filter(Boolean);
161
+ for (const relativePath of relativePaths) {
162
+ try {
163
+ const result = await scopedRoot.read(relativePath, readOptions);
164
+ return { ...result, root: scopedRoot.rootReal };
165
+ }
166
+ catch {
167
+ // Try the canonical spelling before moving to the next configured root.
158
168
  }
159
- const result = await scopedRoot.read(relativePath, readOptions);
160
- return { ...result, root: scopedRoot.rootReal };
161
- }
162
- catch {
163
- continue;
164
169
  }
165
170
  }
166
171
  return null;
@@ -1 +1 @@
1
- {"version":3,"file":"move-path.d.ts","sourceRoot":"","sources":["../src/move-path.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,+BAA+B,GAAG;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IACrC,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,KAAK,sBAAsB,GAAG,cAAc,GAAG,uBAAuB,CAAC;AAEvE,wBAAgB,oCAAoC,CAClD,KAAK,EAAE,OAAO,EACd,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,sBAAsB,GAAG,SAAS,CASpC;AA4UD,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC,IAAI,CAAC,CAuCf"}
1
+ {"version":3,"file":"move-path.d.ts","sourceRoot":"","sources":["../src/move-path.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,+BAA+B,GAAG;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IACrC,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,KAAK,sBAAsB,GAAG,cAAc,GAAG,uBAAuB,CAAC;AAEvE,wBAAgB,oCAAoC,CAClD,KAAK,EAAE,OAAO,EACd,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,sBAAsB,GAAG,SAAS,CASpC;AA6VD,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC,IAAI,CAAC,CA2Df"}
package/dist/move-path.js CHANGED
@@ -4,6 +4,7 @@ import fs from "node:fs/promises";
4
4
  import path from "node:path";
5
5
  import { FsSafeError } from "./errors.js";
6
6
  import { guardedRename } from "./guarded-mutation.js";
7
+ import { registerTempPathForExit } from "./temp-cleanup.js";
7
8
  export function moveCopyFallbackReasonForRenameError(error, platform = process.platform) {
8
9
  const code = error?.code;
9
10
  if (code === "EXDEV") {
@@ -102,6 +103,21 @@ function regularReadFlags() {
102
103
  ? fsConstants.O_NONBLOCK
103
104
  : 0));
104
105
  }
106
+ async function chmodDirectoryPinned(directoryPath, mode) {
107
+ if (process.platform === "win32") {
108
+ // Node cannot portably open a Windows directory for descriptor-bound
109
+ // chmod. POSIX modes are not enforced there, so do not fall back to a
110
+ // pathname operation that could follow a replacement symlink.
111
+ return;
112
+ }
113
+ const handle = await fs.open(directoryPath, fsConstants.O_RDONLY | fsConstants.O_DIRECTORY | fsConstants.O_NOFOLLOW);
114
+ try {
115
+ await handle.chmod(mode);
116
+ }
117
+ finally {
118
+ await handle.close();
119
+ }
120
+ }
105
121
  async function writeAll(handle, buffer, bytesRead) {
106
122
  let offset = 0;
107
123
  while (offset < bytesRead) {
@@ -141,20 +157,20 @@ async function copyRegularFilePinned(params) {
141
157
  }
142
158
  await writeAll(destinationHandle, scratch, bytesRead);
143
159
  }
160
+ // Re-check the opened source before the staged tree can be committed. If
161
+ // it changed while we copied, the caller should retry the move.
162
+ const finalSourceStat = await sourceHandle.stat();
163
+ if (params.rejectHardlinks && finalSourceStat.nlink > 1) {
164
+ throw hardlinkedSourceError(params.from);
165
+ }
166
+ if (!sameIdentity(params.identity, entryIdentity(finalSourceStat))) {
167
+ throw sourceChangedError(params.from);
168
+ }
169
+ await destinationHandle.chmod(modeBits(params.mode));
144
170
  }
145
171
  finally {
146
172
  await destinationHandle.close();
147
173
  }
148
- // Re-check the opened handle before the staged tree can be committed. If
149
- // the source changed while we copied, the caller should retry the move.
150
- const finalSourceStat = await sourceHandle.stat();
151
- if (params.rejectHardlinks && finalSourceStat.nlink > 1) {
152
- throw hardlinkedSourceError(params.from);
153
- }
154
- if (!sameIdentity(params.identity, entryIdentity(finalSourceStat))) {
155
- throw sourceChangedError(params.from);
156
- }
157
- await fs.chmod(params.to, modeBits(params.mode)).catch(() => undefined);
158
174
  }
159
175
  catch (error) {
160
176
  if (destinationCreated) {
@@ -198,7 +214,7 @@ async function copyEntryWithManifest(from, to, options) {
198
214
  await assertSourceStillMatches(from, identity);
199
215
  // mkdir() honors process umask. Restore the source mode before commit so
200
216
  // EXDEV fallback preserves directory permissions like fs.cp did.
201
- await fs.chmod(to, modeBits(sourceStat.mode));
217
+ await chmodDirectoryPinned(to, modeBits(sourceStat.mode));
202
218
  return { ...identity, children, kind: "directory" };
203
219
  }
204
220
  if (!sourceStat.isFile()) {
@@ -284,19 +300,42 @@ export async function movePathWithCopyFallback(options) {
284
300
  await assertCopyDestinationOutsideSource(sourcePath, targetPath);
285
301
  const targetDir = path.dirname(targetPath);
286
302
  const staged = path.join(targetDir, `.fs-safe-move-${process.pid}-${randomUUID()}.tmp`);
303
+ const unregisterStaged = registerTempPathForExit(staged, { recursive: true });
304
+ let stagedCommitted = false;
287
305
  try {
288
306
  const manifest = await copyEntryWithManifest(sourcePath, staged, {
289
307
  sourceHardlinks: options.sourceHardlinks ?? "allow",
290
308
  ...(rejectHardlinks ? { budget: { discovered: 1 } } : {}),
291
309
  });
310
+ unregisterStaged.setIdentity(await fs.lstat(staged));
292
311
  await assertCopyDestinationOutsideSource(sourcePath, targetPath);
293
312
  await guardedRename({ from: staged, to: targetPath });
313
+ stagedCommitted = true;
314
+ unregisterStaged();
294
315
  const cleanupResult = await cleanupCopiedEntry(sourcePath, manifest);
295
316
  if (cleanupResult === "stale") {
296
317
  throw sourceChangedError(sourcePath);
297
318
  }
298
319
  }
299
320
  finally {
300
- await fs.rm(staged, { recursive: true, force: true }).catch(() => undefined);
321
+ if (!stagedCommitted) {
322
+ try {
323
+ const stagedIdentity = await fs.lstat(staged);
324
+ if (!stagedIdentity.isSymbolicLink())
325
+ unregisterStaged.setIdentity(stagedIdentity);
326
+ }
327
+ catch (error) {
328
+ if (error.code === "ENOENT") {
329
+ unregisterStaged();
330
+ }
331
+ }
332
+ try {
333
+ await fs.rm(staged, { recursive: true, force: true });
334
+ unregisterStaged();
335
+ }
336
+ catch {
337
+ // Keep the identity-bound exit cleanup registered for a later retry.
338
+ }
339
+ }
301
340
  }
302
341
  }
@@ -74,6 +74,7 @@ export interface NativeBinding {
74
74
  readArchiveEntryNative(path: string, kind: string, requested: string, maxBytes: number, maxEntries: number, maxMetaEntryBytes: number, signal: AbortSignal): Promise<Buffer>;
75
75
  readOwnerAndDacl(path: string): NativeWindowsSecurityFacts;
76
76
  renameNoReplace(sourceRootFd: number, sourceRelPath: string, targetRootFd: number, targetRelPath: string): void;
77
+ renameReplace(sourceRootFd: number, sourceRelPath: string, targetRootFd: number, targetRelPath: string): void;
77
78
  sha256File(fd: number): Promise<NativeFileHash>;
78
79
  }
79
80
  //# sourceMappingURL=native-binding.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"native-binding.d.ts","sourceRoot":"","sources":["../src/native-binding.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"native-binding.d.ts","sourceRoot":"","sources":["../src/native-binding.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"native-operations.d.ts","sourceRoot":"","sources":["../src/native-operations.ts"],"names":[],"mappings":"AAAA,OAAe,EAAE,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAG7C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAE7D,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAEnE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,WAAW,EAAE,oBAAoB,CAAC;IAC3C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC;IACvB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,QAAQ,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5E,CAAC;AA0CF,wBAAgB,oCAAoC,CAAC,MAAM,EAAE;IAC3D,OAAO,EAAE,aAAa,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,KAAK,CAAC;CACjB,GAAG,IAAI,CAsBP;AAED,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CA6CvC;AAED,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAQzD;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAE5D"}
1
+ {"version":3,"file":"native-operations.d.ts","sourceRoot":"","sources":["../src/native-operations.ts"],"names":[],"mappings":"AAAA,OAAe,EAAE,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAG7C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAE7D,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAEnE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,WAAW,EAAE,oBAAoB,CAAC;IAC3C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC;IACvB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,QAAQ,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5E,CAAC;AA0CF,wBAAgB,oCAAoC,CAAC,MAAM,EAAE;IAC3D,OAAO,EAAE,aAAa,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,KAAK,CAAC;CACjB,GAAG,IAAI,CAsBP;AAED,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAyDvC;AAED,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAQzD;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAE5D"}
@@ -72,7 +72,20 @@ export async function createNativeExclusiveFile(targetPath, mode) {
72
72
  let fd;
73
73
  let created;
74
74
  try {
75
- const opened = binding.openBeneath(parent.fd, basename, nativeOpenFlags(fsSync.constants.O_WRONLY | fsSync.constants.O_CREAT | fsSync.constants.O_EXCL));
75
+ let opened;
76
+ try {
77
+ opened = binding.openBeneath(parent.fd, basename, nativeOpenFlags(fsSync.constants.O_WRONLY | fsSync.constants.O_CREAT | fsSync.constants.O_EXCL));
78
+ }
79
+ catch (error) {
80
+ // The parent open above and every post-create operation remain untagged.
81
+ // Only this exclusive-open failure has enough provenance for a caller to
82
+ // classify a Windows lock-file denial without swallowing setup failures.
83
+ const openError = error;
84
+ if (process.platform === "win32" && openError.code === "EPERM") {
85
+ openError.path = targetPath;
86
+ }
87
+ throw error;
88
+ }
76
89
  fd = opened.fd;
77
90
  fsSync.fchmodSync(fd, mode);
78
91
  created = fsSync.fstatSync(fd);
@@ -1 +1 @@
1
- {"version":3,"file":"native-pinned-write.d.ts","sourceRoot":"","sources":["../src/native-pinned-write.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAM3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAoB,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAiD7E,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,gBAAgB,CAAC,CA4F3B"}
1
+ {"version":3,"file":"native-pinned-write.d.ts","sourceRoot":"","sources":["../src/native-pinned-write.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAM3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAoB,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAiD7E,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,gBAAgB,CAAC,CAsH3B"}
@@ -9,23 +9,22 @@ function assertWithinMaxBytes(bytes, maxBytes) {
9
9
  throw new FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes (got at least ${bytes})`);
10
10
  }
11
11
  }
12
- async function inputToBuffer(input, maxBytes) {
12
+ async function writeNativeInput(fd, input, maxBytes) {
13
13
  if (input.kind === "buffer") {
14
14
  const data = typeof input.data === "string"
15
15
  ? Buffer.from(input.data, input.encoding ?? "utf8")
16
16
  : Buffer.from(input.data);
17
17
  assertWithinMaxBytes(data.byteLength, maxBytes);
18
- return data;
18
+ writeNativeFd(fd, data);
19
+ return;
19
20
  }
20
- const chunks = [];
21
21
  let bytes = 0;
22
22
  for await (const chunk of input.stream) {
23
23
  const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
24
24
  bytes += buffer.byteLength;
25
25
  assertWithinMaxBytes(bytes, maxBytes);
26
- chunks.push(buffer);
26
+ writeNativeFd(fd, buffer);
27
27
  }
28
- return Buffer.concat(chunks, bytes);
29
28
  }
30
29
  function nativeOpenFlags(flags) {
31
30
  const closeOnExec = fsSync.constants.O_CLOEXEC;
@@ -37,7 +36,6 @@ function sameNativeIdentity(left, right) {
37
36
  return left.dev === right.dev && left.ino === right.ino;
38
37
  }
39
38
  export async function runPinnedWriteNative(binding, params) {
40
- const data = await inputToBuffer(params.input, params.maxBytes);
41
39
  const root = await fs.open(params.rootPath, fsSync.constants.O_RDONLY |
42
40
  (typeof fsSync.constants.O_DIRECTORY === "number" ? fsSync.constants.O_DIRECTORY : 0));
43
41
  let parentFd;
@@ -67,27 +65,56 @@ export async function runPinnedWriteNative(binding, params) {
67
65
  !sameNativeIdentity(parentPathStat, parentIdentity)) {
68
66
  throw new FsSafeError("path-mismatch", "native write parent changed during resolution");
69
67
  }
70
- try {
71
- await fs.lstat(path.join(parentPath, params.basename));
72
- throw Object.assign(new Error("destination already exists"), { code: "EEXIST" });
73
- }
74
- catch (error) {
75
- if (error.code !== "ENOENT") {
76
- throw error;
68
+ if (params.overwrite === false) {
69
+ try {
70
+ await fs.lstat(path.join(parentPath, params.basename));
71
+ throw Object.assign(new Error("destination already exists"), { code: "EEXIST" });
72
+ }
73
+ catch (error) {
74
+ if (error.code !== "ENOENT") {
75
+ throw error;
76
+ }
77
77
  }
78
78
  }
79
79
  tempFd = binding.openBeneath(parentFd, tempName, nativeOpenFlags(fsSync.constants.O_WRONLY | fsSync.constants.O_CREAT | fsSync.constants.O_EXCL)).fd;
80
- fsSync.fchmodSync(tempFd, params.mode || 0o600);
81
- writeNativeFd(tempFd, data);
82
- syncNativeFileBestEffort(tempFd);
83
80
  tempIdentity = fsSync.fstatSync(tempFd);
84
- binding.renameNoReplace(parentFd, tempName, parentFd, params.basename);
81
+ // Creation is requested at 0600 in the binding, but a restrictive umask
82
+ // can remove owner access. Keep the unpublished inode private and
83
+ // reopenable until the published name has been identity-fenced.
84
+ fsSync.fchmodSync(tempFd, 0o600);
85
+ await writeNativeInput(tempFd, params.input, params.maxBytes);
86
+ syncNativeFileBestEffort(tempFd);
87
+ if (params.overwrite === false) {
88
+ binding.renameNoReplace(parentFd, tempName, parentFd, params.basename);
89
+ }
90
+ else {
91
+ binding.renameReplace(parentFd, tempName, parentFd, params.basename);
92
+ }
85
93
  renamed = true;
86
94
  targetFd = binding.openBeneath(parentFd, params.basename, nativeOpenFlags(fsSync.constants.O_RDONLY)).fd;
87
95
  const targetIdentity = binding.fstatIdentity(targetFd);
88
96
  if (!targetIdentity.isFile || !sameNativeIdentity(tempIdentity, targetIdentity)) {
89
97
  throw new FsSafeError("path-mismatch", "native write target changed after rename");
90
98
  }
99
+ // Native exclusive creation starts at 0600. Apply the requested mode only
100
+ // after reopening and fencing the published name, both so mode 000 stays
101
+ // verifiable and so broader modes are never exposed before that fence.
102
+ try {
103
+ fsSync.fchmodSync(targetFd, params.mode);
104
+ syncNativeFileBestEffort(targetFd);
105
+ }
106
+ catch (error) {
107
+ fsSync.closeSync(targetFd);
108
+ targetFd = undefined;
109
+ removeNativeCreatedFileIfStillPinned({
110
+ binding,
111
+ parentPath,
112
+ parentFd,
113
+ basename: params.basename,
114
+ created: tempIdentity,
115
+ });
116
+ throw error;
117
+ }
91
118
  syncNativeFileBestEffort(parentFd);
92
119
  return { dev: targetIdentity.dev, ino: targetIdentity.ino };
93
120
  }
@@ -1 +1 @@
1
- {"version":3,"file":"output-sibling.d.ts","sourceRoot":"","sources":["../src/output-sibling.ts"],"names":[],"mappings":"AAgGA,wBAAsB,2BAA2B,CAAC,CAAC,EAAE,MAAM,EAAE;IAC3D,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IACxC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GAAG,OAAO,CAAC,CAAC,CAAC,CA6Cb"}
1
+ {"version":3,"file":"output-sibling.d.ts","sourceRoot":"","sources":["../src/output-sibling.ts"],"names":[],"mappings":"AA+FA,wBAAsB,2BAA2B,CAAC,CAAC,EAAE,MAAM,EAAE;IAC3D,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IACxC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GAAG,OAAO,CAAC,CAAC,CAAC,CA8Cb"}
@@ -66,11 +66,10 @@ async function cleanupTempPath(tempPath, identity) {
66
66
  if (!identity || (!current.isSymbolicLink() && sameFileIdentity(current, identity))) {
67
67
  await fs.rm(tempPath, { force: true });
68
68
  }
69
+ return true;
69
70
  }
70
71
  catch (error) {
71
- if (error.code !== "ENOENT") {
72
- // Cleanup is best-effort; the randomized path remains inside the writable boundary.
73
- }
72
+ return error.code === "ENOENT";
74
73
  }
75
74
  }
76
75
  export async function writeExternalFileViaSibling(params) {
@@ -114,9 +113,11 @@ export async function writeExternalFileViaSibling(params) {
114
113
  return result;
115
114
  }
116
115
  finally {
116
+ let cleanupComplete = renamed;
117
117
  if (!renamed) {
118
- await cleanupTempPath(tempPath, stagedIdentity);
118
+ cleanupComplete = await cleanupTempPath(tempPath, stagedIdentity);
119
119
  }
120
- unregisterTempPath();
120
+ if (cleanupComplete)
121
+ unregisterTempPath();
121
122
  }
122
123
  }
@@ -1 +1 @@
1
- {"version":3,"file":"path-policy.d.ts","sourceRoot":"","sources":["../src/path-policy.ts"],"names":[],"mappings":"AAEA,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;AAED,wBAAsB,2BAA2B,CAAC,MAAM,EAAE;IACxD,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBhB"}
1
+ {"version":3,"file":"path-policy.d.ts","sourceRoot":"","sources":["../src/path-policy.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,gBAAgB,CAAC;AAIxB,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;AAED,wBAAsB,2BAA2B,CAAC,MAAM,EAAE;IACxD,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBhB"}
@@ -1,7 +1,7 @@
1
1
  import fs from "node:fs/promises";
2
- import os from "node:os";
3
2
  import { ROOT_PATH_ALIAS_POLICIES, resolveRootPath, } from "./root-path.js";
4
3
  import { isNotFoundPathError } from "./path.js";
4
+ import { shortPath } from "./short-path.js";
5
5
  export const PATH_ALIAS_POLICIES = ROOT_PATH_ALIAS_POLICIES;
6
6
  export async function assertNoPathAliasEscape(params) {
7
7
  const resolved = await resolveRootPath({
@@ -42,9 +42,3 @@ export async function assertNoHardlinkedFinalPath(params) {
42
42
  throw new Error(`Hardlinked path is not allowed under ${params.boundaryLabel} (${shortPath(params.root)}): ${shortPath(params.filePath)}`);
43
43
  }
44
44
  }
45
- function shortPath(value) {
46
- if (value.startsWith(os.homedir())) {
47
- return `~${value.slice(os.homedir().length)}`;
48
- }
49
- return value;
50
- }
@@ -1 +1 @@
1
- {"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../src/path.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAKzB,OAAO,EACL,4BAA4B,EAC5B,sBAAsB,EACtB,yBAAyB,EACzB,KAAK,yBAAyB,EAC9B,KAAK,2BAA2B,EAChC,KAAK,0BAA0B,GAChC,MAAM,kBAAkB,CAAC;AAM1B,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAWvE;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,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,SAA6B,GAAG,IAAI,CAIjG;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,CA4BlE;AAED,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAElE;AAED,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,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,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,EAAE,CAuBpE;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAOrF"}
1
+ {"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../src/path.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAMzB,OAAO,EACL,4BAA4B,EAC5B,sBAAsB,EACtB,yBAAyB,EACzB,KAAK,yBAAyB,EAC9B,KAAK,2BAA2B,EAChC,KAAK,0BAA0B,GAChC,MAAM,kBAAkB,CAAC;AAM1B,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAWvE;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,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,SAA6B,GAAG,IAAI,CAIjG;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,CA4BlE;AAED,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAElE;AAED,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,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,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,EAAE,CA0BpE;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAOrF"}
package/dist/path.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { FsSafeError } from "./errors.js";
4
+ import { isDriveRelativePath } from "./safe-path-segment.js";
4
5
  export { assertNoUnsafeDeviceReadPath, isUnsafeDeviceReadPath, matchUnsafeDeviceReadPath, } from "./device-path.js";
5
6
  const NOT_FOUND_CODES = new Set(["ENOENT", "ENOTDIR"]);
6
7
  const SYMLINK_OPEN_CODES = new Set(["ELOOP", "EINVAL", "ENOTSUP"]);
@@ -119,6 +120,9 @@ export function splitSafeRelativePath(relativePath) {
119
120
  if (segment === "..") {
120
121
  throw new FsSafeError("invalid-path", "relative path must not contain '..'");
121
122
  }
123
+ if (isDriveRelativePath(segment)) {
124
+ throw new FsSafeError("invalid-path", "relative path must not contain a drive letter");
125
+ }
122
126
  }
123
127
  return segments;
124
128
  }
@@ -0,0 +1,6 @@
1
+ export declare const DEFAULT_PERMISSION_EXEC_TIMEOUT_MS = 30000;
2
+ export declare function executePermissionCommand(command: string, args: string[], timeoutMs?: number): Promise<{
3
+ stdout: string;
4
+ stderr: string;
5
+ }>;
6
+ //# sourceMappingURL=permission-exec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission-exec.d.ts","sourceRoot":"","sources":["../src/permission-exec.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,kCAAkC,QAAS,CAAC;AAEzD,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,SAAS,SAAqC,GAC7C,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAwB7C"}
@@ -0,0 +1,28 @@
1
+ import { execFile } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+ const execFileAsync = promisify(execFile);
4
+ export const DEFAULT_PERMISSION_EXEC_TIMEOUT_MS = 30_000;
5
+ export async function executePermissionCommand(command, args, timeoutMs = DEFAULT_PERMISSION_EXEC_TIMEOUT_MS) {
6
+ try {
7
+ return (await execFileAsync(command, args, {
8
+ encoding: "utf8",
9
+ windowsHide: true,
10
+ maxBuffer: 1024 * 1024,
11
+ timeout: timeoutMs,
12
+ killSignal: "SIGKILL",
13
+ }));
14
+ }
15
+ catch (err) {
16
+ if (err &&
17
+ typeof err === "object" &&
18
+ "killed" in err &&
19
+ err.killed === true &&
20
+ "signal" in err &&
21
+ err.signal === "SIGKILL") {
22
+ throw new Error(`Windows permission inspection timed out after ${timeoutMs}ms`, {
23
+ cause: err,
24
+ });
25
+ }
26
+ throw err;
27
+ }
28
+ }
@@ -0,0 +1,56 @@
1
+ import type { PermissionCheck, PermissionCheckOptions, SafeStatResult } from "./permissions.js";
2
+ export type PermissionExec = (command: string, args: string[]) => Promise<{
3
+ stdout: string;
4
+ stderr: string;
5
+ }>;
6
+ export type WindowsAclEntry = {
7
+ principal: string;
8
+ /** Canonical principal SID when resolved from Windows. */
9
+ sid?: string;
10
+ rights: string[];
11
+ rawRights: string;
12
+ canRead: boolean;
13
+ canWrite: boolean;
14
+ };
15
+ export type WindowsAclSummary = {
16
+ ok: boolean;
17
+ entries: WindowsAclEntry[];
18
+ untrustedWorld: WindowsAclEntry[];
19
+ untrustedGroup: WindowsAclEntry[];
20
+ trusted: WindowsAclEntry[];
21
+ error?: string;
22
+ };
23
+ export type WindowsUserInfoProvider = () => {
24
+ username?: string | null;
25
+ };
26
+ export type IcaclsResetCommandOptions = {
27
+ isDir: boolean;
28
+ env?: NodeJS.ProcessEnv;
29
+ userInfo?: WindowsUserInfoProvider;
30
+ };
31
+ export declare function inspectWindowsPermissions(params: {
32
+ targetPath: string;
33
+ stat: SafeStatResult;
34
+ effectiveIsDir: boolean;
35
+ effectiveMode: number | null;
36
+ bits: number | null;
37
+ opts?: PermissionCheckOptions;
38
+ }): Promise<PermissionCheck>;
39
+ export declare function resolveWindowsUserPrincipal(env?: NodeJS.ProcessEnv, userInfo?: WindowsUserInfoProvider): string | null;
40
+ export declare function parseIcaclsOutput(output: string, targetPath: string): WindowsAclEntry[];
41
+ export declare function summarizeWindowsAcl(entries: WindowsAclEntry[], env?: NodeJS.ProcessEnv): Pick<WindowsAclSummary, "trusted" | "untrustedWorld" | "untrustedGroup">;
42
+ export declare function inspectWindowsAcl(targetPath: string, opts?: {
43
+ env?: NodeJS.ProcessEnv;
44
+ exec?: PermissionExec;
45
+ currentUserSid?: string;
46
+ principalSids?: Record<string, string>;
47
+ principalTranslationFailed?: boolean;
48
+ }): Promise<WindowsAclSummary>;
49
+ export declare function formatWindowsAclSummary(summary: WindowsAclSummary): string;
50
+ export declare function formatIcaclsResetCommand(targetPath: string, opts: IcaclsResetCommandOptions): string;
51
+ export declare function createIcaclsResetCommand(targetPath: string, opts: IcaclsResetCommandOptions): {
52
+ command: string;
53
+ args: string[];
54
+ display: string;
55
+ } | null;
56
+ //# sourceMappingURL=permissions-windows.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions-windows.d.ts","sourceRoot":"","sources":["../src/permissions-windows.ts"],"names":[],"mappings":"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"}