@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,14 +2,16 @@ import fsSync from "node:fs";
2
2
  import fs from "node:fs/promises";
3
3
  import { Readable } from "node:stream";
4
4
  import { readFileHandleBounded } from "./bounded-read.js";
5
- import { ArchiveFormatError } from "./archive-errors.js";
5
+ import { ArchiveFormatError, ArchiveSecurityError, isArchiveFormatErrorMessage, } from "./archive-errors.js";
6
+ import { formatErrorDetail } from "./error-detail.js";
6
7
  import { normalizeArchiveEntryPath, validateArchiveEntryPath, } from "./archive-entry.js";
7
8
  import { resolveArchiveKind } from "./archive-kind.js";
8
9
  import { DEFAULT_MAX_ARCHIVE_BYTES_ZIP, DEFAULT_MAX_ENTRIES, DEFAULT_MAX_META_ENTRY_BYTES, ArchiveLimitError, ARCHIVE_LIMIT_ERROR_CODE, } from "./archive-limits.js";
9
10
  import { readTarEntryInfo } from "./archive-tar.js";
10
11
  import { preflightTarMetadata } from "./archive-tar-meta.js";
11
- import { importOptionalTar } from "./archive-tar-runtime.js";
12
+ import { importOptionalTar, normalizeTarParserError } from "./archive-tar-runtime.js";
12
13
  import { loadZipArchiveWithPreflight } from "./archive-zip-preflight.js";
14
+ import { createZipIntegrityTransform, normalizeZipIntegrityError, } from "./archive-zip-integrity.js";
13
15
  import { FsSafeError } from "./errors.js";
14
16
  import { sameFileIdentity } from "./file-identity.js";
15
17
  import { getNativeBinding } from "./native.js";
@@ -20,7 +22,7 @@ function normalizedRequestedEntry(entryPath) {
20
22
  validateArchiveEntryPath(entryPath, { escapeLabel: "archive root" });
21
23
  const normalized = normalizeArchiveEntryPath(entryPath).replace(/^\.\//, "");
22
24
  if (!normalized || normalized.endsWith("/")) {
23
- throw new Error(`archive entry is not a file: ${entryPath}`);
25
+ throw new Error(`archive entry is not a file: ${formatErrorDetail(entryPath)}`);
24
26
  }
25
27
  return normalized;
26
28
  }
@@ -96,16 +98,18 @@ async function readZipEntry(buffer, entryPath, maxBytes) {
96
98
  });
97
99
  const entry = archive.files[entryPath];
98
100
  if (!entry || entry.dir) {
99
- throw new Error(`archive entry not found: ${entryPath}`);
101
+ throw new Error(`archive entry not found: ${formatErrorDetail(entryPath)}`);
100
102
  }
101
103
  if (typeof entry.unixPermissions === "number" &&
102
104
  (entry.unixPermissions & ZIP_UNIX_FILE_TYPE_MASK) === ZIP_UNIX_SYMLINK_TYPE) {
103
- throw new Error(`archive entry is a link: ${entryPath}`);
105
+ throw new Error(`archive entry is a link: ${formatErrorDetail(entryPath)}`);
104
106
  }
105
107
  const stream = typeof entry.nodeStream === "function"
106
108
  ? entry.nodeStream()
107
109
  : Readable.from(await entry.async("nodebuffer"));
108
- return await readStreamBounded(stream, maxBytes);
110
+ const integrity = createZipIntegrityTransform(entry);
111
+ stream.once("error", (error) => integrity.destroy(normalizeZipIntegrityError(error)));
112
+ return await readStreamBounded(stream.pipe(integrity), maxBytes);
109
113
  }
110
114
  async function readTarEntry(archivePath, entryPath, maxBytes) {
111
115
  const tar = await importOptionalTar();
@@ -114,33 +118,49 @@ async function readTarEntry(archivePath, entryPath, maxBytes) {
114
118
  maxMetaEntryBytes: DEFAULT_MAX_META_ENTRY_BYTES,
115
119
  });
116
120
  let matched;
117
- await tar.t({
118
- file: archivePath,
119
- strict: true,
120
- maxMetaEntrySize: DEFAULT_MAX_META_ENTRY_BYTES,
121
- onReadEntry(entry) {
122
- const info = readTarEntryInfo(entry);
123
- validateArchiveEntryPath(info.path, { escapeLabel: "archive root" });
124
- const normalized = normalizeArchiveEntryPath(info.path).replace(/^\.\//, "");
125
- if (normalized !== entryPath) {
126
- entry.resume();
127
- return;
128
- }
129
- if (info.type !== "File" && info.type !== "OldFile" && info.type !== "ContiguousFile") {
130
- matched = Promise.reject(new Error(`archive entry is not a file: ${entryPath}`));
131
- entry.resume();
132
- return;
133
- }
134
- if (info.size > maxBytes) {
135
- matched = Promise.reject(new ArchiveLimitError(ARCHIVE_LIMIT_ERROR_CODE.ENTRY_EXTRACTED_SIZE_EXCEEDS_LIMIT));
136
- entry.resume();
137
- return;
138
- }
139
- matched = readStreamBounded(entry, maxBytes);
140
- },
141
- });
121
+ let entryError;
122
+ const seenPaths = new Set();
123
+ try {
124
+ await tar.t({
125
+ file: archivePath,
126
+ strict: true,
127
+ maxMetaEntrySize: DEFAULT_MAX_META_ENTRY_BYTES,
128
+ onReadEntry(entry) {
129
+ const info = readTarEntryInfo(entry);
130
+ validateArchiveEntryPath(info.path, { escapeLabel: "archive root" });
131
+ const normalized = normalizeArchiveEntryPath(info.path).replace(/^\.\//, "");
132
+ if (seenPaths.has(normalized)) {
133
+ entryError ??= new ArchiveSecurityError("entry-path", `archive contains duplicate entry path: ${formatErrorDetail(normalized)}`);
134
+ entry.resume();
135
+ return;
136
+ }
137
+ seenPaths.add(normalized);
138
+ if (normalized !== entryPath) {
139
+ entry.resume();
140
+ return;
141
+ }
142
+ if (info.type !== "File" && info.type !== "OldFile" && info.type !== "ContiguousFile") {
143
+ entryError ??= new Error(`archive entry is not a file: ${formatErrorDetail(entryPath)}`);
144
+ entry.resume();
145
+ return;
146
+ }
147
+ if (info.size > maxBytes) {
148
+ entryError ??= new ArchiveLimitError(ARCHIVE_LIMIT_ERROR_CODE.ENTRY_EXTRACTED_SIZE_EXCEEDS_LIMIT);
149
+ entry.resume();
150
+ return;
151
+ }
152
+ matched = readStreamBounded(entry, maxBytes);
153
+ },
154
+ });
155
+ }
156
+ catch (error) {
157
+ throw normalizeTarParserError(error);
158
+ }
159
+ if (entryError) {
160
+ throw entryError;
161
+ }
142
162
  if (!matched) {
143
- throw new Error(`archive entry not found: ${entryPath}`);
163
+ throw new Error(`archive entry not found: ${formatErrorDetail(entryPath)}`);
144
164
  }
145
165
  return await matched;
146
166
  }
@@ -161,19 +181,24 @@ export async function readArchiveEntry(archivePath, entryPath, options) {
161
181
  const signal = new AbortController().signal;
162
182
  const manifest = await native.inspectArchiveNative(staged.path, kind, DEFAULT_MAX_ENTRIES, DEFAULT_MAX_META_ENTRY_BYTES, DEFAULT_MAX_ARCHIVE_BYTES_ZIP, signal);
163
183
  let rawEntryPath;
184
+ const seenPaths = new Set();
164
185
  for (const entry of manifest) {
165
186
  validateArchiveEntryPath(entry.path, { escapeLabel: "archive root" });
166
187
  const normalized = normalizeArchiveEntryPath(entry.path).replace(/^\.\//, "");
188
+ if (seenPaths.has(normalized)) {
189
+ throw new ArchiveSecurityError("entry-path", `archive contains duplicate entry path: ${formatErrorDetail(normalized)}`);
190
+ }
191
+ seenPaths.add(normalized);
167
192
  if (normalized === requestedEntry) {
168
193
  if (entry.kind !== "file") {
169
- throw new Error(`archive entry is not a file: ${entryPath}`);
194
+ throw new Error(`archive entry is not a file: ${formatErrorDetail(entryPath)}`);
170
195
  }
171
196
  rawEntryPath = entry.path;
172
- break;
173
197
  }
174
198
  }
175
- if (!rawEntryPath)
176
- throw new Error(`archive entry not found: ${entryPath}`);
199
+ if (!rawEntryPath) {
200
+ throw new Error(`archive entry not found: ${formatErrorDetail(entryPath)}`);
201
+ }
177
202
  return await native.readArchiveEntryNative(staged.path, kind, rawEntryPath, options.maxBytes, DEFAULT_MAX_ENTRIES, DEFAULT_MAX_META_ENTRY_BYTES, signal);
178
203
  }
179
204
  catch (error) {
@@ -181,7 +206,7 @@ export async function readArchiveEntry(archivePath, entryPath, options) {
181
206
  error.message.includes(ARCHIVE_LIMIT_ERROR_CODE.ENTRY_EXTRACTED_SIZE_EXCEEDS_LIMIT)) {
182
207
  throw new ArchiveLimitError(ARCHIVE_LIMIT_ERROR_CODE.ENTRY_EXTRACTED_SIZE_EXCEEDS_LIMIT);
183
208
  }
184
- if (error instanceof Error && error.message.includes("archive-header-invalid")) {
209
+ if (error instanceof Error && isArchiveFormatErrorMessage(error.message)) {
185
210
  throw new ArchiveFormatError(error.message, { cause: error });
186
211
  }
187
212
  throw error;
@@ -1 +1 @@
1
- {"version":3,"file":"archive-staging.d.ts","sourceRoot":"","sources":["../src/archive-staging.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,oBAAoB,EAErB,MAAM,qBAAqB,CAAC;AAU7B,OAAO,EAAE,oBAAoB,EAAE,KAAK,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAkC1F,wBAAsB,4BAA4B,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA4BnF;AA4DD,wBAAsB,wBAAwB,CAAC,MAAM,EAAE;IACrD,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;CACtB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuChB;AAyHD,wBAAsB,4BAA4B,CAAC,CAAC,EAAE,MAAM,EAAE;IAC5D,kBAAkB,EAAE,MAAM,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,GAAG,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;CACzC,GAAG,OAAO,CAAC,CAAC,CAAC,CA6Bb;AAED,wBAAsB,iCAAiC,CAAC,MAAM,EAAE;IAC9D,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;CAC5B,GAAG,OAAO,CAAC,IAAI,CAAC,CAgFhB;AAED,wBAAgB,kCAAkC,CAAC,YAAY,EAAE,MAAM,GAAG,oBAAoB,CAE7F"}
1
+ {"version":3,"file":"archive-staging.d.ts","sourceRoot":"","sources":["../src/archive-staging.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,oBAAoB,EAErB,MAAM,qBAAqB,CAAC;AAW7B,OAAO,EAAE,oBAAoB,EAAE,KAAK,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAkC1F,wBAAsB,4BAA4B,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA4BnF;AA4DD,wBAAsB,wBAAwB,CAAC,MAAM,EAAE;IACrD,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;CACtB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuChB;AAyHD,wBAAsB,4BAA4B,CAAC,CAAC,EAAE,MAAM,EAAE;IAC5D,kBAAkB,EAAE,MAAM,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,GAAG,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;CACzC,GAAG,OAAO,CAAC,CAAC,CAAC,CA6Bb;AAED,wBAAsB,iCAAiC,CAAC,MAAM,EAAE;IAC9D,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;CAC5B,GAAG,OAAO,CAAC,IAAI,CAAC,CAkFhB;AAED,wBAAgB,kCAAkC,CAAC,YAAY,EAAE,MAAM,GAAG,oBAAoB,CAE7F"}
@@ -4,6 +4,7 @@ import path from "node:path";
4
4
  import { assertAsyncDirectoryGuard, createAsyncDirectoryGuard, } from "./directory-guard.js";
5
5
  import { ArchiveSecurityError, } from "./archive-errors.js";
6
6
  import { FsSafeError } from "./errors.js";
7
+ import { formatErrorDetail } from "./error-detail.js";
7
8
  import { resolveOpenedFileRealPathForHandle, root } from "./root.js";
8
9
  import { isNotFoundPathError, isPathInside } from "./path.js";
9
10
  import { resolveSecureTempRoot } from "./secure-temp-dir.js";
@@ -12,7 +13,7 @@ const ERROR_ARCHIVE_ENTRY_TRAVERSES_SYMLINK = "archive entry traverses symlink i
12
13
  const ARCHIVE_STAGING_MODE = 0o700;
13
14
  export { ArchiveSecurityError } from "./archive-errors.js";
14
15
  function symlinkTraversalError(originalPath) {
15
- return new ArchiveSecurityError("destination-symlink-traversal", `${ERROR_ARCHIVE_ENTRY_TRAVERSES_SYMLINK}: ${originalPath}`);
16
+ return new ArchiveSecurityError("destination-symlink-traversal", `${ERROR_ARCHIVE_ENTRY_TRAVERSES_SYMLINK}: ${formatErrorDetail(originalPath)}`);
16
17
  }
17
18
  async function createDirectoryIdentityGuard(dir) {
18
19
  try {
@@ -306,7 +307,7 @@ export async function mergeExtractedTreeIntoDestination(params) {
306
307
  continue;
307
308
  }
308
309
  if (!sourceStat.isFile()) {
309
- throw new Error(`archive staging contains unsupported entry: ${originalPath}`);
310
+ throw new Error(`archive staging contains unsupported entry: ${formatErrorDetail(originalPath)}`);
310
311
  }
311
312
  await prepareArchiveOutputPath({
312
313
  destinationDir: params.destinationDir,
@@ -1 +1 @@
1
- {"version":3,"file":"archive-tar-meta.d.ts","sourceRoot":"","sources":["../src/archive-tar-meta.ts"],"names":[],"mappings":"AAuIA,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GAAG,OAAO,CAAC,IAAI,CAAC,CAShB"}
1
+ {"version":3,"file":"archive-tar-meta.d.ts","sourceRoot":"","sources":["../src/archive-tar-meta.ts"],"names":[],"mappings":"AAqJA,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GAAG,OAAO,CAAC,IAAI,CAAC,CAShB"}
@@ -19,7 +19,13 @@ class TarMetadataMeter extends Transform {
19
19
  parseSize() {
20
20
  const field = this.block.subarray(124, 136);
21
21
  if ((field[0] ?? 0) & 0x80) {
22
- const value = field.readBigUInt64BE(4);
22
+ if (field[0] !== 0x80) {
23
+ throw this.invalid("base-256 size is negative or malformed");
24
+ }
25
+ let value = 0n;
26
+ for (const byte of field.subarray(1)) {
27
+ value = (value << 8n) | BigInt(byte);
28
+ }
23
29
  if (value > BigInt(Number.MAX_SAFE_INTEGER)) {
24
30
  throw this.invalid("base-256 size exceeds the safe integer range");
25
31
  }
@@ -40,6 +46,14 @@ class TarMetadataMeter extends Transform {
40
46
  this.state = { kind: "header" };
41
47
  return;
42
48
  }
49
+ const nameEnd = this.block.subarray(0, 100).indexOf(0);
50
+ const name = this.block.subarray(0, nameEnd < 0 ? 100 : nameEnd);
51
+ if (name.length === 0) {
52
+ throw this.invalid("entry path is empty");
53
+ }
54
+ if (this.block[156] !== 0x35 && name.at(-1) === 0x2f) {
55
+ throw this.invalid("non-directory entry path ends with a separator");
56
+ }
43
57
  const size = this.parseSize();
44
58
  const type = this.block[156];
45
59
  if ([0x78, 0x67, 0x4c, 0x4b, 0x58].includes(type ?? -1) && size > this.maxMetaEntryBytes) {
@@ -40,4 +40,5 @@ export type TarModule = {
40
40
  }): Promise<unknown>;
41
41
  };
42
42
  export declare function importOptionalTar(): Promise<TarModule>;
43
+ export declare function normalizeTarParserError(error: unknown): unknown;
43
44
  //# sourceMappingURL=archive-tar-runtime.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"archive-tar-runtime.d.ts","sourceRoot":"","sources":["../src/archive-tar-runtime.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,IAAI,IAAI,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,cAAc,GAAG;IAC9C,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAC1B,EAAE,CAAC,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,GAAG,SAAS,CAAC;IAChF,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,GAAG,SAAS,CAAC;IACzE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,SAAS,CAAC;IACnE,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,SAAS,CAAC;IAChE,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,SAAS,CAAC;CACnD,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,MAAM,EAAE,KAAK,OAAO,EAAE;QAAE,MAAM,EAAE,IAAI,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAE,KAAK,SAAS,CAAC;IAC/E,CAAC,CAAC,OAAO,EAAE;QACT,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,aAAa,EAAE,KAAK,CAAC;QACrB,OAAO,EAAE,IAAI,CAAC;QACd,aAAa,EAAE,KAAK,CAAC;QACrB,MAAM,EAAE,IAAI,CAAC;QACb,gBAAgB,EAAE,MAAM,CAAC;QACzB,MAAM,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;QACrE,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;KAClD,GAAG,SAAS,CAAC;IACd,CAAC,CAAC,OAAO,EAAE;QACT,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,IAAI,CAAC;QACb,gBAAgB,EAAE,MAAM,CAAC;QACzB,WAAW,CAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,GAAG;YAAE,MAAM,IAAI,IAAI,CAAA;SAAE,GAAG,IAAI,CAAC;KACvE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACtB,CAAC;AAEF,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,SAAS,CAAC,CAS5D"}
1
+ {"version":3,"file":"archive-tar-runtime.d.ts","sourceRoot":"","sources":["../src/archive-tar-runtime.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,IAAI,IAAI,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,cAAc,GAAG;IAC9C,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAC1B,EAAE,CAAC,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,GAAG,SAAS,CAAC;IAChF,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,GAAG,SAAS,CAAC;IACzE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,SAAS,CAAC;IACnE,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,SAAS,CAAC;IAChE,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,SAAS,CAAC;CACnD,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,MAAM,EAAE,KAAK,OAAO,EAAE;QAAE,MAAM,EAAE,IAAI,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAE,KAAK,SAAS,CAAC;IAC/E,CAAC,CAAC,OAAO,EAAE;QACT,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,aAAa,EAAE,KAAK,CAAC;QACrB,OAAO,EAAE,IAAI,CAAC;QACd,aAAa,EAAE,KAAK,CAAC;QACrB,MAAM,EAAE,IAAI,CAAC;QACb,gBAAgB,EAAE,MAAM,CAAC;QACzB,MAAM,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;QACrE,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;KAClD,GAAG,SAAS,CAAC;IACd,CAAC,CAAC,OAAO,EAAE;QACT,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,IAAI,CAAC;QACb,gBAAgB,EAAE,MAAM,CAAC;QACzB,WAAW,CAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,GAAG;YAAE,MAAM,IAAI,IAAI,CAAA;SAAE,GAAG,IAAI,CAAC;KACvE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACtB,CAAC;AAEF,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,SAAS,CAAC,CAS5D;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAS/D"}
@@ -1,3 +1,4 @@
1
+ import { ArchiveFormatError } from "./archive-errors.js";
1
2
  export async function importOptionalTar() {
2
3
  try {
3
4
  return await import("tar");
@@ -6,3 +7,13 @@ export async function importOptionalTar() {
6
7
  throw new Error('Optional archive dependency "tar" is not installed. Install it to use TAR archive helpers from @openclaw/fs-safe/archive.', { cause });
7
8
  }
8
9
  }
10
+ export function normalizeTarParserError(error) {
11
+ const code = error?.code;
12
+ if (typeof code !== "string" || !code.startsWith("TAR_")) {
13
+ return error;
14
+ }
15
+ const message = error instanceof Error ? error.message : String(error);
16
+ return new ArchiveFormatError(`invalid TAR archive: ${message}`, {
17
+ cause: error instanceof Error ? error : undefined,
18
+ });
19
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"archive-tar.d.ts","sourceRoot":"","sources":["../src/archive-tar.ts"],"names":[],"mappings":"AAKA,OAAO,EAKL,KAAK,oBAAoB,EAC1B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAGL,KAAK,kBAAkB,EACvB,KAAK,0BAA0B,EAChC,MAAM,qBAAqB,CAAC;AAG7B,MAAM,MAAM,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAWvF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,CAyB7D;AAED,wBAAgB,8BAA8B,CAAC,MAAM,EAAE;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,UAAU,CAAC,EAAE,0BAA0B,CAAC;CACzC,GAAG,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CA2CnC"}
1
+ {"version":3,"file":"archive-tar.d.ts","sourceRoot":"","sources":["../src/archive-tar.ts"],"names":[],"mappings":"AAMA,OAAO,EAKL,KAAK,oBAAoB,EAC1B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAGL,KAAK,kBAAkB,EACvB,KAAK,0BAA0B,EAChC,MAAM,qBAAqB,CAAC;AAI7B,MAAM,MAAM,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAWvF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,CAyB7D;AAED,wBAAgB,8BAA8B,CAAC,MAAM,EAAE;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,UAAU,CAAC,EAAE,0BAA0B,CAAC;CACzC,GAAG,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAgDnC"}
@@ -1,7 +1,8 @@
1
- import { resolveArchiveOutputPath, stripArchivePath, validateArchiveEntryPath, } from "./archive-entry.js";
1
+ import { createArchiveOutputPathTracker, resolveArchiveOutputPath, stripArchivePath, validateArchiveEntryPath, } from "./archive-entry.js";
2
2
  import { assertArchiveEntryCountWithinLimit, assertArchiveEntryPathComponentsWithinLimit, createByteBudgetTracker, resolveExtractLimits, } from "./archive-limits.js";
3
3
  import { archiveEntryKindFromTarType, shouldExtractArchiveEntry, } from "./archive-policy.js";
4
4
  import { ArchiveSecurityError } from "./archive-errors.js";
5
+ import { formatErrorDetail } from "./error-detail.js";
5
6
  const BLOCKED_TAR_ENTRY_TYPES = new Set([
6
7
  "SymbolicLink",
7
8
  "Link",
@@ -37,7 +38,10 @@ export function createTarEntryPreflightChecker(params) {
37
38
  const limits = resolveExtractLimits(params.limits);
38
39
  let entryCount = 0;
39
40
  const budget = createByteBudgetTracker(limits);
41
+ const trackOutputPath = createArchiveOutputPathTracker();
40
42
  return (entry) => {
43
+ entryCount += 1;
44
+ assertArchiveEntryCountWithinLimit(entryCount, limits);
41
45
  validateArchiveEntryPath(entry.path, { escapeLabel: params.escapeLabel });
42
46
  const relPath = stripArchivePath(entry.path, strip);
43
47
  if (!relPath) {
@@ -45,14 +49,13 @@ export function createTarEntryPreflightChecker(params) {
45
49
  }
46
50
  validateArchiveEntryPath(relPath, { escapeLabel: params.escapeLabel });
47
51
  assertArchiveEntryPathComponentsWithinLimit(relPath, limits);
52
+ trackOutputPath(relPath, entry.path);
48
53
  resolveArchiveOutputPath({
49
54
  rootDir: params.rootDir,
50
55
  relPath,
51
56
  originalPath: entry.path,
52
57
  escapeLabel: params.escapeLabel,
53
58
  });
54
- entryCount += 1;
55
- assertArchiveEntryCountWithinLimit(entryCount, limits);
56
59
  const kind = archiveEntryKindFromTarType(entry.type);
57
60
  if (!shouldExtractArchiveEntry({
58
61
  filter: params.entryFilter,
@@ -62,8 +65,9 @@ export function createTarEntryPreflightChecker(params) {
62
65
  return false;
63
66
  }
64
67
  if (BLOCKED_TAR_ENTRY_TYPES.has(entry.type)) {
65
- throw new ArchiveSecurityError("entry-link", `tar entry is a link: ${entry.path}`);
68
+ throw new ArchiveSecurityError("entry-link", `tar entry is a link: ${formatErrorDetail(entry.path)}`);
66
69
  }
70
+ budget.startEntry();
67
71
  budget.addEntrySize(entry.size);
68
72
  return true;
69
73
  };
@@ -4,11 +4,17 @@ export type ZipEntry = {
4
4
  dir: boolean;
5
5
  unixPermissions?: number;
6
6
  _data?: {
7
+ crc32?: number;
7
8
  uncompressedSize?: number;
8
- };
9
+ } | PromiseLike<unknown>;
9
10
  nodeStream?: () => NodeJS.ReadableStream;
10
11
  async: (type: "nodebuffer") => Promise<Buffer>;
11
12
  };
13
+ export declare function zipEntryIntegrityMetadata(entry: ZipEntry): {
14
+ crc32?: number;
15
+ uncompressedSize?: number;
16
+ } | undefined;
17
+ export declare function hasDeferredEmptyZipData(entry: ZipEntry): boolean;
12
18
  export declare function isZipSymlinkEntry(entry: ZipEntry): boolean;
13
19
  export declare function zipEntryMode(entry: ZipEntry, policy: ArchiveEntryModePolicy | undefined): number;
14
20
  export declare function zipEntryDeclaredSize(entry: ZipEntry): number;
@@ -1 +1 @@
1
- {"version":3,"file":"archive-zip-entry.d.ts","sourceRoot":"","sources":["../src/archive-zip-entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,sBAAsB,EAC5B,MAAM,qBAAqB,CAAC;AAE7B,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,OAAO,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE;QAAE,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,UAAU,CAAC,EAAE,MAAM,MAAM,CAAC,cAAc,CAAC;IACzC,KAAK,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAChD,CAAC;AAKF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAK1D;AAED,wBAAgB,YAAY,CAC1B,KAAK,EAAE,QAAQ,EACf,MAAM,EAAE,sBAAsB,GAAG,SAAS,GACzC,MAAM,CAMR;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CAE5D"}
1
+ {"version":3,"file":"archive-zip-entry.d.ts","sourceRoot":"","sources":["../src/archive-zip-entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,sBAAsB,EAC5B,MAAM,qBAAqB,CAAC;AAE7B,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,OAAO,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IAC7E,UAAU,CAAC,EAAE,MAAM,MAAM,CAAC,cAAc,CAAC;IACzC,KAAK,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAChD,CAAC;AAEF,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,QAAQ,GACd;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAI3D;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAGhE;AAKD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAK1D;AAED,wBAAgB,YAAY,CAC1B,KAAK,EAAE,QAAQ,EACf,MAAM,EAAE,sBAAsB,GAAG,SAAS,GACzC,MAAM,CAMR;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CAE5D"}
@@ -1,4 +1,14 @@
1
1
  import { resolveArchiveEntryMode, } from "./archive-policy.js";
2
+ export function zipEntryIntegrityMetadata(entry) {
3
+ const data = entry._data;
4
+ if (!data || "then" in data)
5
+ return undefined;
6
+ return data;
7
+ }
8
+ export function hasDeferredEmptyZipData(entry) {
9
+ const data = entry._data;
10
+ return Boolean(data && "then" in data);
11
+ }
2
12
  const ZIP_UNIX_FILE_TYPE_MASK = 0o170000;
3
13
  const ZIP_UNIX_SYMLINK_TYPE = 0o120000;
4
14
  export function isZipSymlinkEntry(entry) {
@@ -13,5 +23,5 @@ export function zipEntryMode(entry, policy) {
13
23
  });
14
24
  }
15
25
  export function zipEntryDeclaredSize(entry) {
16
- return Math.max(0, Math.floor(entry._data?.uncompressedSize ?? 0));
26
+ return Math.max(0, Math.floor(zipEntryIntegrityMetadata(entry)?.uncompressedSize ?? 0));
17
27
  }
@@ -0,0 +1,5 @@
1
+ import { Transform } from "node:stream";
2
+ import { type ZipEntry } from "./archive-zip-entry.js";
3
+ export declare function normalizeZipIntegrityError(error: unknown): Error;
4
+ export declare function createZipIntegrityTransform(entry: ZipEntry): Transform;
5
+ //# sourceMappingURL=archive-zip-integrity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archive-zip-integrity.d.ts","sourceRoot":"","sources":["../src/archive-zip-integrity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,OAAO,EAGL,KAAK,QAAQ,EACd,MAAM,wBAAwB,CAAC;AAkBhC,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAQhE;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,CAgCtE"}
@@ -0,0 +1,54 @@
1
+ import { Transform } from "node:stream";
2
+ import { ArchiveFormatError } from "./archive-errors.js";
3
+ import { hasDeferredEmptyZipData, zipEntryIntegrityMetadata, } from "./archive-zip-entry.js";
4
+ const CRC32_TABLE = Array.from({ length: 256 }, (_, index) => {
5
+ let value = index;
6
+ for (let bit = 0; bit < 8; bit += 1) {
7
+ value = (value & 1) !== 0 ? 0xedb88320 ^ (value >>> 1) : value >>> 1;
8
+ }
9
+ return value >>> 0;
10
+ });
11
+ function updateCrc32(previous, buffer) {
12
+ let crc = previous ^ -1;
13
+ for (const byte of buffer) {
14
+ crc = (crc >>> 8) ^ (CRC32_TABLE[(crc ^ byte) & 0xff] ?? 0);
15
+ }
16
+ return (crc ^ -1) >>> 0;
17
+ }
18
+ export function normalizeZipIntegrityError(error) {
19
+ if (error instanceof Error &&
20
+ error.message.includes("uncompressed data size mismatch")) {
21
+ return new ArchiveFormatError(`invalid ZIP entry data: ${error.message}`, { cause: error });
22
+ }
23
+ return error instanceof Error ? error : new Error(String(error));
24
+ }
25
+ export function createZipIntegrityTransform(entry) {
26
+ const metadata = zipEntryIntegrityMetadata(entry);
27
+ const deferredEmpty = hasDeferredEmptyZipData(entry);
28
+ const expectedCrc32 = deferredEmpty ? 0 : metadata?.crc32;
29
+ const expectedSize = deferredEmpty ? 0 : metadata?.uncompressedSize;
30
+ if (typeof expectedCrc32 !== "number" ||
31
+ !Number.isInteger(expectedCrc32) ||
32
+ typeof expectedSize !== "number" ||
33
+ !Number.isSafeInteger(expectedSize) ||
34
+ expectedSize < 0) {
35
+ throw new ArchiveFormatError(`zip entry has invalid integrity metadata: ${entry.name}`);
36
+ }
37
+ let actualCrc32 = 0;
38
+ let actualSize = 0;
39
+ return new Transform({
40
+ transform(chunk, _encoding, callback) {
41
+ const buffer = chunk instanceof Buffer ? chunk : Buffer.from(chunk);
42
+ actualCrc32 = updateCrc32(actualCrc32, buffer);
43
+ actualSize += buffer.byteLength;
44
+ callback(null, buffer);
45
+ },
46
+ flush(callback) {
47
+ if (actualSize !== expectedSize || actualCrc32 !== (expectedCrc32 >>> 0)) {
48
+ callback(new ArchiveFormatError(`zip entry integrity check failed: ${entry.name}`));
49
+ return;
50
+ }
51
+ callback();
52
+ },
53
+ });
54
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"archive-zip-preflight.d.ts","sourceRoot":"","sources":["../src/archive-zip-preflight.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,oBAAoB,EAC1B,MAAM,qBAAqB,CAAC;AAE7B,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,CAAC;AAiLF,wBAAgB,iCAAiC,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,IAAI,CAU5F;AAED,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,MAAM,GAAG,UAAU,EAC3B,MAAM,CAAC,EAAE,oBAAoB,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CAW9B"}
1
+ {"version":3,"file":"archive-zip-preflight.d.ts","sourceRoot":"","sources":["../src/archive-zip-preflight.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,oBAAoB,EAC1B,MAAM,qBAAqB,CAAC;AAG7B,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,CAAC;AAiLF,wBAAgB,iCAAiC,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,IAAI,CAU5F;AAED,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,MAAM,GAAG,UAAU,EAC3B,MAAM,CAAC,EAAE,oBAAoB,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CA0B9B"}
@@ -1,4 +1,5 @@
1
1
  import { ARCHIVE_LIMIT_ERROR_CODE, ArchiveLimitError, assertArchiveEntryCountWithinLimit, resolveExtractLimits, } from "./archive-limits.js";
2
+ import { ArchiveFormatError, ArchiveSecurityError } from "./archive-errors.js";
2
3
  const ZIP_EOCD_SIGNATURE = 0x06054b50;
3
4
  const ZIP64_EOCD_SIGNATURE = 0x06064b50;
4
5
  const ZIP64_EOCD_LOCATOR_SIGNATURE = 0x07064b50;
@@ -148,7 +149,17 @@ export async function loadZipArchiveWithPreflight(buffer, limits) {
148
149
  assertArchiveEntryCountWithinLimit(entryCount, resolvedLimits);
149
150
  }
150
151
  const JSZip = await importOptionalJsZip();
151
- return await JSZip.loadAsync(buffer);
152
+ let archive;
153
+ try {
154
+ archive = await JSZip.loadAsync(buffer);
155
+ }
156
+ catch (error) {
157
+ throw new ArchiveFormatError(`invalid ZIP archive: ${error instanceof Error ? error.message : String(error)}`, { cause: error instanceof Error ? error : undefined });
158
+ }
159
+ if (entryCount !== null && Object.keys(archive.files).length !== entryCount) {
160
+ throw new ArchiveSecurityError("entry-path", "zip archive contains duplicate or colliding entry names");
161
+ }
162
+ return archive;
152
163
  }
153
164
  async function importOptionalJsZip() {
154
165
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../src/archive.ts"],"names":[],"mappings":"AAuDA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAElE,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AACjF,YAAY,EACV,kBAAkB,EAClB,gBAAgB,EAChB,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,yBAAyB,EACzB,wBAAwB,EACxB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC/F,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EACL,wBAAwB,EACxB,iBAAiB,EACjB,6BAA6B,EAC7B,mBAAmB,EACnB,2BAA2B,EAC3B,uBAAuB,EACvB,4BAA4B,EAC5B,iCAAiC,EACjC,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,KAAK,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AACtF,OAAO,EAAE,oBAAoB,EAAE,KAAK,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAC3F,OAAO,EACL,kCAAkC,EAClC,iCAAiC,EACjC,4BAA4B,EAC5B,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,8BAA8B,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrF,OAAO,EACL,2BAA2B,EAC3B,iCAAiC,EACjC,KAAK,mBAAmB,GACzB,MAAM,4BAA4B,CAAC;AAiNpC,wBAAsB,cAAc,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiKjF"}
1
+ {"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../src/archive.ts"],"names":[],"mappings":"AA6DA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAElE,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AACjF,YAAY,EACV,kBAAkB,EAClB,gBAAgB,EAChB,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,yBAAyB,EACzB,wBAAwB,EACxB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC/F,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EACL,wBAAwB,EACxB,iBAAiB,EACjB,6BAA6B,EAC7B,mBAAmB,EACnB,2BAA2B,EAC3B,uBAAuB,EACvB,4BAA4B,EAC5B,iCAAiC,EACjC,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,KAAK,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AACtF,OAAO,EAAE,oBAAoB,EAAE,KAAK,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAC3F,OAAO,EACL,kCAAkC,EAClC,iCAAiC,EACjC,4BAA4B,EAC5B,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,8BAA8B,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrF,OAAO,EACL,2BAA2B,EAC3B,iCAAiC,EACjC,KAAK,mBAAmB,GACzB,MAAM,4BAA4B,CAAC;AAoNpC,wBAAsB,cAAc,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkKjF"}
package/dist/archive.js CHANGED
@@ -3,7 +3,7 @@ import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { Readable } from "node:stream";
5
5
  import { pipeline } from "node:stream/promises";
6
- import { resolveArchiveOutputPath, stripArchivePath, validateArchiveEntryPath, } from "./archive-entry.js";
6
+ import { createArchiveOutputPathTracker, normalizeArchiveEntryPath, resolveArchiveOutputPath, stripArchivePath, validateArchiveEntryPath, } from "./archive-entry.js";
7
7
  import { createPipelineTimeoutError, waitForDeadline, withExtractionDeadline, } from "./archive-deadline.js";
8
8
  import { assertArchiveEntryCountWithinLimit, assertArchiveEntryPathComponentsWithinLimit, createByteBudgetTracker, createExtractBudgetTransform, resolveExtractLimits, } from "./archive-limits.js";
9
9
  import { resolveArchiveKind } from "./archive-kind.js";
@@ -11,13 +11,14 @@ import { mergeExtractedTreeIntoDestination, prepareArchiveDestinationDir, prepar
11
11
  import { createTarEntryPreflightChecker, readTarEntryInfo, } from "./archive-tar.js";
12
12
  import { loadZipArchiveWithPreflight } from "./archive-zip-preflight.js";
13
13
  import { isZipSymlinkEntry, zipEntryDeclaredSize, zipEntryMode, } from "./archive-zip-entry.js";
14
+ import { createZipIntegrityTransform, normalizeZipIntegrityError, } from "./archive-zip-integrity.js";
14
15
  import { FsSafeError } from "./errors.js";
15
16
  import { ArchiveSecurityError } from "./archive-errors.js";
16
17
  import { extractNativeArchive } from "./archive-native.js";
17
18
  import { stageArchiveFileForExtraction } from "./archive-input.js";
18
19
  import { getNativeBinding } from "./native.js";
19
20
  import { resolveArchiveEntryMode, shouldExtractArchiveEntry, } from "./archive-policy.js";
20
- import { importOptionalTar } from "./archive-tar-runtime.js";
21
+ import { importOptionalTar, normalizeTarParserError } from "./archive-tar-runtime.js";
21
22
  import { preflightTarMetadata } from "./archive-tar-meta.js";
22
23
  import { writeSiblingTempFile } from "./sibling-temp.js";
23
24
  export { isWindowsDrivePath, normalizeArchiveEntryPath, resolveArchiveOutputPath, stripArchivePath, validateArchiveEntryPath, } from "./archive-entry.js";
@@ -81,10 +82,10 @@ async function writeZipFileEntry(params) {
81
82
  handleClosedByStream = true;
82
83
  });
83
84
  try {
84
- await pipeline(readable, createExtractBudgetTransform({ onChunkBytes: params.budget.addBytes }), writable, { signal: params.deadline.signal });
85
+ await pipeline(readable, createExtractBudgetTransform({ onChunkBytes: params.budget.addBytes }), createZipIntegrityTransform(params.entry), writable, { signal: params.deadline.signal });
85
86
  }
86
87
  catch (err) {
87
- throw createPipelineTimeoutError(err, params.deadline);
88
+ throw normalizeZipIntegrityError(createPipelineTimeoutError(err, params.deadline));
88
89
  }
89
90
  params.deadline.check();
90
91
  if (!handleClosedByStream) {
@@ -127,6 +128,7 @@ async function extractZip(params) {
127
128
  const strip = Math.max(0, Math.floor(params.stripComponents ?? 0));
128
129
  assertArchiveEntryCountWithinLimit(entries.length, limits);
129
130
  const budget = createByteBudgetTracker(limits);
131
+ const trackOutputPath = createArchiveOutputPathTracker();
130
132
  await withStagedArchiveDestination({
131
133
  destinationRealDir,
132
134
  run: async (stagingDir) => {
@@ -142,6 +144,7 @@ async function extractZip(params) {
142
144
  continue;
143
145
  }
144
146
  assertArchiveEntryPathComponentsWithinLimit(output.relPath, limits);
147
+ trackOutputPath(output.relPath, entry.name);
145
148
  const isSymlink = isZipSymlinkEntry(entry);
146
149
  const entryKind = isSymlink ? "symlink" : entry.dir ? "directory" : "file";
147
150
  const entrySize = zipEntryDeclaredSize(entry);
@@ -263,6 +266,7 @@ export async function extractArchive(params) {
263
266
  const info = readTarEntryInfo(entry);
264
267
  const accepted = checkTarEntrySafety(info);
265
268
  if (accepted) {
269
+ entry.path = normalizeArchiveEntryPath(info.path);
266
270
  const relPath = stripArchivePath(info.path, Math.max(0, Math.floor(params.stripComponents ?? 0)));
267
271
  if (relPath) {
268
272
  acceptedEntries.push({
@@ -306,7 +310,7 @@ export async function extractArchive(params) {
306
310
  });
307
311
  }
308
312
  catch (error) {
309
- throw createPipelineTimeoutError(error, deadline);
313
+ throw normalizeTarParserError(createPipelineTimeoutError(error, deadline));
310
314
  }
311
315
  for (const accepted of acceptedEntries) {
312
316
  const outputPath = resolveArchiveOutputPath({
@@ -1 +1 @@
1
- {"version":3,"file":"bounded-read-stream.d.ts","sourceRoot":"","sources":["../src/bounded-read-stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAkBnE;AAED,wBAAgB,uBAAuB,CACrC,MAAM,EAAE;IAAE,MAAM,EAAE;QAAE,gBAAgB,IAAI,MAAM,CAAC,cAAc,CAAA;KAAE,CAAA;CAAE,EACjE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,MAAM,CAAC,cAAc,CAGvB"}
1
+ {"version":3,"file":"bounded-read-stream.d.ts","sourceRoot":"","sources":["../src/bounded-read-stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,SAAS,EAAE,MAAM,aAAa,CAAC;AAGlD,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAwBnE;AAED,wBAAgB,uBAAuB,CACrC,MAAM,EAAE;IAAE,MAAM,EAAE;QAAE,gBAAgB,IAAI,MAAM,CAAC,cAAc,CAAA;KAAE,CAAA;CAAE,EACjE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,MAAM,CAAC,cAAc,CAWvB"}
@@ -1,6 +1,10 @@
1
- import { Transform } from "node:stream";
1
+ import { pipeline, Transform } from "node:stream";
2
2
  import { FsSafeError } from "./errors.js";
3
3
  export function createMaxBytesTransform(maxBytes) {
4
+ if (maxBytes !== Number.POSITIVE_INFINITY &&
5
+ (!Number.isSafeInteger(maxBytes) || maxBytes < 0)) {
6
+ throw new RangeError("maxBytes must be a non-negative safe integer or Infinity");
7
+ }
4
8
  let bytes = 0;
5
9
  return new Transform({
6
10
  transform(chunk, _encoding, callback) {
@@ -16,5 +20,12 @@ export function createMaxBytesTransform(maxBytes) {
16
20
  }
17
21
  export function createBoundedReadStream(opened, maxBytes) {
18
22
  const stream = opened.handle.createReadStream();
19
- return maxBytes === undefined ? stream : stream.pipe(createMaxBytesTransform(maxBytes));
23
+ if (maxBytes === undefined) {
24
+ return stream;
25
+ }
26
+ const bounded = createMaxBytesTransform(maxBytes);
27
+ // pipeline couples teardown in both directions: source failures reach the
28
+ // returned stream, and an early consumer close destroys the file stream.
29
+ pipeline(stream, bounded, () => undefined);
30
+ return bounded;
20
31
  }
@@ -1,3 +1,4 @@
1
+ export declare function resolveMutationComparablePaths(rawPath: string): Promise<Set<string>>;
1
2
  export type DenyMutationPolicy = {
2
3
  paths?: readonly string[];
3
4
  prefixes?: readonly string[];
@@ -1 +1 @@
1
- {"version":3,"file":"deny-mutations.d.ts","sourceRoot":"","sources":["../src/deny-mutations.ts"],"names":[],"mappings":"AAmDA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9B,CAAC;AAEF,KAAK,wBAAwB,GAAG;IAC9B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAqBF,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,kBAAkB,GAAG,SAAS,EACtC,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,IAAI,CAAC,CAiCf;AAED,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,kBAAkB,GAAG,SAAS,EAC7C,UAAU,EAAE,kBAAkB,GAAG,SAAS,GACzC,kBAAkB,GAAG,SAAS,CAWhC"}
1
+ {"version":3,"file":"deny-mutations.d.ts","sourceRoot":"","sources":["../src/deny-mutations.ts"],"names":[],"mappings":"AAKA,wBAAsB,8BAA8B,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAI1F;AAMD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9B,CAAC;AAEF,KAAK,wBAAwB,GAAG;IAC9B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAqBF,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,kBAAkB,GAAG,SAAS,EACtC,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,IAAI,CAAC,CAiCf;AAED,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,kBAAkB,GAAG,SAAS,EAC7C,UAAU,EAAE,kBAAkB,GAAG,SAAS,GACzC,kBAAkB,GAAG,SAAS,CAWhC"}