@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.
- package/CHANGELOG.md +17 -0
- package/README.md +21 -7
- package/dist/archive-deadline.d.ts +9 -0
- package/dist/archive-deadline.d.ts.map +1 -0
- package/dist/archive-deadline.js +67 -0
- package/dist/archive-file-io.d.ts +9 -0
- package/dist/archive-file-io.d.ts.map +1 -0
- package/dist/archive-file-io.js +11 -0
- package/dist/archive-staging.d.ts.map +1 -1
- package/dist/archive-staging.js +43 -3
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +213 -105
- package/dist/deny-mutations.d.ts +11 -0
- package/dist/deny-mutations.d.ts.map +1 -0
- package/dist/deny-mutations.js +102 -0
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/json-durable-queue.d.ts.map +1 -1
- package/dist/json-durable-queue.js +5 -0
- package/dist/json.d.ts.map +1 -1
- package/dist/json.js +51 -4
- package/dist/opened-realpath.d.ts +3 -0
- package/dist/opened-realpath.d.ts.map +1 -0
- package/dist/opened-realpath.js +79 -0
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +1 -0
- package/dist/pinned-write.js +3 -9
- package/dist/read-opened-file.d.ts +18 -0
- package/dist/read-opened-file.d.ts.map +1 -0
- package/dist/read-opened-file.js +15 -0
- package/dist/regular-file.d.ts.map +1 -1
- package/dist/regular-file.js +23 -3
- package/dist/root-impl.d.ts +18 -15
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +60 -100
- package/dist/root-paths.d.ts.map +1 -1
- package/dist/root-paths.js +16 -5
- package/dist/root.d.ts +1 -1
- package/dist/root.d.ts.map +1 -1
- package/dist/secret-file.d.ts +1 -0
- package/dist/secret-file.d.ts.map +1 -1
- package/dist/secret-file.js +23 -3
- package/dist/sidecar-lock.d.ts.map +1 -1
- package/dist/sidecar-lock.js +9 -3
- package/dist/symlink-parents.js +2 -2
- package/docs/errors.md +2 -0
- package/docs/python-helper.md +3 -3
- package/docs/root.md +19 -9
- package/docs/secret-file.md +3 -2
- package/docs/security-model.md +4 -0
- package/docs/timing.md +1 -1
- package/docs/types.md +21 -9
- package/docs/writing.md +19 -1
- 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 {
|
|
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
|
-
|
|
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
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
242
|
+
params.deadline.check();
|
|
243
|
+
await mergeExtractedTreeIntoDestination({
|
|
244
|
+
sourceDir: stagingRealDir,
|
|
245
|
+
destinationDir: params.destDir,
|
|
246
|
+
destinationRealDir,
|
|
150
247
|
});
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
|
263
|
+
await withExtractionDeadline(params.timeoutMs, label, async (deadline) => {
|
|
168
264
|
const tar = await importOptionalTar();
|
|
169
265
|
const limits = resolveExtractLimits(params.limits);
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 {
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -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";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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;
|
|
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");
|
package/dist/json.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":"AACA,OAAO,MAAM,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":"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"}
|