@openclaw/fs-safe 0.5.1 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/README.md +18 -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 +25 -5
  8. package/dist/archive-input.d.ts +7 -0
  9. package/dist/archive-input.d.ts.map +1 -1
  10. package/dist/archive-input.js +11 -1
  11. package/dist/archive-limits.js +1 -1
  12. package/dist/archive-native.d.ts.map +1 -1
  13. package/dist/archive-native.js +6 -3
  14. package/dist/archive-policy.d.ts.map +1 -1
  15. package/dist/archive-policy.js +2 -1
  16. package/dist/archive-read.d.ts.map +1 -1
  17. package/dist/archive-read.js +30 -12
  18. package/dist/archive-staging.d.ts.map +1 -1
  19. package/dist/archive-staging.js +3 -2
  20. package/dist/archive-tar-meta.d.ts.map +1 -1
  21. package/dist/archive-tar-meta.js +7 -1
  22. package/dist/archive-tar.d.ts.map +1 -1
  23. package/dist/archive-tar.js +7 -4
  24. package/dist/archive-zip-entry.d.ts +1 -0
  25. package/dist/archive-zip-entry.d.ts.map +1 -1
  26. package/dist/archive-zip-integrity.d.ts +4 -0
  27. package/dist/archive-zip-integrity.d.ts.map +1 -0
  28. package/dist/archive-zip-integrity.js +44 -0
  29. package/dist/archive-zip-preflight.d.ts.map +1 -1
  30. package/dist/archive-zip-preflight.js +6 -1
  31. package/dist/archive.d.ts.map +1 -1
  32. package/dist/archive.js +5 -2
  33. package/dist/bounded-read-stream.d.ts.map +1 -1
  34. package/dist/bounded-read-stream.js +13 -2
  35. package/dist/deny-mutations.d.ts +1 -0
  36. package/dist/deny-mutations.d.ts.map +1 -1
  37. package/dist/deny-mutations.js +6 -39
  38. package/dist/device-path.d.ts +1 -0
  39. package/dist/device-path.d.ts.map +1 -1
  40. package/dist/device-path.js +6 -6
  41. package/dist/directory-guard.d.ts.map +1 -1
  42. package/dist/directory-guard.js +5 -4
  43. package/dist/error-detail.d.ts +2 -0
  44. package/dist/error-detail.d.ts.map +1 -0
  45. package/dist/error-detail.js +4 -0
  46. package/dist/errors.d.ts.map +1 -1
  47. package/dist/errors.js +3 -0
  48. package/dist/file-hash.d.ts.map +1 -1
  49. package/dist/file-hash.js +4 -1
  50. package/dist/file-lock-sync.d.ts.map +1 -1
  51. package/dist/file-lock-sync.js +88 -35
  52. package/dist/file-store-boundary.d.ts +6 -0
  53. package/dist/file-store-boundary.d.ts.map +1 -1
  54. package/dist/file-store-boundary.js +20 -5
  55. package/dist/file-store-sync-write.d.ts +9 -0
  56. package/dist/file-store-sync-write.d.ts.map +1 -0
  57. package/dist/file-store-sync-write.js +97 -0
  58. package/dist/file-store.d.ts.map +1 -1
  59. package/dist/file-store.js +36 -128
  60. package/dist/filename.d.ts.map +1 -1
  61. package/dist/filename.js +27 -0
  62. package/dist/guarded-mkdir.d.ts.map +1 -1
  63. package/dist/guarded-mkdir.js +5 -6
  64. package/dist/install-path.js +1 -1
  65. package/dist/json-document-store.d.ts.map +1 -1
  66. package/dist/json-document-store.js +19 -13
  67. package/dist/json.d.ts.map +1 -1
  68. package/dist/json.js +1 -3
  69. package/dist/local-file-access.d.ts +3 -2
  70. package/dist/local-file-access.d.ts.map +1 -1
  71. package/dist/local-file-access.js +12 -6
  72. package/dist/local-roots.d.ts.map +1 -1
  73. package/dist/local-roots.js +35 -30
  74. package/dist/move-path.d.ts.map +1 -1
  75. package/dist/move-path.js +51 -12
  76. package/dist/native/darwin-arm64/fs-safe-native.node +0 -0
  77. package/dist/native/darwin-x64/fs-safe-native.node +0 -0
  78. package/dist/native/linux-arm64-gnu/fs-safe-native.node +0 -0
  79. package/dist/native/linux-arm64-musl/fs-safe-native.node +0 -0
  80. package/dist/native/linux-x64-gnu/fs-safe-native.node +0 -0
  81. package/dist/native/linux-x64-musl/fs-safe-native.node +0 -0
  82. package/dist/native/win32-x64-msvc/fs-safe-native.node +0 -0
  83. package/dist/native-binding.d.ts +1 -0
  84. package/dist/native-binding.d.ts.map +1 -1
  85. package/dist/native-operations.d.ts.map +1 -1
  86. package/dist/native-operations.js +14 -1
  87. package/dist/native-pinned-write.d.ts.map +1 -1
  88. package/dist/native-pinned-write.js +44 -17
  89. package/dist/output-sibling.d.ts.map +1 -1
  90. package/dist/output-sibling.js +6 -5
  91. package/dist/path-policy.d.ts.map +1 -1
  92. package/dist/path-policy.js +1 -7
  93. package/dist/path.d.ts.map +1 -1
  94. package/dist/path.js +4 -0
  95. package/dist/permission-exec.d.ts +6 -0
  96. package/dist/permission-exec.d.ts.map +1 -0
  97. package/dist/permission-exec.js +28 -0
  98. package/dist/permissions-windows.d.ts +56 -0
  99. package/dist/permissions-windows.d.ts.map +1 -0
  100. package/dist/permissions-windows.js +285 -0
  101. package/dist/permissions.d.ts +3 -46
  102. package/dist/permissions.d.ts.map +1 -1
  103. package/dist/permissions.js +17 -328
  104. package/dist/pinned-write.js +2 -1
  105. package/dist/regular-file.d.ts.map +1 -1
  106. package/dist/regular-file.js +21 -2
  107. package/dist/replace-directory.d.ts.map +1 -1
  108. package/dist/replace-directory.js +10 -1
  109. package/dist/replace-file-copy-fallback.d.ts +3 -0
  110. package/dist/replace-file-copy-fallback.d.ts.map +1 -1
  111. package/dist/replace-file-copy-fallback.js +18 -11
  112. package/dist/replace-file-descriptor.d.ts +33 -0
  113. package/dist/replace-file-descriptor.d.ts.map +1 -0
  114. package/dist/replace-file-descriptor.js +96 -0
  115. package/dist/replace-file.d.ts +9 -2
  116. package/dist/replace-file.d.ts.map +1 -1
  117. package/dist/replace-file.js +64 -72
  118. package/dist/root-context.d.ts +8 -0
  119. package/dist/root-context.d.ts.map +1 -1
  120. package/dist/root-context.js +43 -2
  121. package/dist/root-errors.d.ts +7 -0
  122. package/dist/root-errors.d.ts.map +1 -1
  123. package/dist/root-errors.js +46 -1
  124. package/dist/root-file.d.ts +1 -0
  125. package/dist/root-file.d.ts.map +1 -1
  126. package/dist/root-file.js +2 -0
  127. package/dist/root-impl.d.ts.map +1 -1
  128. package/dist/root-impl.js +213 -168
  129. package/dist/root-path-symlink.d.ts +3 -0
  130. package/dist/root-path-symlink.d.ts.map +1 -0
  131. package/dist/root-path-symlink.js +35 -0
  132. package/dist/root-path.d.ts +1 -0
  133. package/dist/root-path.d.ts.map +1 -1
  134. package/dist/root-path.js +129 -190
  135. package/dist/root-walk.d.ts.map +1 -1
  136. package/dist/root-walk.js +15 -0
  137. package/dist/safe-path-segment.d.ts +2 -0
  138. package/dist/safe-path-segment.d.ts.map +1 -1
  139. package/dist/safe-path-segment.js +12 -0
  140. package/dist/secret-file.d.ts.map +1 -1
  141. package/dist/secret-file.js +19 -3
  142. package/dist/secure-temp-dir.d.ts.map +1 -1
  143. package/dist/secure-temp-dir.js +6 -1
  144. package/dist/short-path.d.ts +2 -0
  145. package/dist/short-path.d.ts.map +1 -0
  146. package/dist/short-path.js +7 -0
  147. package/dist/sidecar-lock-acquire.d.ts +30 -0
  148. package/dist/sidecar-lock-acquire.d.ts.map +1 -0
  149. package/dist/sidecar-lock-acquire.js +260 -0
  150. package/dist/sidecar-lock-policy.d.ts +3 -0
  151. package/dist/sidecar-lock-policy.d.ts.map +1 -1
  152. package/dist/sidecar-lock-policy.js +20 -3
  153. package/dist/sidecar-lock-reclaim.d.ts +5 -1
  154. package/dist/sidecar-lock-reclaim.d.ts.map +1 -1
  155. package/dist/sidecar-lock-reclaim.js +59 -9
  156. package/dist/sidecar-lock.d.ts.map +1 -1
  157. package/dist/sidecar-lock.js +27 -226
  158. package/dist/temp-target.d.ts.map +1 -1
  159. package/dist/temp-target.js +15 -4
  160. package/dist/test-hooks.d.ts +1 -0
  161. package/dist/test-hooks.d.ts.map +1 -1
  162. package/dist/timing.d.ts +2 -0
  163. package/dist/timing.d.ts.map +1 -1
  164. package/dist/timing.js +9 -0
  165. package/dist/walk.d.ts.map +1 -1
  166. package/dist/walk.js +15 -0
  167. package/docs/advanced.md +1 -1
  168. package/docs/archive.md +27 -20
  169. package/docs/assets/readme-banner.jpg +0 -0
  170. package/docs/atomic.md +48 -19
  171. package/docs/errors.md +31 -7
  172. package/docs/file-store.md +10 -1
  173. package/docs/filename.md +17 -16
  174. package/docs/index.md +1 -0
  175. package/docs/install-path.md +9 -6
  176. package/docs/json.md +8 -2
  177. package/docs/local-roots.md +79 -103
  178. package/docs/native-helper.md +11 -5
  179. package/docs/native.md +2 -2
  180. package/docs/output.md +8 -3
  181. package/docs/path-scope.md +4 -1
  182. package/docs/path.md +7 -3
  183. package/docs/permissions.md +8 -0
  184. package/docs/public-api.md +109 -0
  185. package/docs/quickstart.md +6 -2
  186. package/docs/reading.md +13 -10
  187. package/docs/root.md +26 -4
  188. package/docs/secret-file.md +17 -4
  189. package/docs/security-model.md +21 -4
  190. package/docs/sidecar-lock.md +15 -5
  191. package/docs/temp.md +37 -5
  192. package/docs/test-hooks.md +2 -0
  193. package/docs/types.md +32 -15
  194. package/docs/writing.md +25 -12
  195. package/package.json +7 -3
  196. package/dist/archive-file-io.d.ts +0 -9
  197. package/dist/archive-file-io.d.ts.map +0 -1
  198. package/dist/archive-file-io.js +0 -11
  199. package/dist/file-store-source.d.ts +0 -5
  200. package/dist/file-store-source.d.ts.map +0 -1
  201. package/dist/file-store-source.js +0 -30
  202. package/dist/file-url.d.ts +0 -2
  203. package/dist/file-url.d.ts.map +0 -1
  204. package/dist/file-url.js +0 -1
  205. package/dist/path-stat.d.ts +0 -4
  206. package/dist/path-stat.d.ts.map +0 -1
  207. package/dist/path-stat.js +0 -15
  208. package/dist/windows-permissions-native.d.ts +0 -9
  209. package/dist/windows-permissions-native.d.ts.map +0 -1
  210. package/dist/windows-permissions-native.js +0 -31
@@ -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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"}
@@ -0,0 +1,285 @@
1
+ import os from "node:os";
2
+ import { getNativeBinding } from "./native.js";
3
+ import { executePermissionCommand } from "./permission-exec.js";
4
+ import { normalizeLowercaseStringOrEmpty } from "./string-coerce.js";
5
+ import { resolveWindowsSystemCommand } from "./windows-command.js";
6
+ import { inspectWindowsOwner, resolveWindowsCurrentUserSid, resolveWindowsPrincipalSids, } from "./windows-owner.js";
7
+ const INHERIT_FLAGS = new Set(["I", "OI", "CI", "IO", "NP"]);
8
+ const WORLD_PRINCIPALS = new Set(["everyone", "users", "builtin\\users", "authenticated users", "nt authority\\authenticated users", "anonymous logon", "nt authority\\anonymous logon", "guests", "builtin\\guests", "interactive", "nt authority\\interactive", "network", "nt authority\\network", "local"]);
9
+ const TRUSTED_BASE = new Set([
10
+ "nt authority\\system",
11
+ "system",
12
+ "builtin\\administrators",
13
+ "creator owner",
14
+ "autorite nt\\système",
15
+ "nt-autorität\\system",
16
+ "autoridad nt\\system",
17
+ "autoridade nt\\system",
18
+ ]);
19
+ const WORLD_SUFFIXES = ["\\users", "\\authenticated users"];
20
+ const SID_RE = /^\*?s-\d+-\d+(-\d+)+$/i;
21
+ const TRUSTED_SIDS = new Set([
22
+ "s-1-5-18",
23
+ "s-1-5-32-544",
24
+ "s-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464",
25
+ ]);
26
+ const WORLD_SIDS = new Set(["s-1-1-0", "s-1-5-11", "s-1-5-32-545", "s-1-5-7", "s-1-5-32-546", "s-1-5-4", "s-1-2-0", "s-1-5-2"]);
27
+ const STATUS_PREFIXES = [
28
+ "successfully processed",
29
+ "processed",
30
+ "failed processing",
31
+ "no mapping between account names",
32
+ ];
33
+ function stripDiacritics(value) {
34
+ return value.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
35
+ }
36
+ const TRUSTED_BASE_ASCII = new Set([...TRUSTED_BASE].map(stripDiacritics));
37
+ const normalize = (value) => normalizeLowercaseStringOrEmpty(value);
38
+ const defaultWindowsUserInfo = () => os.userInfo();
39
+ const defaultPermissionExec = executePermissionCommand;
40
+ function inspectWindowsPermissionsNative(params) {
41
+ const native = getNativeBinding();
42
+ if (!native)
43
+ return undefined;
44
+ try {
45
+ const facts = native.readOwnerAndDacl(params.targetPath);
46
+ if (facts.fallbackRequired)
47
+ return undefined;
48
+ return {
49
+ ok: true,
50
+ isSymlink: params.stat.isSymlink,
51
+ isDir: params.effectiveIsDir,
52
+ mode: params.effectiveMode,
53
+ bits: params.bits,
54
+ source: "windows-acl",
55
+ worldWritable: facts.worldWritable,
56
+ groupWritable: facts.groupWritable,
57
+ worldReadable: facts.worldReadable,
58
+ groupReadable: facts.groupReadable,
59
+ ownerSid: facts.ownerSid,
60
+ ownerTrusted: facts.ownerClass !== "foreign",
61
+ aclSummary: `native owner=${facts.ownerClass} world=` +
62
+ `${facts.worldReadable ? "r" : "-"}${facts.worldWritable ? "w" : "-"} ` +
63
+ `group=${facts.groupReadable ? "r" : "-"}${facts.groupWritable ? "w" : "-"}`,
64
+ };
65
+ }
66
+ catch {
67
+ return undefined;
68
+ }
69
+ }
70
+ export async function inspectWindowsPermissions(params) {
71
+ const native = inspectWindowsPermissionsNative(params);
72
+ if (native)
73
+ return native;
74
+ const unverified = {
75
+ ok: true,
76
+ isSymlink: params.stat.isSymlink,
77
+ isDir: params.effectiveIsDir,
78
+ mode: params.effectiveMode,
79
+ bits: params.bits,
80
+ source: "unknown",
81
+ worldWritable: false,
82
+ groupWritable: false,
83
+ worldReadable: false,
84
+ groupReadable: false,
85
+ };
86
+ const owner = await inspectWindowsOwner({
87
+ targetPath: params.targetPath,
88
+ env: params.opts?.env,
89
+ exec: params.opts?.exec ?? defaultPermissionExec,
90
+ });
91
+ if (owner.error) {
92
+ const error = `Windows owner inspection failed: ${owner.error}`;
93
+ return { ...unverified, ownerError: owner.error, error };
94
+ }
95
+ const acl = await inspectWindowsAcl(params.targetPath, {
96
+ env: params.opts?.env,
97
+ exec: params.opts?.exec,
98
+ currentUserSid: owner.currentUserSid,
99
+ principalSids: owner.principalSids,
100
+ principalTranslationFailed: owner.principalTranslationFailed,
101
+ });
102
+ const ownerFields = {
103
+ ...(owner.sid ? { ownerSid: owner.sid } : {}),
104
+ ...(owner.trusted !== undefined ? { ownerTrusted: owner.trusted } : {}),
105
+ ...(owner.error ? { ownerError: owner.error } : {}),
106
+ };
107
+ if (!acl.ok)
108
+ return { ...unverified, ...ownerFields, error: acl.error };
109
+ return {
110
+ ok: true,
111
+ isSymlink: params.stat.isSymlink,
112
+ isDir: params.effectiveIsDir,
113
+ mode: params.effectiveMode,
114
+ bits: params.bits,
115
+ source: "windows-acl",
116
+ worldWritable: acl.untrustedWorld.some((entry) => entry.canWrite),
117
+ groupWritable: acl.untrustedGroup.some((entry) => entry.canWrite),
118
+ worldReadable: acl.untrustedWorld.some((entry) => entry.canRead),
119
+ groupReadable: acl.untrustedGroup.some((entry) => entry.canRead),
120
+ ...ownerFields,
121
+ aclSummary: formatWindowsAclSummary(acl),
122
+ };
123
+ }
124
+ function normalizeSid(value) {
125
+ const normalized = normalize(value);
126
+ return normalized.startsWith("*") ? normalized.slice(1) : normalized;
127
+ }
128
+ export function resolveWindowsUserPrincipal(env, userInfo = defaultWindowsUserInfo) {
129
+ const username = env?.USERNAME?.trim() || userInfo().username?.trim();
130
+ if (!username)
131
+ return null;
132
+ const domain = env?.USERDOMAIN?.trim();
133
+ return domain ? `${domain}\\${username}` : username;
134
+ }
135
+ function buildTrustedPrincipals(env) {
136
+ const trusted = new Set(TRUSTED_BASE);
137
+ const principal = resolveWindowsUserPrincipal(env);
138
+ if (principal) {
139
+ trusted.add(normalize(principal));
140
+ const userOnly = principal.split("\\").at(-1);
141
+ if (userOnly)
142
+ trusted.add(normalize(userOnly));
143
+ }
144
+ const userSid = normalizeSid(env?.USERSID ?? "");
145
+ if (userSid && SID_RE.test(userSid) && !WORLD_SIDS.has(userSid))
146
+ trusted.add(userSid);
147
+ return trusted;
148
+ }
149
+ function classifyPrincipal(principal, trustedPrincipals) {
150
+ const normalized = normalize(principal);
151
+ if (SID_RE.test(normalized)) {
152
+ const sid = normalizeSid(normalized);
153
+ if (WORLD_SIDS.has(sid))
154
+ return "world";
155
+ if (TRUSTED_SIDS.has(sid) || trustedPrincipals.has(sid))
156
+ return "trusted";
157
+ return "group";
158
+ }
159
+ if (trustedPrincipals.has(normalized) || TRUSTED_BASE.has(normalized))
160
+ return "trusted";
161
+ if (WORLD_PRINCIPALS.has(normalized) || WORLD_SUFFIXES.some((suffix) => normalized.endsWith(suffix)))
162
+ return "world";
163
+ const stripped = stripDiacritics(normalized);
164
+ return stripped !== normalized && TRUSTED_BASE_ASCII.has(stripped) ? "trusted" : "group";
165
+ }
166
+ function rightsFromTokens(tokens) {
167
+ const upper = tokens.join("").toUpperCase();
168
+ return {
169
+ canWrite: upper.includes("F") || upper.includes("M") || upper.includes("W") || upper.includes("D"),
170
+ canRead: upper.includes("F") || upper.includes("M") || upper.includes("R"),
171
+ };
172
+ }
173
+ function stripTargetPrefix(params) {
174
+ if (params.lowerLine.startsWith(params.lowerTarget))
175
+ return params.trimmedLine.slice(params.normalizedTarget.length).trim();
176
+ if (params.lowerLine.startsWith(params.quotedLower))
177
+ return params.trimmedLine.slice(params.quotedTarget.length).trim();
178
+ return params.trimmedLine;
179
+ }
180
+ function parseAceEntry(entry) {
181
+ if (!entry.includes("("))
182
+ return null;
183
+ const idx = entry.indexOf(":");
184
+ if (idx === -1)
185
+ return null;
186
+ const principal = entry.slice(0, idx).trim();
187
+ const rawRights = entry.slice(idx + 1).trim();
188
+ const tokens = rawRights.match(/\(([^)]+)\)/g)?.map((token) => token.slice(1, -1).trim()).filter(Boolean) ?? [];
189
+ if (tokens.some((token) => token.toUpperCase() === "DENY"))
190
+ return null;
191
+ const rights = tokens.filter((token) => !INHERIT_FLAGS.has(token.toUpperCase()));
192
+ if (rights.length === 0)
193
+ return null;
194
+ const normalizedPrincipal = normalizeSid(principal);
195
+ return { principal, ...(SID_RE.test(normalizedPrincipal) ? { sid: normalizedPrincipal } : {}), rights, rawRights, ...rightsFromTokens(rights) };
196
+ }
197
+ export function parseIcaclsOutput(output, targetPath) {
198
+ const entries = [];
199
+ const normalizedTarget = targetPath.trim();
200
+ const lowerTarget = normalizedTarget.toLowerCase();
201
+ const quotedTarget = `"${normalizedTarget}"`;
202
+ const quotedLower = quotedTarget.toLowerCase();
203
+ for (const rawLine of output.split(/\r?\n/)) {
204
+ const line = rawLine.trimEnd();
205
+ if (!line.trim())
206
+ continue;
207
+ const trimmed = line.trim();
208
+ const lowerLine = trimmed.toLowerCase();
209
+ if (STATUS_PREFIXES.some((prefix) => lowerLine.startsWith(prefix)))
210
+ continue;
211
+ const parsed = parseAceEntry(stripTargetPrefix({ trimmedLine: trimmed, lowerLine, normalizedTarget, lowerTarget, quotedTarget, quotedLower }));
212
+ if (parsed)
213
+ entries.push(parsed);
214
+ }
215
+ return entries;
216
+ }
217
+ export function summarizeWindowsAcl(entries, env) {
218
+ const trustedPrincipals = buildTrustedPrincipals(env);
219
+ const trusted = [];
220
+ const untrustedWorld = [];
221
+ const untrustedGroup = [];
222
+ for (const entry of entries) {
223
+ const classification = classifyPrincipal(entry.sid ?? entry.principal, trustedPrincipals);
224
+ if (classification === "trusted")
225
+ trusted.push(entry);
226
+ else if (classification === "world")
227
+ untrustedWorld.push(entry);
228
+ else
229
+ untrustedGroup.push(entry);
230
+ }
231
+ return { trusted, untrustedWorld, untrustedGroup };
232
+ }
233
+ export async function inspectWindowsAcl(targetPath, opts) {
234
+ const exec = opts?.exec ?? defaultPermissionExec;
235
+ try {
236
+ if (opts?.principalTranslationFailed)
237
+ throw new Error("Windows ACL principal SID translation failed");
238
+ const { stdout, stderr } = await exec(resolveWindowsSystemCommand("icacls.exe", opts?.env), [targetPath]);
239
+ let entries = parseIcaclsOutput(`${stdout}\n${stderr}`.trim(), targetPath);
240
+ if (!entries.length)
241
+ throw new Error("Windows ACL output could not be verified");
242
+ const unresolvedPrincipals = entries.filter((entry) => !entry.sid).map((entry) => entry.principal);
243
+ const principalSids = await resolveWindowsPrincipalSids({ principals: unresolvedPrincipals, known: opts?.principalSids, env: opts?.env, exec });
244
+ entries = entries.map((entry) => {
245
+ const sid = entry.sid ?? principalSids[entry.principal.toLowerCase()];
246
+ if (!sid)
247
+ throw new Error(`Windows ACL principal SID could not be verified: ${entry.principal}`);
248
+ return { ...entry, sid };
249
+ });
250
+ let currentUserSid = normalizeSid(opts?.currentUserSid ?? "");
251
+ let effectiveEnv = currentUserSid ? { USERSID: currentUserSid } : undefined;
252
+ let { trusted, untrustedWorld, untrustedGroup } = summarizeWindowsAcl(entries, effectiveEnv);
253
+ if (!currentUserSid && untrustedGroup.some((entry) => entry.sid && !TRUSTED_SIDS.has(entry.sid))) {
254
+ currentUserSid = (await resolveWindowsCurrentUserSid({ exec, env: opts?.env })) ?? "";
255
+ if (currentUserSid) {
256
+ effectiveEnv = { USERSID: currentUserSid };
257
+ ({ trusted, untrustedWorld, untrustedGroup } = summarizeWindowsAcl(entries, effectiveEnv));
258
+ }
259
+ }
260
+ return { ok: true, entries, trusted, untrustedWorld, untrustedGroup };
261
+ }
262
+ catch (err) {
263
+ return { ok: false, entries: [], trusted: [], untrustedWorld: [], untrustedGroup: [], error: String(err) };
264
+ }
265
+ }
266
+ export function formatWindowsAclSummary(summary) {
267
+ if (!summary.ok)
268
+ return "unknown";
269
+ const untrusted = [...summary.untrustedWorld, ...summary.untrustedGroup];
270
+ return untrusted.length === 0 ? "trusted-only" : untrusted.map((entry) => `${entry.principal}:${entry.rawRights}`).join(", ");
271
+ }
272
+ export function formatIcaclsResetCommand(targetPath, opts) {
273
+ const command = resolveWindowsSystemCommand("icacls.exe", opts.env);
274
+ const user = resolveWindowsUserPrincipal(opts.env, opts.userInfo) ?? "%USERNAME%";
275
+ const grant = opts.isDir ? "(OI)(CI)F" : "F";
276
+ return [command, `"${targetPath}"`, "/inheritance:r", "/grant:r", `"${user}:${grant}"`, "/grant:r", `"*S-1-5-18:${grant}"`].join(" ");
277
+ }
278
+ export function createIcaclsResetCommand(targetPath, opts) {
279
+ const user = resolveWindowsUserPrincipal(opts.env, opts.userInfo);
280
+ if (!user)
281
+ return null;
282
+ const grant = opts.isDir ? "(OI)(CI)F" : "F";
283
+ const args = [targetPath, "/inheritance:r", "/grant:r", `${user}:${grant}`, "/grant:r", `*S-1-5-18:${grant}`];
284
+ return { command: resolveWindowsSystemCommand("icacls.exe", opts.env), args, display: formatIcaclsResetCommand(targetPath, opts) };
285
+ }
@@ -1,7 +1,6 @@
1
- export type PermissionExec = (command: string, args: string[]) => Promise<{
2
- stdout: string;
3
- stderr: string;
4
- }>;
1
+ import { type PermissionExec } from "./permissions-windows.js";
2
+ export { createIcaclsResetCommand, formatIcaclsResetCommand, formatWindowsAclSummary, inspectWindowsAcl, parseIcaclsOutput, resolveWindowsUserPrincipal, summarizeWindowsAcl, } from "./permissions-windows.js";
3
+ export type { IcaclsResetCommandOptions, PermissionExec, WindowsAclEntry, WindowsAclSummary, WindowsUserInfoProvider, } from "./permissions-windows.js";
5
4
  export type PermissionCheck = {
6
5
  ok: boolean;
7
6
  isSymlink: boolean;
@@ -36,31 +35,6 @@ export type SafeStatResult = {
36
35
  gid: number | null;
37
36
  error?: string;
38
37
  };
39
- export type WindowsAclEntry = {
40
- principal: string;
41
- /** Canonical principal SID when resolved from Windows. */
42
- sid?: string;
43
- rights: string[];
44
- rawRights: string;
45
- canRead: boolean;
46
- canWrite: boolean;
47
- };
48
- export type WindowsAclSummary = {
49
- ok: boolean;
50
- entries: WindowsAclEntry[];
51
- untrustedWorld: WindowsAclEntry[];
52
- untrustedGroup: WindowsAclEntry[];
53
- trusted: WindowsAclEntry[];
54
- error?: string;
55
- };
56
- export type WindowsUserInfoProvider = () => {
57
- username?: string | null;
58
- };
59
- export type IcaclsResetCommandOptions = {
60
- isDir: boolean;
61
- env?: NodeJS.ProcessEnv;
62
- userInfo?: WindowsUserInfoProvider;
63
- };
64
38
  export declare function safeStat(targetPath: string): Promise<SafeStatResult>;
65
39
  export declare function inspectPathPermissions(targetPath: string, opts?: PermissionCheckOptions): Promise<PermissionCheck>;
66
40
  export declare function formatPermissionDetail(targetPath: string, perms: PermissionCheck): string;
@@ -77,21 +51,4 @@ export declare function isWorldWritable(bits: number | null): boolean;
77
51
  export declare function isGroupWritable(bits: number | null): boolean;
78
52
  export declare function isWorldReadable(bits: number | null): boolean;
79
53
  export declare function isGroupReadable(bits: number | null): boolean;
80
- export declare function resolveWindowsUserPrincipal(env?: NodeJS.ProcessEnv, userInfo?: WindowsUserInfoProvider): string | null;
81
- export declare function parseIcaclsOutput(output: string, targetPath: string): WindowsAclEntry[];
82
- export declare function summarizeWindowsAcl(entries: WindowsAclEntry[], env?: NodeJS.ProcessEnv): Pick<WindowsAclSummary, "trusted" | "untrustedWorld" | "untrustedGroup">;
83
- export declare function inspectWindowsAcl(targetPath: string, opts?: {
84
- env?: NodeJS.ProcessEnv;
85
- exec?: PermissionExec;
86
- currentUserSid?: string;
87
- principalSids?: Record<string, string>;
88
- principalTranslationFailed?: boolean;
89
- }): Promise<WindowsAclSummary>;
90
- export declare function formatWindowsAclSummary(summary: WindowsAclSummary): string;
91
- export declare function formatIcaclsResetCommand(targetPath: string, opts: IcaclsResetCommandOptions): string;
92
- export declare function createIcaclsResetCommand(targetPath: string, opts: IcaclsResetCommandOptions): {
93
- command: string;
94
- args: string[];
95
- display: string;
96
- } | null;
97
54
  //# sourceMappingURL=permissions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"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"}