@openclaw/fs-safe 0.2.6 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +21 -7
  3. package/dist/archive-deadline.d.ts +9 -0
  4. package/dist/archive-deadline.d.ts.map +1 -0
  5. package/dist/archive-deadline.js +67 -0
  6. package/dist/archive-file-io.d.ts +9 -0
  7. package/dist/archive-file-io.d.ts.map +1 -0
  8. package/dist/archive-file-io.js +11 -0
  9. package/dist/archive-staging.d.ts.map +1 -1
  10. package/dist/archive-staging.js +43 -3
  11. package/dist/archive.d.ts.map +1 -1
  12. package/dist/archive.js +213 -105
  13. package/dist/deny-mutations.d.ts +11 -0
  14. package/dist/deny-mutations.d.ts.map +1 -0
  15. package/dist/deny-mutations.js +102 -0
  16. package/dist/errors.d.ts +1 -1
  17. package/dist/errors.d.ts.map +1 -1
  18. package/dist/index.d.ts +1 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/json-durable-queue.d.ts.map +1 -1
  21. package/dist/json-durable-queue.js +5 -0
  22. package/dist/json.d.ts.map +1 -1
  23. package/dist/json.js +51 -4
  24. package/dist/opened-realpath.d.ts +3 -0
  25. package/dist/opened-realpath.d.ts.map +1 -0
  26. package/dist/opened-realpath.js +79 -0
  27. package/dist/output.d.ts.map +1 -1
  28. package/dist/output.js +1 -0
  29. package/dist/pinned-write.js +3 -9
  30. package/dist/read-opened-file.d.ts +18 -0
  31. package/dist/read-opened-file.d.ts.map +1 -0
  32. package/dist/read-opened-file.js +15 -0
  33. package/dist/regular-file.d.ts.map +1 -1
  34. package/dist/regular-file.js +23 -3
  35. package/dist/root-impl.d.ts +18 -15
  36. package/dist/root-impl.d.ts.map +1 -1
  37. package/dist/root-impl.js +60 -100
  38. package/dist/root-paths.d.ts.map +1 -1
  39. package/dist/root-paths.js +16 -5
  40. package/dist/root.d.ts +1 -1
  41. package/dist/root.d.ts.map +1 -1
  42. package/dist/secret-file.d.ts +1 -0
  43. package/dist/secret-file.d.ts.map +1 -1
  44. package/dist/secret-file.js +23 -3
  45. package/dist/sidecar-lock.d.ts.map +1 -1
  46. package/dist/sidecar-lock.js +9 -3
  47. package/dist/symlink-parents.js +2 -2
  48. package/docs/errors.md +2 -0
  49. package/docs/python-helper.md +3 -3
  50. package/docs/root.md +19 -9
  51. package/docs/secret-file.md +3 -2
  52. package/docs/security-model.md +4 -0
  53. package/docs/timing.md +1 -1
  54. package/docs/types.md +21 -9
  55. package/docs/writing.md +19 -1
  56. package/package.json +1 -1
package/dist/archive.js CHANGED
@@ -4,13 +4,16 @@ import path from "node:path";
4
4
  import { Readable } from "node:stream";
5
5
  import { pipeline } from "node:stream/promises";
6
6
  import { resolveArchiveOutputPath, stripArchivePath, validateArchiveEntryPath, } from "./archive-entry.js";
7
+ import { createPipelineTimeoutError, waitForDeadline, withExtractionDeadline, } from "./archive-deadline.js";
8
+ import { writeFileHandleFully } from "./archive-file-io.js";
7
9
  import { ARCHIVE_LIMIT_ERROR_CODE, ArchiveLimitError, assertArchiveEntryCountWithinLimit, createByteBudgetTracker, createExtractBudgetTransform, resolveExtractLimits, } from "./archive-limits.js";
8
10
  import { resolveArchiveKind } from "./archive-kind.js";
9
11
  import { mergeExtractedTreeIntoDestination, prepareArchiveDestinationDir, prepareArchiveOutputPath, withStagedArchiveDestination, } from "./archive-staging.js";
10
12
  import { createTarEntryPreflightChecker, readTarEntryInfo, } from "./archive-tar.js";
11
13
  import { loadZipArchiveWithPreflight } from "./archive-zip-preflight.js";
14
+ import { sameFileIdentity } from "./file-identity.js";
12
15
  import { writeSiblingTempFile } from "./sibling-temp.js";
13
- import { withTimeout } from "./timing.js";
16
+ import { tempFile } from "./temp-target.js";
14
17
  export { isWindowsDrivePath, normalizeArchiveEntryPath, resolveArchiveOutputPath, stripArchivePath, validateArchiveEntryPath, } from "./archive-entry.js";
15
18
  export { resolveArchiveKind, resolvePackedRootDir } from "./archive-kind.js";
16
19
  export { ARCHIVE_LIMIT_ERROR_CODE, ArchiveLimitError, DEFAULT_MAX_ARCHIVE_BYTES_ZIP, DEFAULT_MAX_ENTRIES, DEFAULT_MAX_EXTRACTED_BYTES, DEFAULT_MAX_ENTRY_BYTES, } from "./archive-limits.js";
@@ -29,6 +32,85 @@ function isZipSymlinkEntry(entry) {
29
32
  return (typeof entry.unixPermissions === "number" &&
30
33
  (entry.unixPermissions & ZIP_UNIX_FILE_TYPE_MASK) === ZIP_UNIX_SYMLINK_TYPE);
31
34
  }
35
+ function zipEntryFileMode(entry) {
36
+ if (typeof entry.unixPermissions !== "number") {
37
+ return undefined;
38
+ }
39
+ const mode = entry.unixPermissions & 0o777;
40
+ return mode === 0 ? undefined : mode;
41
+ }
42
+ async function cleanupStagedArchiveFile(staged) {
43
+ if (staged) {
44
+ await staged.cleanup().catch(() => undefined);
45
+ }
46
+ }
47
+ async function closeFileHandle(handle) {
48
+ if (handle) {
49
+ await handle.close().catch(() => undefined);
50
+ }
51
+ }
52
+ async function stageArchiveFileForExtraction(params) {
53
+ params.deadline.check();
54
+ const sourcePath = path.resolve(params.archivePath);
55
+ const initialStat = await fs.lstat(sourcePath);
56
+ if (initialStat.isSymbolicLink() || !initialStat.isFile()) {
57
+ throw new Error(`archive is not a regular file: ${params.archivePath}`);
58
+ }
59
+ if (initialStat.size > params.limits.maxArchiveBytes) {
60
+ throw new ArchiveLimitError(ARCHIVE_LIMIT_ERROR_CODE.ARCHIVE_SIZE_EXCEEDS_LIMIT);
61
+ }
62
+ const noFollow = process.platform !== "win32" && "O_NOFOLLOW" in fsConstants ? fsConstants.O_NOFOLLOW : 0;
63
+ const handle = await fs.open(sourcePath, fsConstants.O_RDONLY | noFollow);
64
+ let staged;
65
+ let output;
66
+ try {
67
+ staged = await tempFile({
68
+ prefix: "fs-safe-archive-input",
69
+ fileName: path.basename(sourcePath),
70
+ });
71
+ const openedStat = await handle.stat();
72
+ const pathStat = await fs.lstat(sourcePath);
73
+ if (!openedStat.isFile() ||
74
+ pathStat.isSymbolicLink() ||
75
+ !pathStat.isFile() ||
76
+ !sameFileIdentity(initialStat, openedStat) ||
77
+ !sameFileIdentity(pathStat, openedStat)) {
78
+ throw new Error("archive changed during validation");
79
+ }
80
+ output = await fs.open(staged.path, OPEN_WRITE_CREATE_FLAGS, 0o600);
81
+ const buffer = Buffer.allocUnsafe(64 * 1024);
82
+ let written = 0;
83
+ while (true) {
84
+ params.deadline.check();
85
+ const { bytesRead } = await handle.read(buffer, 0, buffer.length, null);
86
+ if (bytesRead === 0) {
87
+ break;
88
+ }
89
+ written += bytesRead;
90
+ if (written > params.limits.maxArchiveBytes) {
91
+ throw new ArchiveLimitError(ARCHIVE_LIMIT_ERROR_CODE.ARCHIVE_SIZE_EXCEEDS_LIMIT);
92
+ }
93
+ await writeFileHandleFully({
94
+ handle: output,
95
+ buffer,
96
+ bytes: bytesRead,
97
+ deadline: params.deadline,
98
+ });
99
+ params.deadline.check();
100
+ }
101
+ await output.close();
102
+ output = undefined;
103
+ return staged;
104
+ }
105
+ catch (err) {
106
+ await closeFileHandle(output);
107
+ await cleanupStagedArchiveFile(staged);
108
+ throw err;
109
+ }
110
+ finally {
111
+ await closeFileHandle(handle);
112
+ }
113
+ }
32
114
  async function readZipEntryStream(entry) {
33
115
  if (typeof entry.nodeStream === "function") {
34
116
  return entry.nodeStream();
@@ -57,6 +139,7 @@ async function prepareZipOutputPath(params) {
57
139
  await prepareArchiveOutputPath(params);
58
140
  }
59
141
  async function writeZipFileEntry(params) {
142
+ params.deadline.check();
60
143
  params.budget.startEntry();
61
144
  const readable = await readZipEntryStream(params.entry);
62
145
  const destinationPath = params.outPath;
@@ -67,13 +150,20 @@ async function writeZipFileEntry(params) {
67
150
  dir: path.dirname(destinationPath),
68
151
  tempPrefix: `.${path.basename(destinationPath)}.fs-safe-archive`,
69
152
  chmodDir: false,
153
+ mode: zipEntryFileMode(params.entry),
70
154
  writeTemp: async (tempPath) => {
71
155
  tempHandle = await fs.open(tempPath, OPEN_WRITE_CREATE_FLAGS, 0o666);
72
156
  const writable = tempHandle.createWriteStream();
73
157
  writable.once("close", () => {
74
158
  handleClosedByStream = true;
75
159
  });
76
- await pipeline(readable, createExtractBudgetTransform({ onChunkBytes: params.budget.addBytes }), writable);
160
+ try {
161
+ await pipeline(readable, createExtractBudgetTransform({ onChunkBytes: params.budget.addBytes }), writable, { signal: params.deadline.signal });
162
+ }
163
+ catch (err) {
164
+ throw createPipelineTimeoutError(err, params.deadline);
165
+ }
166
+ params.deadline.check();
77
167
  if (!handleClosedByStream) {
78
168
  await tempHandle.close().catch(() => undefined);
79
169
  handleClosedByStream = true;
@@ -83,13 +173,6 @@ async function writeZipFileEntry(params) {
83
173
  },
84
174
  resolveFinalPath: (filePath) => filePath,
85
175
  });
86
- // Best-effort permission restore for zip entries created on unix.
87
- if (typeof params.entry.unixPermissions === "number") {
88
- const mode = params.entry.unixPermissions & 0o777;
89
- if (mode !== 0) {
90
- await fs.chmod(destinationPath, mode).catch(() => undefined);
91
- }
92
- }
93
176
  }
94
177
  catch (err) {
95
178
  // Failures here happen before the temp has been committed. The destination
@@ -105,57 +188,70 @@ async function writeZipFileEntry(params) {
105
188
  }
106
189
  async function extractZip(params) {
107
190
  const limits = resolveExtractLimits(params.limits);
108
- const destinationRealDir = await prepareArchiveDestinationDir(params.destDir);
109
- const stat = await fs.stat(params.archivePath);
110
- if (stat.size > limits.maxArchiveBytes) {
111
- throw new ArchiveLimitError(ARCHIVE_LIMIT_ERROR_CODE.ARCHIVE_SIZE_EXCEEDS_LIMIT);
112
- }
113
- const buffer = await fs.readFile(params.archivePath);
114
- const zip = await loadZipArchiveWithPreflight(buffer, limits);
115
- const entries = Object.values(zip.files);
116
- const strip = Math.max(0, Math.floor(params.stripComponents ?? 0));
117
- assertArchiveEntryCountWithinLimit(entries.length, limits);
118
- const budget = createByteBudgetTracker(limits);
119
- await withStagedArchiveDestination({
120
- destinationRealDir,
121
- run: async (stagingDir) => {
122
- const stagingRealDir = await fs.realpath(stagingDir);
123
- for (const entry of entries) {
124
- const output = resolveZipOutputPath({
125
- entryPath: entry.name,
126
- strip,
127
- destinationDir: stagingRealDir,
128
- });
129
- if (!output) {
130
- continue;
131
- }
132
- await prepareZipOutputPath({
133
- destinationDir: stagingRealDir,
134
- destinationRealDir: stagingRealDir,
135
- relPath: output.relPath,
136
- outPath: output.outPath,
137
- originalPath: entry.name,
138
- isDirectory: entry.dir,
139
- });
140
- if (entry.dir) {
141
- continue;
142
- }
143
- if (isZipSymlinkEntry(entry)) {
144
- throw new Error(`zip entry is a link: ${entry.name}`);
191
+ const stagedArchive = await stageArchiveFileForExtraction({
192
+ archivePath: params.archivePath,
193
+ limits,
194
+ deadline: params.deadline,
195
+ });
196
+ try {
197
+ const destinationRealDir = await prepareArchiveDestinationDir(params.destDir);
198
+ params.deadline.check();
199
+ const buffer = await fs.readFile(stagedArchive.path, { signal: params.deadline.signal });
200
+ params.deadline.check();
201
+ const zip = await waitForDeadline(loadZipArchiveWithPreflight(buffer, limits), params.deadline);
202
+ params.deadline.check();
203
+ const entries = Object.values(zip.files);
204
+ const strip = Math.max(0, Math.floor(params.stripComponents ?? 0));
205
+ assertArchiveEntryCountWithinLimit(entries.length, limits);
206
+ const budget = createByteBudgetTracker(limits);
207
+ await withStagedArchiveDestination({
208
+ destinationRealDir,
209
+ run: async (stagingDir) => {
210
+ const stagingRealDir = await fs.realpath(stagingDir);
211
+ for (const entry of entries) {
212
+ params.deadline.check();
213
+ const output = resolveZipOutputPath({
214
+ entryPath: entry.name,
215
+ strip,
216
+ destinationDir: stagingRealDir,
217
+ });
218
+ if (!output) {
219
+ continue;
220
+ }
221
+ await prepareZipOutputPath({
222
+ destinationDir: stagingRealDir,
223
+ destinationRealDir: stagingRealDir,
224
+ relPath: output.relPath,
225
+ outPath: output.outPath,
226
+ originalPath: entry.name,
227
+ isDirectory: entry.dir,
228
+ });
229
+ if (entry.dir) {
230
+ continue;
231
+ }
232
+ if (isZipSymlinkEntry(entry)) {
233
+ throw new Error(`zip entry is a link: ${entry.name}`);
234
+ }
235
+ await writeZipFileEntry({
236
+ entry,
237
+ outPath: output.outPath,
238
+ budget,
239
+ deadline: params.deadline,
240
+ });
145
241
  }
146
- await writeZipFileEntry({
147
- entry,
148
- outPath: output.outPath,
149
- budget,
242
+ params.deadline.check();
243
+ await mergeExtractedTreeIntoDestination({
244
+ sourceDir: stagingRealDir,
245
+ destinationDir: params.destDir,
246
+ destinationRealDir,
150
247
  });
151
- }
152
- await mergeExtractedTreeIntoDestination({
153
- sourceDir: stagingRealDir,
154
- destinationDir: params.destDir,
155
- destinationRealDir,
156
- });
157
- },
158
- });
248
+ params.deadline.check();
249
+ },
250
+ });
251
+ }
252
+ finally {
253
+ await stagedArchive.cleanup();
254
+ }
159
255
  }
160
256
  export async function extractArchive(params) {
161
257
  const kind = params.kind ?? resolveArchiveKind(params.archivePath);
@@ -164,62 +260,74 @@ export async function extractArchive(params) {
164
260
  }
165
261
  const label = kind === "zip" ? "extract zip" : "extract tar";
166
262
  if (kind === "tar") {
167
- await withTimeout((async () => {
263
+ await withExtractionDeadline(params.timeoutMs, label, async (deadline) => {
168
264
  const tar = await importOptionalTar();
169
265
  const limits = resolveExtractLimits(params.limits);
170
- const stat = await fs.stat(params.archivePath);
171
- if (stat.size > limits.maxArchiveBytes) {
172
- throw new ArchiveLimitError(ARCHIVE_LIMIT_ERROR_CODE.ARCHIVE_SIZE_EXCEEDS_LIMIT);
173
- }
174
- const destinationRealDir = await prepareArchiveDestinationDir(params.destDir);
175
- await withStagedArchiveDestination({
176
- destinationRealDir,
177
- run: async (stagingDir) => {
178
- const checkTarEntrySafety = createTarEntryPreflightChecker({
179
- rootDir: destinationRealDir,
180
- stripComponents: params.stripComponents,
181
- limits,
182
- });
183
- // A canonical cwd is not enough here: tar can still follow
184
- // pre-existing child symlinks in the live destination tree.
185
- // Extract into a private staging dir first, then merge through
186
- // the same safe-open boundary checks used by direct file writes.
187
- await tar.x({
188
- file: params.archivePath,
189
- cwd: stagingDir,
190
- strip: Math.max(0, Math.floor(params.stripComponents ?? 0)),
191
- gzip: params.tarGzip,
192
- preservePaths: false,
193
- strict: true,
194
- onReadEntry(entry) {
195
- try {
196
- checkTarEntrySafety(readTarEntryInfo(entry));
197
- }
198
- catch (err) {
199
- const error = err instanceof Error ? err : new Error(String(err));
200
- // Node's EventEmitter calls listeners with `this` bound to the
201
- // emitter (tar.Unpack), which exposes Parser.abort().
202
- const emitter = this;
203
- emitter.abort?.(error);
204
- }
205
- },
206
- });
207
- await mergeExtractedTreeIntoDestination({
208
- sourceDir: stagingDir,
209
- destinationDir: params.destDir,
210
- destinationRealDir,
211
- });
212
- },
266
+ const stagedArchive = await stageArchiveFileForExtraction({
267
+ archivePath: params.archivePath,
268
+ limits,
269
+ deadline,
213
270
  });
214
- })(), params.timeoutMs, label);
271
+ try {
272
+ const destinationRealDir = await prepareArchiveDestinationDir(params.destDir);
273
+ await withStagedArchiveDestination({
274
+ destinationRealDir,
275
+ run: async (stagingDir) => {
276
+ deadline.check();
277
+ const checkTarEntrySafety = createTarEntryPreflightChecker({
278
+ rootDir: destinationRealDir,
279
+ stripComponents: params.stripComponents,
280
+ limits,
281
+ });
282
+ // A canonical cwd is not enough here: tar can still follow
283
+ // pre-existing child symlinks in the live destination tree.
284
+ // Extract into a private staging dir first, then merge through
285
+ // the same safe-open boundary checks used by direct file writes.
286
+ await tar.x({
287
+ file: stagedArchive.path,
288
+ cwd: stagingDir,
289
+ strip: Math.max(0, Math.floor(params.stripComponents ?? 0)),
290
+ gzip: params.tarGzip,
291
+ signal: deadline.signal,
292
+ preservePaths: false,
293
+ strict: true,
294
+ onReadEntry(entry) {
295
+ try {
296
+ deadline.check();
297
+ checkTarEntrySafety(readTarEntryInfo(entry));
298
+ }
299
+ catch (err) {
300
+ const error = err instanceof Error ? err : new Error(String(err));
301
+ // Node's EventEmitter calls listeners with `this` bound to the
302
+ // emitter (tar.Unpack), which exposes Parser.abort().
303
+ const emitter = this;
304
+ emitter.abort?.(error);
305
+ }
306
+ },
307
+ });
308
+ deadline.check();
309
+ await mergeExtractedTreeIntoDestination({
310
+ sourceDir: stagingDir,
311
+ destinationDir: params.destDir,
312
+ destinationRealDir,
313
+ });
314
+ deadline.check();
315
+ },
316
+ });
317
+ }
318
+ finally {
319
+ await stagedArchive.cleanup();
320
+ }
321
+ });
215
322
  return;
216
323
  }
217
- await withTimeout(extractZip({
324
+ await withExtractionDeadline(params.timeoutMs, label, async (deadline) => extractZip({
218
325
  archivePath: params.archivePath,
219
326
  destDir: params.destDir,
220
327
  stripComponents: params.stripComponents,
221
328
  limits: params.limits,
222
- }), params.timeoutMs, label);
329
+ deadline,
330
+ }));
223
331
  }
224
332
  async function importOptionalTar() {
225
333
  try {
@@ -0,0 +1,11 @@
1
+ export type DenyMutationPolicy = {
2
+ paths?: readonly string[];
3
+ prefixes?: readonly string[];
4
+ };
5
+ type DenyMutationCheckOptions = {
6
+ protectAncestors?: boolean;
7
+ };
8
+ export declare function assertMutationNotDenied(filePath: string, policy: DenyMutationPolicy | undefined, options?: DenyMutationCheckOptions): Promise<void>;
9
+ export declare function mergeDenyMutationPolicies(defaultPolicy: DenyMutationPolicy | undefined, callPolicy: DenyMutationPolicy | undefined): DenyMutationPolicy | undefined;
10
+ export {};
11
+ //# sourceMappingURL=deny-mutations.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,102 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { FsSafeError } from "./errors.js";
4
+ import { assertNoNulPathInput, isNotFoundPathError, isPathInside } from "./path.js";
5
+ async function pathExists(filePath) {
6
+ try {
7
+ await fs.lstat(filePath);
8
+ return true;
9
+ }
10
+ catch (err) {
11
+ if (!isNotFoundPathError(err)) {
12
+ throw err;
13
+ }
14
+ return false;
15
+ }
16
+ }
17
+ async function resolvePathViaExistingAncestor(targetPath) {
18
+ const normalized = path.resolve(targetPath);
19
+ let cursor = normalized;
20
+ const missingSuffix = [];
21
+ while (path.dirname(cursor) !== cursor && !(await pathExists(cursor))) {
22
+ missingSuffix.unshift(path.basename(cursor));
23
+ cursor = path.dirname(cursor);
24
+ }
25
+ if (!(await pathExists(cursor))) {
26
+ return normalized;
27
+ }
28
+ try {
29
+ const resolvedAncestor = path.resolve(await fs.realpath(cursor));
30
+ return missingSuffix.length === 0
31
+ ? resolvedAncestor
32
+ : path.resolve(resolvedAncestor, ...missingSuffix);
33
+ }
34
+ catch {
35
+ return normalized;
36
+ }
37
+ }
38
+ async function comparablePaths(rawPath) {
39
+ assertNoNulPathInput(rawPath, "path contains a NUL byte");
40
+ const resolved = path.resolve(rawPath);
41
+ return new Set([resolved, await resolvePathViaExistingAncestor(resolved)]);
42
+ }
43
+ function isSamePath(left, right) {
44
+ return isPathInside(left, right) && isPathInside(right, left);
45
+ }
46
+ function hasPolicyEntries(policy) {
47
+ return Boolean(policy?.paths?.length || policy?.prefixes?.length);
48
+ }
49
+ function policyPathEntries(entries) {
50
+ const paths = [];
51
+ for (const entry of entries ?? []) {
52
+ if (entry.length === 0) {
53
+ throw new FsSafeError("invalid-path", "deny mutation paths must be non-empty");
54
+ }
55
+ assertNoNulPathInput(entry, "deny mutation path contains a NUL byte");
56
+ if (!path.isAbsolute(entry)) {
57
+ throw new FsSafeError("invalid-path", "deny mutation paths must be absolute");
58
+ }
59
+ paths.push(entry);
60
+ }
61
+ return paths;
62
+ }
63
+ export async function assertMutationNotDenied(filePath, policy, options = {}) {
64
+ if (!hasPolicyEntries(policy)) {
65
+ return;
66
+ }
67
+ const targetPaths = await comparablePaths(filePath);
68
+ for (const deniedPath of policyPathEntries(policy.paths)) {
69
+ const deniedPaths = await comparablePaths(deniedPath);
70
+ for (const target of targetPaths) {
71
+ for (const denied of deniedPaths) {
72
+ if (isSamePath(denied, target) ||
73
+ (options.protectAncestors === true && isPathInside(target, denied))) {
74
+ throw new FsSafeError("denied-path", "path is denied by denyMutations policy");
75
+ }
76
+ }
77
+ }
78
+ }
79
+ for (const deniedPrefix of policyPathEntries(policy.prefixes)) {
80
+ const deniedPaths = await comparablePaths(deniedPrefix);
81
+ for (const target of targetPaths) {
82
+ for (const denied of deniedPaths) {
83
+ if (isPathInside(denied, target) ||
84
+ (options.protectAncestors === true && isPathInside(target, denied))) {
85
+ throw new FsSafeError("denied-path", "path is denied by denyMutations policy");
86
+ }
87
+ }
88
+ }
89
+ }
90
+ }
91
+ export function mergeDenyMutationPolicies(defaultPolicy, callPolicy) {
92
+ if (!defaultPolicy) {
93
+ return callPolicy;
94
+ }
95
+ if (!callPolicy) {
96
+ return defaultPolicy;
97
+ }
98
+ return {
99
+ paths: [...(defaultPolicy.paths ?? []), ...(callPolicy.paths ?? [])],
100
+ prefixes: [...(defaultPolicy.prefixes ?? []), ...(callPolicy.prefixes ?? [])],
101
+ };
102
+ }
package/dist/errors.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type FsSafeErrorCode = "already-exists" | "hardlink" | "helper-failed" | "helper-unavailable" | "invalid-path" | "insecure-permissions" | "not-empty" | "not-file" | "not-found" | "not-owned" | "not-removable" | "outside-workspace" | "path-alias" | "path-mismatch" | "permission-unverified" | "symlink" | "timeout" | "too-large" | "unsupported-platform";
1
+ export type FsSafeErrorCode = "already-exists" | "denied-path" | "hardlink" | "helper-failed" | "helper-unavailable" | "invalid-path" | "insecure-permissions" | "not-empty" | "not-file" | "not-found" | "not-owned" | "not-removable" | "outside-workspace" | "path-alias" | "path-mismatch" | "permission-unverified" | "symlink" | "timeout" | "too-large" | "unsupported-platform";
2
2
  export type FsSafeErrorCategory = "policy" | "operational";
3
3
  export declare function categorizeFsSafeError(code: FsSafeErrorCode): FsSafeErrorCategory;
4
4
  export declare class FsSafeError extends Error {
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GACvB,gBAAgB,GAChB,UAAU,GACV,eAAe,GACf,oBAAoB,GACpB,cAAc,GACd,sBAAsB,GACtB,WAAW,GACX,UAAU,GACV,WAAW,GACX,WAAW,GACX,eAAe,GACf,mBAAmB,GACnB,YAAY,GACZ,eAAe,GACf,uBAAuB,GACvB,SAAS,GACT,SAAS,GACT,WAAW,GACX,sBAAsB,CAAC;AAE3B,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,aAAa,CAAC;AAU3D,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,eAAe,GAAG,mBAAmB,CAEhF;AAED,qBAAa,WAAY,SAAQ,KAAK;IACpC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAC;gBAE3B,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAO;CAMtF"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GACvB,gBAAgB,GAChB,aAAa,GACb,UAAU,GACV,eAAe,GACf,oBAAoB,GACpB,cAAc,GACd,sBAAsB,GACtB,WAAW,GACX,UAAU,GACV,WAAW,GACX,WAAW,GACX,eAAe,GACf,mBAAmB,GACnB,YAAY,GACZ,eAAe,GACf,uBAAuB,GACvB,SAAS,GACT,SAAS,GACT,WAAW,GACX,sBAAsB,CAAC;AAE3B,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,aAAa,CAAC;AAU3D,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,eAAe,GAAG,mBAAmB,CAEhF;AAED,qBAAa,WAAY,SAAQ,KAAK;IACpC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAC;gBAE3B,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAO;CAMtF"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { FsSafeError, categorizeFsSafeError, type FsSafeErrorCategory, type FsSafeErrorCode, } from "./errors.js";
2
- export { DEFAULT_ROOT_MAX_BYTES, root, type HardlinkPolicy, type OpenResult, type ReadResult, type Root, type RootAppendOptions, type RootCopyOptions, type RootCreateJsonOptions, type RootCreateOptions, type RootDefaults, type RootOpenOptions, type RootOpenWritableOptions, type RootOptions, type RootReadOptions, type RootWriteJsonOptions, type RootWriteOptions, type SymlinkPolicy, type WritableOpenMode, type WritableOpenResult, } from "./root.js";
2
+ export { DEFAULT_ROOT_MAX_BYTES, root, type DenyMutationPolicy, type HardlinkPolicy, type OpenResult, type ReadResult, type Root, type RootAppendOptions, type RootCopyOptions, type RootCreateJsonOptions, type RootCreateOptions, type RootDefaults, type RootMkdirOptions, type RootMoveOptions, type RootOpenOptions, type RootOpenWritableOptions, type RootOptions, type RootReadOptions, type RootRemoveOptions, type RootWriteJsonOptions, type RootWriteOptions, type SymlinkPolicy, type WritableOpenMode, type WritableOpenResult, } from "./root.js";
3
3
  export { configureFsSafePython, getFsSafePythonConfig, type FsSafePythonConfig, type FsSafePythonMode, } from "./pinned-python-config.js";
4
4
  export { writeExternalFileWithinRoot, type ExternalFileWriteOptions, type ExternalFileWriteResult, } from "./output.js";
5
5
  export { configureFsSafeLocks, getFsSafeLockConfig, type FsSafeLockConfig, } from "./lock-config.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,KAAK,mBAAmB,EACxB,KAAK,eAAe,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,sBAAsB,EACtB,IAAI,EACJ,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,IAAI,EACT,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,GACxB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,GACtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,2BAA2B,EAC3B,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,GAC7B,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,KAAK,gBAAgB,GACtB,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,KAAK,mBAAmB,EACxB,KAAK,eAAe,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,sBAAsB,EACtB,IAAI,EACJ,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,IAAI,EACT,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,GACxB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,GACtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,2BAA2B,EAC3B,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,GAC7B,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,KAAK,gBAAgB,GACtB,MAAM,kBAAkB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"json-durable-queue.d.ts","sourceRoot":"","sources":["../src/json-durable-queue.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,0BAA0B,GAAG;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,0BAA0B,CAAC,CAAC,IAAI;IAC1C,KAAK,EAAE,CAAC,CAAC;IACT,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,2BAA2B,CAAC,CAAC,IAAI;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9E,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAOF,eAAO,MAAM,0CAA0C,QAAmB,CAAC;AAY3E,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtE;AAED,wBAAsB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAUpF;AAmBD,wBAAgB,iCAAiC,CAC/C,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,MAAM,GACT,0BAA0B,CAM5B;AAED,wBAAsB,0BAA0B,CAAC,MAAM,EAAE;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhB;AA+KD,wBAAsB,0BAA0B,CAAC,MAAM,EAAE;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,IAAI,CAAC,CAOhB;AAiDD,wBAAsB,yBAAyB,CAAC,CAAC,EAC/C,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,OAAO,CAAC,CAAC,CAAC,CAOZ;AAED,wBAAsB,wBAAwB,CAAC,KAAK,EAAE,0BAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CAW/F;AAED,wBAAsB,yBAAyB,CAAC,CAAC,EAAE,MAAM,EAAE;IACzD,KAAK,EAAE,0BAA0B,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAwBpB;AAED,wBAAsB,kCAAkC,CAAC,CAAC,EACxD,OAAO,EAAE,2BAA2B,CAAC,CAAC,CAAC,GACtC,OAAO,CAAC,CAAC,EAAE,CAAC,CAkDd;AAED,wBAAsB,iCAAiC,CAAC,MAAM,EAAE;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;CACZ,GAAG,OAAO,CAAC,IAAI,CAAC,CAShB"}
1
+ {"version":3,"file":"json-durable-queue.d.ts","sourceRoot":"","sources":["../src/json-durable-queue.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,0BAA0B,GAAG;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,0BAA0B,CAAC,CAAC,IAAI;IAC1C,KAAK,EAAE,CAAC,CAAC;IACT,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,2BAA2B,CAAC,CAAC,IAAI;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9E,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAOF,eAAO,MAAM,0CAA0C,QAAmB,CAAC;AAY3E,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtE;AAED,wBAAsB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAUpF;AAmBD,wBAAgB,iCAAiC,CAC/C,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,MAAM,GACT,0BAA0B,CAM5B;AAED,wBAAsB,0BAA0B,CAAC,MAAM,EAAE;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhB;AA+KD,wBAAsB,0BAA0B,CAAC,MAAM,EAAE;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,IAAI,CAAC,CAOhB;AAsDD,wBAAsB,yBAAyB,CAAC,CAAC,EAC/C,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,OAAO,CAAC,CAAC,CAAC,CAOZ;AAED,wBAAsB,wBAAwB,CAAC,KAAK,EAAE,0BAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CAW/F;AAED,wBAAsB,yBAAyB,CAAC,CAAC,EAAE,MAAM,EAAE;IACzD,KAAK,EAAE,0BAA0B,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAwBpB;AAED,wBAAsB,kCAAkC,CAAC,CAAC,EACxD,OAAO,EAAE,2BAA2B,CAAC,CAAC,CAAC,GACtC,OAAO,CAAC,CAAC,EAAE,CAAC,CAkDd;AAED,wBAAsB,iCAAiC,CAAC,MAAM,EAAE;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;CACZ,GAAG,OAAO,CAAC,IAAI,CAAC,CAShB"}
@@ -215,6 +215,9 @@ async function readBoundedUtf8File(params) {
215
215
  if (initialStat.isSymbolicLink() || !initialStat.isFile()) {
216
216
  throw new Error("queue entry is not a regular file");
217
217
  }
218
+ if (initialStat.nlink > 1) {
219
+ throw new Error("queue entry hardlinks are not allowed");
220
+ }
218
221
  if (initialStat.size > params.maxBytes) {
219
222
  throw new Error(`queue entry exceeds ${params.maxBytes} bytes`);
220
223
  }
@@ -228,6 +231,8 @@ async function readBoundedUtf8File(params) {
228
231
  if (!openedStat.isFile() ||
229
232
  pathStat.isSymbolicLink() ||
230
233
  !pathStat.isFile() ||
234
+ openedStat.nlink > 1 ||
235
+ pathStat.nlink > 1 ||
231
236
  !sameFileIdentity(initialStat, openedStat) ||
232
237
  !sameFileIdentity(pathStat, openedStat)) {
233
238
  throw new Error("queue entry changed during read");
@@ -1 +1 @@
1
- {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":"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"}