@happyvertical/files 0.78.2 → 0.78.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.
- package/README.md +76 -0
- package/dist/archive.d.ts +84 -0
- package/dist/archive.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +754 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { createWriteStream, existsSync, statSync, constants } from "node:fs";
|
|
3
|
-
import {
|
|
3
|
+
import { lstat, realpath, rename, rm, writeFile, stat, chmod, chown, mkdir, readFile, readdir, access, rmdir, unlink, copyFile } from "node:fs/promises";
|
|
4
4
|
import * as path from "node:path";
|
|
5
5
|
import { join, dirname, basename, resolve, isAbsolute, relative, sep, extname } from "node:path";
|
|
6
6
|
import { Readable, Transform } from "node:stream";
|
|
@@ -8,6 +8,752 @@ import { pipeline } from "node:stream/promises";
|
|
|
8
8
|
import { getTempDirectory } from "@happyvertical/utils";
|
|
9
9
|
import { URL as URL$1 } from "node:url";
|
|
10
10
|
import { S3Client, ListObjectsV2Command, HeadObjectCommand, GetObjectCommand, PutObjectCommand, DeleteObjectCommand, CopyObjectCommand } from "@aws-sdk/client-s3";
|
|
11
|
+
const CENTRAL_DIRECTORY_SIGNATURE = 33639248;
|
|
12
|
+
const END_OF_CENTRAL_DIRECTORY_SIGNATURE = 101010256;
|
|
13
|
+
const LOCAL_FILE_HEADER_SIGNATURE = 67324752;
|
|
14
|
+
const ZIP64_END_OF_CENTRAL_DIRECTORY_SIGNATURE = 101075792;
|
|
15
|
+
const ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIGNATURE = 117853008;
|
|
16
|
+
const ZIP64_EXTRA_FIELD_ID = 1;
|
|
17
|
+
const PKWARE_EXTENDED_LANGUAGE_ENCODING_EXTRA_FIELD_ID = 8;
|
|
18
|
+
const PKWARE_UNIX_EXTRA_FIELD_ID = 13;
|
|
19
|
+
const STRONG_ENCRYPTION_EXTRA_FIELD_ID = 23;
|
|
20
|
+
const XCEED_UNICODE_PATH_EXTRA_FIELD_ID = 21838;
|
|
21
|
+
const LIBARCHIVE_EXTRA_FIELD_ID = 27768;
|
|
22
|
+
const UNICODE_PATH_EXTRA_FIELD_ID = 28789;
|
|
23
|
+
const ASI_UNIX_EXTRA_FIELD_ID = 30062;
|
|
24
|
+
const WINZIP_AES_EXTRA_FIELD_ID = 39169;
|
|
25
|
+
const WINZIP_AES_COMPRESSION_METHOD = 99;
|
|
26
|
+
const END_OF_CENTRAL_DIRECTORY_SIZE = 22;
|
|
27
|
+
const MAX_ZIP_COMMENT_BYTES = 65535;
|
|
28
|
+
const CENTRAL_DIRECTORY_ENTRY_SIZE = 46;
|
|
29
|
+
const LOCAL_FILE_HEADER_SIZE = 30;
|
|
30
|
+
const ENCRYPTED_FLAG = 1;
|
|
31
|
+
const STRONG_ENCRYPTION_FLAG = 64;
|
|
32
|
+
const DATA_DESCRIPTOR_FLAG = 8;
|
|
33
|
+
const MASKED_LOCAL_HEADER_FLAG = 8192;
|
|
34
|
+
const UNIX_FILE_TYPE_MASK = 61440;
|
|
35
|
+
const UNIX_DIRECTORY_TYPE = 16384;
|
|
36
|
+
const UNIX_REGULAR_FILE_TYPE = 32768;
|
|
37
|
+
const UNIX_SYMLINK_TYPE = 40960;
|
|
38
|
+
const DOS_DIRECTORY_ATTRIBUTE = 16;
|
|
39
|
+
const WINDOWS_REPARSE_POINT_ATTRIBUTE = 1024;
|
|
40
|
+
const UNIX_CREATOR_SYSTEM = 3;
|
|
41
|
+
const DARWIN_CREATOR_SYSTEM = 19;
|
|
42
|
+
const WINDOWS_RESERVED_DEVICE_BASENAME = /^(?:aux|con|conin\$|conout\$|nul|prn|com[1-9\u00b9\u00b2\u00b3]|lpt[1-9\u00b9\u00b2\u00b3])$/iu;
|
|
43
|
+
const utf8Decoder = new TextDecoder("utf-8", {
|
|
44
|
+
fatal: true,
|
|
45
|
+
ignoreBOM: true
|
|
46
|
+
});
|
|
47
|
+
const DEFAULT_ZIP_MANIFEST_LIMITS = Object.freeze({
|
|
48
|
+
maxEntries: 1e4,
|
|
49
|
+
maxEntryUncompressedBytes: 200 * 1024 * 1024,
|
|
50
|
+
maxTotalUncompressedBytes: 2 * 1024 * 1024 * 1024,
|
|
51
|
+
maxPathBytes: 1024
|
|
52
|
+
});
|
|
53
|
+
class ZipManifestError extends Error {
|
|
54
|
+
constructor(message, code, entryPath) {
|
|
55
|
+
super(message);
|
|
56
|
+
this.code = code;
|
|
57
|
+
this.entryPath = entryPath;
|
|
58
|
+
this.name = "ZipManifestError";
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
class InvalidZipArchiveError extends ZipManifestError {
|
|
62
|
+
constructor(message) {
|
|
63
|
+
super(message, "ZIP_INVALID");
|
|
64
|
+
this.name = "InvalidZipArchiveError";
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
class UnsafeZipEntryError extends ZipManifestError {
|
|
68
|
+
constructor(reason, entryPath, message) {
|
|
69
|
+
super(message, "ZIP_UNSAFE_ENTRY", entryPath);
|
|
70
|
+
this.reason = reason;
|
|
71
|
+
this.name = "UnsafeZipEntryError";
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
class ZipManifestLimitError extends ZipManifestError {
|
|
75
|
+
constructor(limit, actual, maximum, message, entryPath) {
|
|
76
|
+
super(message, "ZIP_LIMIT_EXCEEDED", entryPath);
|
|
77
|
+
this.limit = limit;
|
|
78
|
+
this.actual = actual;
|
|
79
|
+
this.maximum = maximum;
|
|
80
|
+
this.name = "ZipManifestLimitError";
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
class UnsupportedZipFeatureError extends ZipManifestError {
|
|
84
|
+
constructor(feature, message, entryPath) {
|
|
85
|
+
super(message, "ZIP_UNSUPPORTED_FEATURE", entryPath);
|
|
86
|
+
this.feature = feature;
|
|
87
|
+
this.name = "UnsupportedZipFeatureError";
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function inspectZipManifest(data, limits = {}) {
|
|
91
|
+
if (!(data instanceof Uint8Array)) {
|
|
92
|
+
throw new TypeError("inspectZipManifest data must be a Uint8Array");
|
|
93
|
+
}
|
|
94
|
+
const resolvedLimits = resolveLimits(limits);
|
|
95
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
96
|
+
const endRecord = findEndOfCentralDirectory(view, resolvedLimits.maxEntries);
|
|
97
|
+
assertClassicZip(endRecord, view);
|
|
98
|
+
if (endRecord.totalEntries > resolvedLimits.maxEntries) {
|
|
99
|
+
throw new ZipManifestLimitError(
|
|
100
|
+
"entry-count",
|
|
101
|
+
endRecord.totalEntries,
|
|
102
|
+
resolvedLimits.maxEntries,
|
|
103
|
+
`ZIP archive contains ${endRecord.totalEntries} entries; the limit is ${resolvedLimits.maxEntries}.`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
if (endRecord.centralDirectoryOffset > endRecord.offset || endRecord.centralDirectorySize > endRecord.offset - endRecord.centralDirectoryOffset) {
|
|
107
|
+
throw new InvalidZipArchiveError(
|
|
108
|
+
"ZIP central directory points outside the archive metadata bounds."
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
const centralDirectoryEnd = endRecord.centralDirectoryOffset + endRecord.centralDirectorySize;
|
|
112
|
+
if (centralDirectoryEnd !== endRecord.offset) {
|
|
113
|
+
throw new InvalidZipArchiveError(
|
|
114
|
+
"ZIP central directory size or offset does not match the end record."
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
const entries = [];
|
|
118
|
+
const localEntryRanges = [];
|
|
119
|
+
const normalizedPaths = /* @__PURE__ */ new Set();
|
|
120
|
+
const portablePaths = /* @__PURE__ */ new Map();
|
|
121
|
+
let totalUncompressedBytes = 0;
|
|
122
|
+
let fileCount = 0;
|
|
123
|
+
let directoryCount = 0;
|
|
124
|
+
let offset = endRecord.centralDirectoryOffset;
|
|
125
|
+
for (let index = 0; index < endRecord.totalEntries; index += 1) {
|
|
126
|
+
assertRange(
|
|
127
|
+
offset,
|
|
128
|
+
CENTRAL_DIRECTORY_ENTRY_SIZE,
|
|
129
|
+
centralDirectoryEnd,
|
|
130
|
+
"ZIP central directory is truncated."
|
|
131
|
+
);
|
|
132
|
+
if (view.getUint32(offset, true) !== CENTRAL_DIRECTORY_SIGNATURE) {
|
|
133
|
+
throw new InvalidZipArchiveError(
|
|
134
|
+
`ZIP central directory entry ${index + 1} has an invalid signature.`
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
const versionMadeBy = view.getUint16(offset + 4, true);
|
|
138
|
+
const flags = view.getUint16(offset + 8, true);
|
|
139
|
+
const compressionMethod = view.getUint16(offset + 10, true);
|
|
140
|
+
const crc32 = view.getUint32(offset + 16, true);
|
|
141
|
+
const compressedSize = view.getUint32(offset + 20, true);
|
|
142
|
+
const uncompressedSize = view.getUint32(offset + 24, true);
|
|
143
|
+
const fileNameLength = view.getUint16(offset + 28, true);
|
|
144
|
+
const extraFieldLength = view.getUint16(offset + 30, true);
|
|
145
|
+
const commentLength = view.getUint16(offset + 32, true);
|
|
146
|
+
const startingDisk = view.getUint16(offset + 34, true);
|
|
147
|
+
const externalAttributes = view.getUint32(offset + 38, true);
|
|
148
|
+
const localHeaderOffset = view.getUint32(offset + 42, true);
|
|
149
|
+
if (compressedSize === 4294967295 || uncompressedSize === 4294967295 || localHeaderOffset === 4294967295 || startingDisk === 65535) {
|
|
150
|
+
throw zip64Error();
|
|
151
|
+
}
|
|
152
|
+
if (startingDisk !== 0) {
|
|
153
|
+
throw multiDiskError();
|
|
154
|
+
}
|
|
155
|
+
assertNotEncrypted(flags);
|
|
156
|
+
assertNotAesCompression(compressionMethod);
|
|
157
|
+
const nameStart = offset + CENTRAL_DIRECTORY_ENTRY_SIZE;
|
|
158
|
+
const nameEnd = nameStart + fileNameLength;
|
|
159
|
+
const extraEnd = nameEnd + extraFieldLength;
|
|
160
|
+
const entryEnd = extraEnd + commentLength;
|
|
161
|
+
assertRange(
|
|
162
|
+
offset,
|
|
163
|
+
entryEnd - offset,
|
|
164
|
+
centralDirectoryEnd,
|
|
165
|
+
"ZIP central directory entry metadata is truncated."
|
|
166
|
+
);
|
|
167
|
+
if (fileNameLength > resolvedLimits.maxPathBytes) {
|
|
168
|
+
throw new ZipManifestLimitError(
|
|
169
|
+
"path-bytes",
|
|
170
|
+
fileNameLength,
|
|
171
|
+
resolvedLimits.maxPathBytes,
|
|
172
|
+
`ZIP entry path is ${fileNameLength} bytes; the limit is ${resolvedLimits.maxPathBytes}.`
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
assertExtraFields(data, nameEnd, extraEnd, "central directory");
|
|
176
|
+
const rawPathBytes = data.subarray(nameStart, nameEnd);
|
|
177
|
+
const rawPath = decodeEntryPath(rawPathBytes);
|
|
178
|
+
const path2 = normalizeEntryPath(rawPath);
|
|
179
|
+
const unixMode = externalAttributes >>> 16 & 65535;
|
|
180
|
+
const unixFileType = unixMode & UNIX_FILE_TYPE_MASK;
|
|
181
|
+
const creatorSystem = versionMadeBy >>> 8;
|
|
182
|
+
if ((externalAttributes & WINDOWS_REPARSE_POINT_ATTRIBUTE) !== 0) {
|
|
183
|
+
throw new UnsafeZipEntryError(
|
|
184
|
+
"reparse-point",
|
|
185
|
+
rawPath,
|
|
186
|
+
`ZIP entry "${rawPath}" is a Windows reparse point, which is not allowed.`
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
if (unixFileType !== 0 && creatorSystem !== UNIX_CREATOR_SYSTEM && creatorSystem !== DARWIN_CREATOR_SYSTEM) {
|
|
190
|
+
throw new UnsupportedZipFeatureError(
|
|
191
|
+
"ambiguous-metadata",
|
|
192
|
+
`ZIP entry "${rawPath}" declares Unix file-type metadata from a non-Unix creator system.`,
|
|
193
|
+
rawPath
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
if (unixFileType === UNIX_SYMLINK_TYPE) {
|
|
197
|
+
throw new UnsafeZipEntryError(
|
|
198
|
+
"symlink",
|
|
199
|
+
rawPath,
|
|
200
|
+
`ZIP entry "${rawPath}" is a symbolic link, which is not allowed.`
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
if (unixFileType !== 0 && unixFileType !== UNIX_DIRECTORY_TYPE && unixFileType !== UNIX_REGULAR_FILE_TYPE) {
|
|
204
|
+
throw new UnsafeZipEntryError(
|
|
205
|
+
"special-file",
|
|
206
|
+
rawPath,
|
|
207
|
+
`ZIP entry "${rawPath}" uses an unsupported Unix special file type.`
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
if (compressionMethod === 0 && compressedSize !== uncompressedSize) {
|
|
211
|
+
throw new InvalidZipArchiveError(
|
|
212
|
+
`Stored ZIP entry "${path2}" declares different compressed and uncompressed sizes.`
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
const hasDirectoryPathMarker = rawPath.endsWith("/") || rawPath.endsWith("\\");
|
|
216
|
+
const hasDosDirectoryAttribute = (externalAttributes & DOS_DIRECTORY_ATTRIBUTE) !== 0;
|
|
217
|
+
if (unixFileType === UNIX_REGULAR_FILE_TYPE && (hasDirectoryPathMarker || hasDosDirectoryAttribute)) {
|
|
218
|
+
throw new UnsupportedZipFeatureError(
|
|
219
|
+
"ambiguous-metadata",
|
|
220
|
+
`ZIP entry "${rawPath}" has contradictory Unix file and directory metadata.`,
|
|
221
|
+
rawPath
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
const type = hasDirectoryPathMarker || unixFileType === UNIX_DIRECTORY_TYPE || hasDosDirectoryAttribute ? "directory" : "file";
|
|
225
|
+
if (normalizedPaths.has(path2)) {
|
|
226
|
+
throw new UnsafeZipEntryError(
|
|
227
|
+
"duplicate-path",
|
|
228
|
+
rawPath,
|
|
229
|
+
`ZIP entry "${rawPath}" collides with another normalized path ("${path2}").`
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
const portableKey = portablePathKey(path2);
|
|
233
|
+
const existingPortablePath = portablePaths.get(portableKey);
|
|
234
|
+
if (existingPortablePath !== void 0) {
|
|
235
|
+
throw new UnsafeZipEntryError(
|
|
236
|
+
"portable-path-collision",
|
|
237
|
+
rawPath,
|
|
238
|
+
`ZIP entry "${rawPath}" collides with "${existingPortablePath}" on a case-insensitive or Unicode-normalizing filesystem.`
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
normalizedPaths.add(path2);
|
|
242
|
+
portablePaths.set(portableKey, path2);
|
|
243
|
+
const localDataEnd = validateLocalHeader(data, view, {
|
|
244
|
+
centralDirectoryOffset: endRecord.centralDirectoryOffset,
|
|
245
|
+
centralCrc32: crc32,
|
|
246
|
+
centralFlags: flags,
|
|
247
|
+
centralNameStart: nameStart,
|
|
248
|
+
centralNameEnd: nameEnd,
|
|
249
|
+
compressedSize,
|
|
250
|
+
compressionMethod,
|
|
251
|
+
localHeaderOffset,
|
|
252
|
+
rawPath,
|
|
253
|
+
uncompressedSize
|
|
254
|
+
});
|
|
255
|
+
localEntryRanges.push({
|
|
256
|
+
end: localDataEnd,
|
|
257
|
+
path: path2,
|
|
258
|
+
start: localHeaderOffset
|
|
259
|
+
});
|
|
260
|
+
if (uncompressedSize > resolvedLimits.maxEntryUncompressedBytes) {
|
|
261
|
+
throw new ZipManifestLimitError(
|
|
262
|
+
"entry-uncompressed-size",
|
|
263
|
+
uncompressedSize,
|
|
264
|
+
resolvedLimits.maxEntryUncompressedBytes,
|
|
265
|
+
`ZIP entry "${path2}" declares ${uncompressedSize} uncompressed bytes; the per-entry limit is ${resolvedLimits.maxEntryUncompressedBytes}.`,
|
|
266
|
+
path2
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
totalUncompressedBytes += uncompressedSize;
|
|
270
|
+
if (totalUncompressedBytes > resolvedLimits.maxTotalUncompressedBytes) {
|
|
271
|
+
throw new ZipManifestLimitError(
|
|
272
|
+
"total-uncompressed-size",
|
|
273
|
+
totalUncompressedBytes,
|
|
274
|
+
resolvedLimits.maxTotalUncompressedBytes,
|
|
275
|
+
`ZIP archive declares ${totalUncompressedBytes} uncompressed bytes; the aggregate limit is ${resolvedLimits.maxTotalUncompressedBytes}.`,
|
|
276
|
+
path2
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
if (type === "directory") {
|
|
280
|
+
directoryCount += 1;
|
|
281
|
+
} else {
|
|
282
|
+
fileCount += 1;
|
|
283
|
+
}
|
|
284
|
+
entries.push({
|
|
285
|
+
path: path2,
|
|
286
|
+
type,
|
|
287
|
+
size: uncompressedSize,
|
|
288
|
+
compressedSize,
|
|
289
|
+
compressionMethod
|
|
290
|
+
});
|
|
291
|
+
offset = entryEnd;
|
|
292
|
+
}
|
|
293
|
+
if (offset !== centralDirectoryEnd) {
|
|
294
|
+
throw new InvalidZipArchiveError(
|
|
295
|
+
"ZIP central directory entry count does not match its declared size."
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
assertLocalEntryRangesCoverArchiveData(
|
|
299
|
+
localEntryRanges,
|
|
300
|
+
endRecord.centralDirectoryOffset
|
|
301
|
+
);
|
|
302
|
+
assertNoFileDescendantConflicts(entries);
|
|
303
|
+
return {
|
|
304
|
+
entries,
|
|
305
|
+
entryCount: entries.length,
|
|
306
|
+
fileCount,
|
|
307
|
+
directoryCount,
|
|
308
|
+
totalUncompressedBytes
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
function assertLocalEntryRangesCoverArchiveData(ranges, centralDirectoryOffset) {
|
|
312
|
+
const sortedRanges = [...ranges].sort(
|
|
313
|
+
(left, right) => left.start - right.start || left.end - right.end
|
|
314
|
+
);
|
|
315
|
+
let expectedStart = 0;
|
|
316
|
+
for (const range of sortedRanges) {
|
|
317
|
+
if (range.start < expectedStart) {
|
|
318
|
+
throw new InvalidZipArchiveError(
|
|
319
|
+
`ZIP local entry ranges overlap at "${range.path}".`
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
if (range.start > expectedStart) {
|
|
323
|
+
throw new InvalidZipArchiveError(
|
|
324
|
+
"ZIP contains data before the central directory that is not described by a central-directory entry."
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
expectedStart = range.end;
|
|
328
|
+
}
|
|
329
|
+
if (expectedStart !== centralDirectoryOffset) {
|
|
330
|
+
throw new InvalidZipArchiveError(
|
|
331
|
+
"ZIP contains data before the central directory that is not described by a central-directory entry."
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
function assertNoFileDescendantConflicts(entries) {
|
|
336
|
+
const sortedEntries = entries.map((entry) => ({ entry, portableKey: portablePathKey(entry.path) })).sort(
|
|
337
|
+
(left, right) => left.portableKey < right.portableKey ? -1 : left.portableKey > right.portableKey ? 1 : 0
|
|
338
|
+
);
|
|
339
|
+
for (const { entry, portableKey } of sortedEntries) {
|
|
340
|
+
if (entry.type !== "file") {
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
const descendantPrefix = `${portableKey}/`;
|
|
344
|
+
let low = 0;
|
|
345
|
+
let high = sortedEntries.length;
|
|
346
|
+
while (low < high) {
|
|
347
|
+
const middle = low + Math.floor((high - low) / 2);
|
|
348
|
+
if (sortedEntries[middle].portableKey < descendantPrefix) {
|
|
349
|
+
low = middle + 1;
|
|
350
|
+
} else {
|
|
351
|
+
high = middle;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
const descendant = sortedEntries[low];
|
|
355
|
+
if (descendant?.portableKey.startsWith(descendantPrefix)) {
|
|
356
|
+
throw new UnsafeZipEntryError(
|
|
357
|
+
"path-conflict",
|
|
358
|
+
entry.path,
|
|
359
|
+
`ZIP file entry "${entry.path}" conflicts with descendant entry "${descendant.entry.path}".`
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
function resolveLimits(limits) {
|
|
365
|
+
return {
|
|
366
|
+
maxEntries: validateLimit(
|
|
367
|
+
"maxEntries",
|
|
368
|
+
limits.maxEntries ?? DEFAULT_ZIP_MANIFEST_LIMITS.maxEntries
|
|
369
|
+
),
|
|
370
|
+
maxEntryUncompressedBytes: validateLimit(
|
|
371
|
+
"maxEntryUncompressedBytes",
|
|
372
|
+
limits.maxEntryUncompressedBytes ?? DEFAULT_ZIP_MANIFEST_LIMITS.maxEntryUncompressedBytes
|
|
373
|
+
),
|
|
374
|
+
maxTotalUncompressedBytes: validateLimit(
|
|
375
|
+
"maxTotalUncompressedBytes",
|
|
376
|
+
limits.maxTotalUncompressedBytes ?? DEFAULT_ZIP_MANIFEST_LIMITS.maxTotalUncompressedBytes
|
|
377
|
+
),
|
|
378
|
+
maxPathBytes: validateLimit(
|
|
379
|
+
"maxPathBytes",
|
|
380
|
+
limits.maxPathBytes ?? DEFAULT_ZIP_MANIFEST_LIMITS.maxPathBytes
|
|
381
|
+
)
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
function validateLimit(name, value) {
|
|
385
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
386
|
+
throw new RangeError(`${name} must be a non-negative safe integer`);
|
|
387
|
+
}
|
|
388
|
+
return value;
|
|
389
|
+
}
|
|
390
|
+
function findEndOfCentralDirectory(view, maxEntries) {
|
|
391
|
+
if (view.byteLength < END_OF_CENTRAL_DIRECTORY_SIZE) {
|
|
392
|
+
throw new InvalidZipArchiveError(
|
|
393
|
+
"Data is too small to contain a ZIP end-of-central-directory record."
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
const earliestOffset = Math.max(
|
|
397
|
+
0,
|
|
398
|
+
view.byteLength - END_OF_CENTRAL_DIRECTORY_SIZE - MAX_ZIP_COMMENT_BYTES
|
|
399
|
+
);
|
|
400
|
+
const validationBudget = {
|
|
401
|
+
maxEntries,
|
|
402
|
+
remainingEntryChecks: Math.min(maxEntries, 65534) + 1
|
|
403
|
+
};
|
|
404
|
+
let endRecord;
|
|
405
|
+
for (let offset = view.byteLength - END_OF_CENTRAL_DIRECTORY_SIZE; offset >= earliestOffset; offset -= 1) {
|
|
406
|
+
if (view.getUint32(offset, true) !== END_OF_CENTRAL_DIRECTORY_SIGNATURE) {
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
const commentLength = view.getUint16(offset + 20, true);
|
|
410
|
+
if (offset + END_OF_CENTRAL_DIRECTORY_SIZE + commentLength !== view.byteLength) {
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
const candidate = {
|
|
414
|
+
offset,
|
|
415
|
+
diskNumber: view.getUint16(offset + 4, true),
|
|
416
|
+
centralDirectoryDisk: view.getUint16(offset + 6, true),
|
|
417
|
+
entriesOnDisk: view.getUint16(offset + 8, true),
|
|
418
|
+
totalEntries: view.getUint16(offset + 10, true),
|
|
419
|
+
centralDirectorySize: view.getUint32(offset + 12, true),
|
|
420
|
+
centralDirectoryOffset: view.getUint32(offset + 16, true)
|
|
421
|
+
};
|
|
422
|
+
if (isStructurallyPlausibleEndRecord(view, candidate, validationBudget)) {
|
|
423
|
+
if (endRecord) {
|
|
424
|
+
throw new InvalidZipArchiveError(
|
|
425
|
+
"ZIP archive contains multiple structurally valid end-of-central-directory records."
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
endRecord = candidate;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
if (endRecord) {
|
|
432
|
+
return endRecord;
|
|
433
|
+
}
|
|
434
|
+
throw new InvalidZipArchiveError(
|
|
435
|
+
"ZIP end-of-central-directory record is missing or truncated."
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
function isStructurallyPlausibleEndRecord(view, endRecord, budget) {
|
|
439
|
+
if (hasStructurallyValidZip64Chain(view, endRecord)) {
|
|
440
|
+
return true;
|
|
441
|
+
}
|
|
442
|
+
if (endRecord.centralDirectoryOffset === 4294967295 || endRecord.centralDirectorySize === 4294967295) {
|
|
443
|
+
return false;
|
|
444
|
+
}
|
|
445
|
+
const centralDirectoryEnd = endRecord.centralDirectoryOffset + endRecord.centralDirectorySize;
|
|
446
|
+
if (endRecord.centralDirectoryOffset > endRecord.offset || centralDirectoryEnd !== endRecord.offset) {
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
if (endRecord.entriesOnDisk === 65535 || endRecord.totalEntries === 65535) {
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
if (endRecord.totalEntries > budget.maxEntries) {
|
|
453
|
+
return endRecord.centralDirectorySize >= endRecord.totalEntries * CENTRAL_DIRECTORY_ENTRY_SIZE && endRecord.centralDirectoryOffset <= endRecord.offset - CENTRAL_DIRECTORY_ENTRY_SIZE && view.getUint32(endRecord.centralDirectoryOffset, true) === CENTRAL_DIRECTORY_SIGNATURE;
|
|
454
|
+
}
|
|
455
|
+
let offset = endRecord.centralDirectoryOffset;
|
|
456
|
+
for (let index = 0; index < endRecord.totalEntries; index += 1) {
|
|
457
|
+
if (budget.remainingEntryChecks === 0) {
|
|
458
|
+
throw new InvalidZipArchiveError(
|
|
459
|
+
"ZIP end-record candidate validation exceeded the configured entry-check budget."
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
budget.remainingEntryChecks -= 1;
|
|
463
|
+
if (offset > endRecord.offset - CENTRAL_DIRECTORY_ENTRY_SIZE || view.getUint32(offset, true) !== CENTRAL_DIRECTORY_SIGNATURE) {
|
|
464
|
+
return false;
|
|
465
|
+
}
|
|
466
|
+
const fileNameLength = view.getUint16(offset + 28, true);
|
|
467
|
+
const extraFieldLength = view.getUint16(offset + 30, true);
|
|
468
|
+
const commentLength = view.getUint16(offset + 32, true);
|
|
469
|
+
const entryLength = CENTRAL_DIRECTORY_ENTRY_SIZE + fileNameLength + extraFieldLength + commentLength;
|
|
470
|
+
if (entryLength > endRecord.offset - offset) {
|
|
471
|
+
return false;
|
|
472
|
+
}
|
|
473
|
+
offset += entryLength;
|
|
474
|
+
}
|
|
475
|
+
return offset === endRecord.offset;
|
|
476
|
+
}
|
|
477
|
+
function hasStructurallyValidZip64Chain(view, endRecord) {
|
|
478
|
+
const locatorOffset = endRecord.offset - 20;
|
|
479
|
+
if (locatorOffset < 0 || view.getUint32(locatorOffset, true) !== ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIGNATURE) {
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
const zip64Offset = readSafeUint64(view, locatorOffset + 8);
|
|
483
|
+
if (zip64Offset === void 0 || zip64Offset > locatorOffset - 56 || view.getUint32(zip64Offset, true) !== ZIP64_END_OF_CENTRAL_DIRECTORY_SIGNATURE) {
|
|
484
|
+
return false;
|
|
485
|
+
}
|
|
486
|
+
const recordSize = readSafeUint64(view, zip64Offset + 4);
|
|
487
|
+
return recordSize !== void 0 && recordSize >= 44 && zip64Offset + 12 + recordSize === locatorOffset;
|
|
488
|
+
}
|
|
489
|
+
function readSafeUint64(view, offset) {
|
|
490
|
+
const low = view.getUint32(offset, true);
|
|
491
|
+
const high = view.getUint32(offset + 4, true);
|
|
492
|
+
const value = high * 4294967296 + low;
|
|
493
|
+
return Number.isSafeInteger(value) ? value : void 0;
|
|
494
|
+
}
|
|
495
|
+
function assertClassicZip(endRecord, view) {
|
|
496
|
+
const hasZip64Locator = hasStructurallyValidZip64Chain(view, endRecord);
|
|
497
|
+
if (endRecord.entriesOnDisk === 65535 || endRecord.totalEntries === 65535 || endRecord.centralDirectorySize === 4294967295 || endRecord.centralDirectoryOffset === 4294967295 || hasZip64Locator) {
|
|
498
|
+
throw zip64Error();
|
|
499
|
+
}
|
|
500
|
+
if (endRecord.diskNumber !== 0 || endRecord.centralDirectoryDisk !== 0 || endRecord.entriesOnDisk !== endRecord.totalEntries) {
|
|
501
|
+
throw multiDiskError();
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
function validateLocalHeader(data, view, entry) {
|
|
505
|
+
assertRange(
|
|
506
|
+
entry.localHeaderOffset,
|
|
507
|
+
LOCAL_FILE_HEADER_SIZE,
|
|
508
|
+
entry.centralDirectoryOffset,
|
|
509
|
+
"ZIP local file header is missing or truncated."
|
|
510
|
+
);
|
|
511
|
+
if (view.getUint32(entry.localHeaderOffset, true) !== LOCAL_FILE_HEADER_SIGNATURE) {
|
|
512
|
+
throw new InvalidZipArchiveError(
|
|
513
|
+
"ZIP central directory points to an invalid local file header."
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
const localFlags = view.getUint16(entry.localHeaderOffset + 6, true);
|
|
517
|
+
const localCompressionMethod = view.getUint16(
|
|
518
|
+
entry.localHeaderOffset + 8,
|
|
519
|
+
true
|
|
520
|
+
);
|
|
521
|
+
const localCrc32 = view.getUint32(entry.localHeaderOffset + 14, true);
|
|
522
|
+
const localCompressedSize = view.getUint32(
|
|
523
|
+
entry.localHeaderOffset + 18,
|
|
524
|
+
true
|
|
525
|
+
);
|
|
526
|
+
const localUncompressedSize = view.getUint32(
|
|
527
|
+
entry.localHeaderOffset + 22,
|
|
528
|
+
true
|
|
529
|
+
);
|
|
530
|
+
const localNameLength = view.getUint16(entry.localHeaderOffset + 26, true);
|
|
531
|
+
const localExtraLength = view.getUint16(entry.localHeaderOffset + 28, true);
|
|
532
|
+
if (localCompressedSize === 4294967295 || localUncompressedSize === 4294967295) {
|
|
533
|
+
throw zip64Error();
|
|
534
|
+
}
|
|
535
|
+
assertNotEncrypted(localFlags);
|
|
536
|
+
if (localFlags !== entry.centralFlags || localCompressionMethod !== entry.compressionMethod) {
|
|
537
|
+
throw new InvalidZipArchiveError(
|
|
538
|
+
"ZIP local file header does not match its central-directory entry."
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
if ((localFlags & DATA_DESCRIPTOR_FLAG) !== 0) {
|
|
542
|
+
throw new UnsupportedZipFeatureError(
|
|
543
|
+
"ambiguous-metadata",
|
|
544
|
+
`ZIP entry "${entry.rawPath}" uses a data descriptor whose payload boundary cannot be verified without decompression or extractor-specific scanning.`,
|
|
545
|
+
entry.rawPath
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
const localNameStart = entry.localHeaderOffset + LOCAL_FILE_HEADER_SIZE;
|
|
549
|
+
const localNameEnd = localNameStart + localNameLength;
|
|
550
|
+
const localExtraEnd = localNameEnd + localExtraLength;
|
|
551
|
+
assertRange(
|
|
552
|
+
entry.localHeaderOffset,
|
|
553
|
+
localExtraEnd - entry.localHeaderOffset,
|
|
554
|
+
entry.centralDirectoryOffset,
|
|
555
|
+
"ZIP local file header metadata is truncated."
|
|
556
|
+
);
|
|
557
|
+
const centralNameLength = entry.centralNameEnd - entry.centralNameStart;
|
|
558
|
+
if (localNameLength !== centralNameLength || !equalBytes(
|
|
559
|
+
data.subarray(localNameStart, localNameEnd),
|
|
560
|
+
data.subarray(entry.centralNameStart, entry.centralNameEnd)
|
|
561
|
+
)) {
|
|
562
|
+
throw new InvalidZipArchiveError(
|
|
563
|
+
"ZIP local and central-directory entry paths do not match."
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
assertExtraFields(data, localNameEnd, localExtraEnd, "local file header");
|
|
567
|
+
if (localCrc32 !== entry.centralCrc32 || localCompressedSize !== entry.compressedSize || localUncompressedSize !== entry.uncompressedSize) {
|
|
568
|
+
throw new InvalidZipArchiveError(
|
|
569
|
+
"ZIP local file sizes do not match the central-directory entry."
|
|
570
|
+
);
|
|
571
|
+
}
|
|
572
|
+
if (entry.compressedSize > entry.centralDirectoryOffset - localExtraEnd) {
|
|
573
|
+
throw new InvalidZipArchiveError(
|
|
574
|
+
"ZIP entry body is truncated or overlaps the central directory."
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
return localExtraEnd + entry.compressedSize;
|
|
578
|
+
}
|
|
579
|
+
function assertExtraFields(data, start, end, location) {
|
|
580
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
581
|
+
let offset = start;
|
|
582
|
+
while (offset < end) {
|
|
583
|
+
if (end - offset < 4) {
|
|
584
|
+
throw new InvalidZipArchiveError(
|
|
585
|
+
`ZIP ${location} contains a truncated extra field.`
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
const headerId = view.getUint16(offset, true);
|
|
589
|
+
const dataSize = view.getUint16(offset + 2, true);
|
|
590
|
+
const nextOffset = offset + 4 + dataSize;
|
|
591
|
+
if (nextOffset > end) {
|
|
592
|
+
throw new InvalidZipArchiveError(
|
|
593
|
+
`ZIP ${location} contains a truncated extra field.`
|
|
594
|
+
);
|
|
595
|
+
}
|
|
596
|
+
if (headerId === ZIP64_EXTRA_FIELD_ID) {
|
|
597
|
+
throw zip64Error();
|
|
598
|
+
}
|
|
599
|
+
if (headerId === STRONG_ENCRYPTION_EXTRA_FIELD_ID || headerId === WINZIP_AES_EXTRA_FIELD_ID) {
|
|
600
|
+
throw encryptionError();
|
|
601
|
+
}
|
|
602
|
+
if (headerId === PKWARE_EXTENDED_LANGUAGE_ENCODING_EXTRA_FIELD_ID || headerId === XCEED_UNICODE_PATH_EXTRA_FIELD_ID || headerId === UNICODE_PATH_EXTRA_FIELD_ID) {
|
|
603
|
+
throw new UnsupportedZipFeatureError(
|
|
604
|
+
"ambiguous-metadata",
|
|
605
|
+
"ZIP alternate path-encoding extra fields are not supported because entry names must have one unambiguous representation."
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
if (headerId === PKWARE_UNIX_EXTRA_FIELD_ID || headerId === ASI_UNIX_EXTRA_FIELD_ID || headerId === LIBARCHIVE_EXTRA_FIELD_ID) {
|
|
609
|
+
throw new UnsupportedZipFeatureError(
|
|
610
|
+
"ambiguous-metadata",
|
|
611
|
+
"ZIP Unix link and file-type extra fields are not supported because entry type must have one unambiguous representation."
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
offset = nextOffset;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
function assertNotEncrypted(flags) {
|
|
618
|
+
if ((flags & (ENCRYPTED_FLAG | STRONG_ENCRYPTION_FLAG | MASKED_LOCAL_HEADER_FLAG)) !== 0) {
|
|
619
|
+
throw encryptionError();
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
function assertNotAesCompression(compressionMethod) {
|
|
623
|
+
if (compressionMethod === WINZIP_AES_COMPRESSION_METHOD) {
|
|
624
|
+
throw encryptionError();
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
function decodeEntryPath(bytes) {
|
|
628
|
+
let path2;
|
|
629
|
+
try {
|
|
630
|
+
path2 = utf8Decoder.decode(bytes);
|
|
631
|
+
} catch {
|
|
632
|
+
throw new InvalidZipArchiveError(
|
|
633
|
+
"ZIP entry path is not valid UTF-8. Legacy non-UTF-8 names are not supported."
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
if (path2.startsWith("\uFEFF")) {
|
|
637
|
+
throw new UnsupportedZipFeatureError(
|
|
638
|
+
"ambiguous-metadata",
|
|
639
|
+
"ZIP entry path starts with a UTF-8 BOM, which filename decoders interpret inconsistently.",
|
|
640
|
+
path2
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
return path2;
|
|
644
|
+
}
|
|
645
|
+
function normalizeEntryPath(rawPath) {
|
|
646
|
+
if (rawPath.length === 0) {
|
|
647
|
+
throw new UnsafeZipEntryError(
|
|
648
|
+
"empty-path",
|
|
649
|
+
rawPath,
|
|
650
|
+
"ZIP entry path is empty."
|
|
651
|
+
);
|
|
652
|
+
}
|
|
653
|
+
if (rawPath.includes("\0")) {
|
|
654
|
+
throw new UnsafeZipEntryError(
|
|
655
|
+
"nul-byte",
|
|
656
|
+
rawPath,
|
|
657
|
+
"ZIP entry path contains a NUL byte."
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
const unifiedPath = rawPath.replace(/\\/g, "/");
|
|
661
|
+
if (/^[a-zA-Z]:/.test(unifiedPath)) {
|
|
662
|
+
throw new UnsafeZipEntryError(
|
|
663
|
+
"drive-qualified-path",
|
|
664
|
+
rawPath,
|
|
665
|
+
`ZIP entry "${rawPath}" uses a drive-qualified path.`
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
if (unifiedPath.includes(":")) {
|
|
669
|
+
throw new UnsafeZipEntryError(
|
|
670
|
+
"alternate-data-stream",
|
|
671
|
+
rawPath,
|
|
672
|
+
`ZIP entry "${rawPath}" contains a colon that can select an NTFS alternate data stream.`
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
if (unifiedPath.startsWith("/")) {
|
|
676
|
+
throw new UnsafeZipEntryError(
|
|
677
|
+
"absolute-path",
|
|
678
|
+
rawPath,
|
|
679
|
+
`ZIP entry "${rawPath}" uses an absolute path.`
|
|
680
|
+
);
|
|
681
|
+
}
|
|
682
|
+
const segments = unifiedPath.split("/");
|
|
683
|
+
if (segments.includes("..")) {
|
|
684
|
+
throw new UnsafeZipEntryError(
|
|
685
|
+
"path-traversal",
|
|
686
|
+
rawPath,
|
|
687
|
+
`ZIP entry "${rawPath}" contains a parent-directory traversal segment.`
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
for (const segment of segments) {
|
|
691
|
+
if (segment === "" || segment === ".") {
|
|
692
|
+
continue;
|
|
693
|
+
}
|
|
694
|
+
if (/[. ]$/u.test(segment)) {
|
|
695
|
+
throw new UnsafeZipEntryError(
|
|
696
|
+
"windows-reserved-path",
|
|
697
|
+
rawPath,
|
|
698
|
+
`ZIP entry "${rawPath}" contains a path segment ending in a dot or space, which is not portable to Windows.`
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
const basename2 = segment.split(".", 1)[0].replace(/[. ]+$/u, "");
|
|
702
|
+
if (WINDOWS_RESERVED_DEVICE_BASENAME.test(basename2)) {
|
|
703
|
+
throw new UnsafeZipEntryError(
|
|
704
|
+
"windows-reserved-path",
|
|
705
|
+
rawPath,
|
|
706
|
+
`ZIP entry "${rawPath}" contains the Windows-reserved device name "${segment}".`
|
|
707
|
+
);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
const normalizedPath = segments.filter((segment) => segment !== "" && segment !== ".").join("/");
|
|
711
|
+
if (normalizedPath.length === 0) {
|
|
712
|
+
throw new UnsafeZipEntryError(
|
|
713
|
+
"empty-path",
|
|
714
|
+
rawPath,
|
|
715
|
+
`ZIP entry "${rawPath}" normalizes to an empty path.`
|
|
716
|
+
);
|
|
717
|
+
}
|
|
718
|
+
return normalizedPath;
|
|
719
|
+
}
|
|
720
|
+
function portablePathKey(path2) {
|
|
721
|
+
return path2.normalize("NFC").toUpperCase().toLowerCase().normalize("NFC");
|
|
722
|
+
}
|
|
723
|
+
function assertRange(offset, length, boundary, message) {
|
|
724
|
+
if (offset < 0 || length < 0 || offset > boundary || length > boundary - offset) {
|
|
725
|
+
throw new InvalidZipArchiveError(message);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
function equalBytes(left, right) {
|
|
729
|
+
if (left.length !== right.length) {
|
|
730
|
+
return false;
|
|
731
|
+
}
|
|
732
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
733
|
+
if (left[index] !== right[index]) {
|
|
734
|
+
return false;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
return true;
|
|
738
|
+
}
|
|
739
|
+
function zip64Error() {
|
|
740
|
+
return new UnsupportedZipFeatureError(
|
|
741
|
+
"zip64",
|
|
742
|
+
"ZIP64 archives are not supported; use a classic ZIP archive within the configured limits."
|
|
743
|
+
);
|
|
744
|
+
}
|
|
745
|
+
function multiDiskError() {
|
|
746
|
+
return new UnsupportedZipFeatureError(
|
|
747
|
+
"multi-disk",
|
|
748
|
+
"Multi-disk ZIP archives are not supported."
|
|
749
|
+
);
|
|
750
|
+
}
|
|
751
|
+
function encryptionError() {
|
|
752
|
+
return new UnsupportedZipFeatureError(
|
|
753
|
+
"encryption",
|
|
754
|
+
"Encrypted ZIP entries are not supported because their metadata cannot be safely inspected without decryption."
|
|
755
|
+
);
|
|
756
|
+
}
|
|
11
757
|
function createTempFilePath(filepath) {
|
|
12
758
|
return join(
|
|
13
759
|
dirname(filepath),
|
|
@@ -2680,16 +3426,22 @@ Promise.resolve().then(() => factory).then(({ initializeProviders: initializePro
|
|
|
2680
3426
|
});
|
|
2681
3427
|
const PACKAGE_VERSION_INITIALIZED = true;
|
|
2682
3428
|
export {
|
|
3429
|
+
DEFAULT_ZIP_MANIFEST_LIMITS,
|
|
2683
3430
|
DirectoryNotEmptyError,
|
|
2684
3431
|
FileNotFoundError,
|
|
2685
3432
|
FilesystemAdapter,
|
|
2686
3433
|
FilesystemError,
|
|
2687
3434
|
GoogleDriveProvider,
|
|
2688
3435
|
InvalidPathError,
|
|
3436
|
+
InvalidZipArchiveError,
|
|
2689
3437
|
LocalFilesystemProvider,
|
|
2690
3438
|
PACKAGE_VERSION_INITIALIZED,
|
|
2691
3439
|
PermissionError,
|
|
2692
3440
|
S3FilesystemProvider,
|
|
3441
|
+
UnsafeZipEntryError,
|
|
3442
|
+
UnsupportedZipFeatureError,
|
|
3443
|
+
ZipManifestError,
|
|
3444
|
+
ZipManifestLimitError,
|
|
2693
3445
|
addRateLimit,
|
|
2694
3446
|
factory as default,
|
|
2695
3447
|
download,
|
|
@@ -2706,6 +3458,7 @@ export {
|
|
|
2706
3458
|
getProviderInfo,
|
|
2707
3459
|
getRateLimit,
|
|
2708
3460
|
initializeProviders,
|
|
3461
|
+
inspectZipManifest,
|
|
2709
3462
|
isDirectory,
|
|
2710
3463
|
isFile,
|
|
2711
3464
|
isProviderAvailable,
|