@zip.js/zip.js 2.19.0 → 2.21.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/BENCHMARKS.md +154 -130
- package/deno.json +1 -1
- package/dist/zip-core-external.js +440 -225
- package/dist/zip-core-external.min.js +1 -1
- package/dist/zip-core.js +441 -224
- package/dist/zip-core.min.js +1 -1
- package/dist/zip-fs-core-external.js +440 -225
- package/dist/zip-fs-core-external.min.js +1 -1
- package/dist/zip-fs-core.js +441 -224
- package/dist/zip-fs-core.min.js +1 -1
- package/dist/zip-fs-external.js +440 -225
- package/dist/zip-fs-external.min.js +1 -1
- package/dist/zip-fs-native.js +441 -224
- package/dist/zip-fs-native.min.js +1 -1
- package/dist/zip-fs.js +441 -224
- package/dist/zip-fs.min.js +1 -1
- package/dist/zip-legacy.js +441 -224
- package/dist/zip-legacy.min.js +1 -1
- package/dist/zip-native.js +441 -224
- package/dist/zip-native.min.js +1 -1
- package/dist/zip.js +441 -224
- package/dist/zip.min.js +1 -1
- package/index-native.cjs +441 -224
- package/index-native.min.js +1 -1
- package/index.cjs +441 -224
- package/index.d.cts +117 -24
- package/index.d.ts +117 -24
- package/index.min.js +1 -1
- package/lib/core/constants.js +7 -0
- package/lib/core/version.js +1 -1
- package/lib/core/zip-reader.js +225 -76
- package/lib/core/zip-writer.js +214 -147
- package/lib/zip-core-reader.js +2 -0
- package/package.json +1 -1
package/lib/core/constants.js
CHANGED
|
@@ -117,6 +117,10 @@ const MIN_DATE = new Date(1980, 0, 1);
|
|
|
117
117
|
|
|
118
118
|
const UNDEFINED_VALUE = undefined;
|
|
119
119
|
const INFINITY_VALUE = Infinity;
|
|
120
|
+
const ERR_LOCAL_FILE_HEADER_NOT_FOUND = "Local file header not found";
|
|
121
|
+
const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
|
|
122
|
+
const ERR_EXTRAFIELD_ZIP64_NOT_FOUND = "Zip64 extra field not found";
|
|
123
|
+
|
|
120
124
|
const UNDEFINED_TYPE = "undefined";
|
|
121
125
|
const FUNCTION_TYPE = "function";
|
|
122
126
|
const OBJECT_TYPE = "object";
|
|
@@ -211,6 +215,9 @@ export {
|
|
|
211
215
|
MAX_DATE,
|
|
212
216
|
UNDEFINED_VALUE,
|
|
213
217
|
INFINITY_VALUE,
|
|
218
|
+
ERR_LOCAL_FILE_HEADER_NOT_FOUND,
|
|
219
|
+
ERR_OVERLAPPING_ENTRY,
|
|
220
|
+
ERR_EXTRAFIELD_ZIP64_NOT_FOUND,
|
|
214
221
|
UNDEFINED_TYPE,
|
|
215
222
|
FUNCTION_TYPE,
|
|
216
223
|
OBJECT_TYPE,
|
package/lib/core/version.js
CHANGED
package/lib/core/zip-reader.js
CHANGED
|
@@ -68,6 +68,9 @@ import {
|
|
|
68
68
|
ZIP64_END_OF_CENTRAL_DIR_LENGTH,
|
|
69
69
|
BITFLAG_ENCRYPTED,
|
|
70
70
|
BITFLAG_STRONG_ENCRYPTION,
|
|
71
|
+
ERR_LOCAL_FILE_HEADER_NOT_FOUND,
|
|
72
|
+
ERR_OVERLAPPING_ENTRY,
|
|
73
|
+
ERR_EXTRAFIELD_ZIP64_NOT_FOUND,
|
|
71
74
|
BITFLAG_COMPRESSED_PATCHED_DATA,
|
|
72
75
|
BITFLAG_MASKED_LOCAL_HEADERS,
|
|
73
76
|
BITFLAG_LEVEL,
|
|
@@ -188,12 +191,9 @@ const ERR_BAD_FORMAT = "File format is not recognized";
|
|
|
188
191
|
const ERR_EOCDR_NOT_FOUND = "End of central directory not found";
|
|
189
192
|
const ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND = "End of Zip64 central directory locator not found";
|
|
190
193
|
const ERR_CENTRAL_DIRECTORY_NOT_FOUND = "Central directory header not found";
|
|
191
|
-
const ERR_LOCAL_FILE_HEADER_NOT_FOUND = "Local file header not found";
|
|
192
|
-
const ERR_EXTRAFIELD_ZIP64_NOT_FOUND = "Zip64 extra field not found";
|
|
193
194
|
const ERR_ENCRYPTED = "File contains encrypted entry";
|
|
194
195
|
const ERR_UNSUPPORTED_ENCRYPTION = "Encryption method not supported";
|
|
195
196
|
const ERR_SPLIT_ZIP_FILE = "Split zip file";
|
|
196
|
-
const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
|
|
197
197
|
const ERR_ENTRY_DATA_OUT_OF_BOUNDS = "Entry data out of bounds";
|
|
198
198
|
const ERR_AMBIGUOUS_ARCHIVE = "Ambiguous archive";
|
|
199
199
|
const ERR_ENCRYPTED_CENTRAL_DIRECTORY = "Encrypted central directory is not supported";
|
|
@@ -212,6 +212,8 @@ const WARNING_APPENDED_DATA = "appended data";
|
|
|
212
212
|
const WARNING_PREPENDED_DATA = "prepended data";
|
|
213
213
|
const WARNING_PREPENDED_CENTRAL_DIRECTORY = "prepended central directory";
|
|
214
214
|
const WARNING_TRAILING_CENTRAL_DIRECTORY_DATA = "trailing central directory data";
|
|
215
|
+
const WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET = "mismatched central directory offset";
|
|
216
|
+
const WARNING_MISSING_ZIP64_EXTRA_FIELD = "missing zip64 extra field";
|
|
215
217
|
const WARNING_DUPLICATE_FILENAME = "duplicate filename";
|
|
216
218
|
const WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY = "mismatched zip64 end of central directory record";
|
|
217
219
|
const WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY = "multiple end of central directory records";
|
|
@@ -256,7 +258,8 @@ class ZipReader {
|
|
|
256
258
|
Object.assign(this, {
|
|
257
259
|
reader: new GenericReader(reader),
|
|
258
260
|
options,
|
|
259
|
-
readRanges: { indexes: new Set(), sortedRanges: [], pendingRanges: [] }
|
|
261
|
+
readRanges: { indexes: new Set(), sortedRanges: [], pendingRanges: [] },
|
|
262
|
+
entriesMissingZip64ExtraField: new WeakSet()
|
|
260
263
|
});
|
|
261
264
|
}
|
|
262
265
|
|
|
@@ -327,16 +330,22 @@ class ZipReader {
|
|
|
327
330
|
directoryDataOffset = getDiskOffset(reader, getUint32(endOfDirectoryLocatorView, 4)) + getBigUint64(endOfDirectoryLocatorView, 8);
|
|
328
331
|
let endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
|
|
329
332
|
let endOfDirectoryView = getDataView(endOfDirectoryArray);
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
333
|
+
if (endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
|
|
334
|
+
const locatorOffset = endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH;
|
|
335
|
+
let expectedDirectoryDataOffset = locatorOffset - ZIP64_END_OF_CENTRAL_DIR_LENGTH;
|
|
336
|
+
if (!await startsWithSignature(reader, expectedDirectoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_SIGNATURE)) {
|
|
337
|
+
expectedDirectoryDataOffset = await findZip64EndOfDirectoryOffset(reader, locatorOffset);
|
|
338
|
+
}
|
|
339
|
+
if (expectedDirectoryDataOffset !== UNDEFINED_VALUE && expectedDirectoryDataOffset >= 0 &&
|
|
340
|
+
directoryDataOffset != expectedDirectoryDataOffset) {
|
|
341
|
+
const originalDirectoryDataOffset = directoryDataOffset;
|
|
342
|
+
directoryDataOffset = expectedDirectoryDataOffset;
|
|
343
|
+
if (directoryDataOffset > originalDirectoryDataOffset) {
|
|
344
|
+
prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
|
|
345
|
+
}
|
|
346
|
+
endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
|
|
347
|
+
endOfDirectoryView = getDataView(endOfDirectoryArray);
|
|
337
348
|
}
|
|
338
|
-
endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
|
|
339
|
-
endOfDirectoryView = getDataView(endOfDirectoryArray);
|
|
340
349
|
}
|
|
341
350
|
if (endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
|
|
342
351
|
throw new Error(ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND);
|
|
@@ -374,15 +383,13 @@ class ZipReader {
|
|
|
374
383
|
reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY);
|
|
375
384
|
}
|
|
376
385
|
directoryDataOffset = getDiskOffset(reader, diskNumber) + getBigUint64(endOfDirectoryView, 48) + prependedDataLength;
|
|
386
|
+
} else if (directoryDataOffset == MAX_32_BITS || directoryDataLength == MAX_32_BITS || diskNumber == MAX_16_BITS) {
|
|
387
|
+
throw new Error(ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND);
|
|
377
388
|
}
|
|
378
389
|
}
|
|
379
390
|
let declaredDirectoryDataLength = directoryDataLength;
|
|
380
391
|
const centralDirectoryEndOffset = endOfDirectoryInfo.offset -
|
|
381
392
|
(zip64EndOfDirectory ? zip64EndOfDirectoryLength + ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH : 0);
|
|
382
|
-
if (directoryDataOffset >= reader.size) {
|
|
383
|
-
prependedDataLength = reader.size - directoryDataOffset - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
|
|
384
|
-
directoryDataOffset = reader.size - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
|
|
385
|
-
}
|
|
386
393
|
if (expectedLastDiskNumber != lastDiskNumber) {
|
|
387
394
|
throw new Error(ERR_SPLIT_ZIP_FILE);
|
|
388
395
|
}
|
|
@@ -390,33 +397,64 @@ class ZipReader {
|
|
|
390
397
|
throw new Error(ERR_BAD_FORMAT);
|
|
391
398
|
}
|
|
392
399
|
let offset = 0;
|
|
393
|
-
let directoryArray =
|
|
400
|
+
let directoryArray = directoryDataOffset < reader.size ?
|
|
401
|
+
await readUint8Array(reader, directoryDataOffset, directoryDataLength) :
|
|
402
|
+
EMPTY_UINT8_ARRAY;
|
|
394
403
|
let directoryView = getDataView(directoryArray);
|
|
395
404
|
if (directoryDataLength) {
|
|
396
|
-
|
|
397
|
-
throw new Error(ERR_BAD_FORMAT);
|
|
398
|
-
}
|
|
399
|
-
const expectedDirectoryDataOffset = centralDirectoryEndOffset - directoryDataLength;
|
|
405
|
+
let expectedDirectoryDataOffset = centralDirectoryEndOffset - directoryDataLength;
|
|
400
406
|
if (directoryDataOffset != expectedDirectoryDataOffset && diskNumber == lastDiskNumber) {
|
|
401
|
-
const storedPointsAtDirectory =
|
|
407
|
+
const storedPointsAtDirectory = directoryArray.length >= 4 && (
|
|
408
|
+
getUint32(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
|
|
402
409
|
Boolean(directoryEncryptionInfo && directoryEncryptionInfo.compressedSize) ||
|
|
403
|
-
detectEncryptedCentralDirectory(directoryView);
|
|
410
|
+
detectEncryptedCentralDirectory(directoryView));
|
|
411
|
+
if (!storedPointsAtDirectory && expectedDirectoryDataOffset < 0) {
|
|
412
|
+
throw new Error(ERR_BAD_FORMAT);
|
|
413
|
+
}
|
|
404
414
|
let reconcile = !storedPointsAtDirectory;
|
|
405
|
-
if (
|
|
406
|
-
|
|
407
|
-
|
|
415
|
+
if (expectedDirectoryDataOffset >= 0 && expectedDirectoryDataOffset + 4 <= reader.size) {
|
|
416
|
+
let expectedPointsAtDirectory = await pointsAtDirectory(reader, expectedDirectoryDataOffset);
|
|
417
|
+
if (!expectedPointsAtDirectory) {
|
|
418
|
+
const signatureRecordOffset = await findDigitalSignatureRecordOffset(reader, centralDirectoryEndOffset);
|
|
419
|
+
const signedDirectoryDataOffset = signatureRecordOffset - directoryDataLength;
|
|
420
|
+
if (signatureRecordOffset !== UNDEFINED_VALUE && signedDirectoryDataOffset >= 0 &&
|
|
421
|
+
await pointsAtDirectory(reader, signedDirectoryDataOffset)) {
|
|
422
|
+
expectedDirectoryDataOffset = signedDirectoryDataOffset;
|
|
423
|
+
expectedPointsAtDirectory = true;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
if (storedPointsAtDirectory) {
|
|
427
|
+
reconcile = expectedPointsAtDirectory;
|
|
428
|
+
}
|
|
408
429
|
}
|
|
409
|
-
if (reconcile) {
|
|
430
|
+
if (reconcile && expectedDirectoryDataOffset != directoryDataOffset) {
|
|
410
431
|
const originalDirectoryDataOffset = directoryDataOffset;
|
|
411
432
|
directoryDataOffset = expectedDirectoryDataOffset;
|
|
412
|
-
if (directoryDataOffset > originalDirectoryDataOffset) {
|
|
413
|
-
prependedDataLength += directoryDataOffset - originalDirectoryDataOffset;
|
|
414
|
-
prependedCentralDirectory = storedPointsAtDirectory;
|
|
415
|
-
}
|
|
416
433
|
directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
|
|
417
434
|
directoryView = getDataView(directoryArray);
|
|
435
|
+
const offsetDelta = directoryDataOffset - originalDirectoryDataOffset;
|
|
436
|
+
const localHeaderOffset = getFirstLocalHeaderOffset(directoryArray, directoryView);
|
|
437
|
+
const localHeaderFound = localHeaderOffset !== UNDEFINED_VALUE &&
|
|
438
|
+
await startsWithSignature(reader, localHeaderOffset, LOCAL_FILE_HEADER_SIGNATURE);
|
|
439
|
+
const shiftedLocalHeaderFound = localHeaderOffset !== UNDEFINED_VALUE &&
|
|
440
|
+
await startsWithSignature(reader, localHeaderOffset + offsetDelta, LOCAL_FILE_HEADER_SIGNATURE);
|
|
441
|
+
if (offsetDelta > 0 && !(localHeaderFound && !shiftedLocalHeaderFound)) {
|
|
442
|
+
prependedDataLength += offsetDelta;
|
|
443
|
+
prependedCentralDirectory = storedPointsAtDirectory;
|
|
444
|
+
} else {
|
|
445
|
+
reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET);
|
|
446
|
+
if (offsetDelta < 0 && !localHeaderFound && shiftedLocalHeaderFound) {
|
|
447
|
+
prependedDataLength += offsetDelta;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
418
450
|
}
|
|
419
451
|
}
|
|
452
|
+
if (directoryArray.length < 4) {
|
|
453
|
+
throw new Error(ERR_BAD_FORMAT);
|
|
454
|
+
}
|
|
455
|
+
} else if (!filesLength && directoryDataOffset < centralDirectoryEndOffset && diskNumber == lastDiskNumber) {
|
|
456
|
+
prependedDataLength += centralDirectoryEndOffset - directoryDataOffset;
|
|
457
|
+
directoryDataOffset = centralDirectoryEndOffset;
|
|
420
458
|
}
|
|
421
459
|
const expectedDirectoryDataLength = centralDirectoryEndOffset - directoryDataOffset;
|
|
422
460
|
if (directoryDataLength != expectedDirectoryDataLength && expectedDirectoryDataLength >= 0 && diskNumber == lastDiskNumber) {
|
|
@@ -458,8 +496,10 @@ class ZipReader {
|
|
|
458
496
|
addWarning(warnings, WARNING_WRAPPED_ENTRIES_COUNT);
|
|
459
497
|
}
|
|
460
498
|
}
|
|
499
|
+
const entryOptions = Object.assign({}, zipReader.options, options, { [OPTION_STRICTNESS]: strictness });
|
|
500
|
+
delete entryOptions[OPTION_CHECK_AMBIGUITY];
|
|
461
501
|
for (let indexFile = 0; indexFile < filesLength; indexFile++) {
|
|
462
|
-
const fileEntry = new ZipEntry(reader,
|
|
502
|
+
const fileEntry = new ZipEntry(reader, entryOptions);
|
|
463
503
|
if (offset + CENTRAL_FILE_HEADER_LENGTH > directoryArray.length || getUint32(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE) {
|
|
464
504
|
if (indexFile == 0 && !decryptedDirectory && (zip64EndOfDirectoryVersion2 || detectEncryptedCentralDirectory(directoryView))) {
|
|
465
505
|
throw new Error(ERR_ENCRYPTED_CENTRAL_DIRECTORY);
|
|
@@ -525,7 +565,16 @@ class ZipReader {
|
|
|
525
565
|
filename,
|
|
526
566
|
comment
|
|
527
567
|
});
|
|
528
|
-
|
|
568
|
+
let malformedExtraField;
|
|
569
|
+
try {
|
|
570
|
+
malformedExtraField = readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6);
|
|
571
|
+
} catch (error) {
|
|
572
|
+
if (checkAmbiguity || error.message != ERR_EXTRAFIELD_ZIP64_NOT_FOUND) {
|
|
573
|
+
throw error;
|
|
574
|
+
}
|
|
575
|
+
fileEntry.zip64ExtraFieldMissing = true;
|
|
576
|
+
addWarning(warnings, WARNING_MISSING_ZIP64_EXTRA_FIELD, fileEntry.filename);
|
|
577
|
+
}
|
|
529
578
|
filename = fileEntry.filename;
|
|
530
579
|
if (normalizeFilename) {
|
|
531
580
|
const normalizedFilename = normalizeFilename(filename);
|
|
@@ -541,13 +590,17 @@ class ZipReader {
|
|
|
541
590
|
if (malformedExtraField) {
|
|
542
591
|
addWarning(warnings, WARNING_MALFORMED_EXTRA_FIELD, filename);
|
|
543
592
|
}
|
|
544
|
-
fileEntry.offset
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
593
|
+
if (fileEntry.zip64ExtraFieldMissing && fileEntry.offset == MAX_32_BITS) {
|
|
594
|
+
startOffset = 0;
|
|
595
|
+
} else {
|
|
596
|
+
fileEntry.offset += prependedDataLength;
|
|
597
|
+
const entryPosition = getDiskOffset(reader, fileEntry.diskNumberStart) + fileEntry.offset;
|
|
598
|
+
startOffset = Math.min(entryPosition, startOffset);
|
|
599
|
+
if (entryPosition < previousEntryPosition) {
|
|
600
|
+
addWarning(warnings, WARNING_UNSORTED_CENTRAL_DIRECTORY, filename);
|
|
601
|
+
}
|
|
602
|
+
previousEntryPosition = entryPosition;
|
|
549
603
|
}
|
|
550
|
-
previousEntryPosition = entryPosition;
|
|
551
604
|
if ((fileEntry.version & MAX_8_BITS) > MAX_KNOWN_VERSION) {
|
|
552
605
|
addWarning(warnings, WARNING_UNKNOWN_VERSION, filename);
|
|
553
606
|
}
|
|
@@ -567,11 +620,12 @@ class ZipReader {
|
|
|
567
620
|
const sticky = Boolean(fileEntry.unixMode & FILE_ATTR_UNIX_STICKY_MASK);
|
|
568
621
|
const unixType = fileEntry.unixMode === UNDEFINED_VALUE ? unixExternalUpper : fileEntry.unixMode;
|
|
569
622
|
const symlink = (unixType & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_SYMLINK;
|
|
570
|
-
const executable = !symlink && ((fileEntry.unixMode !== UNDEFINED_VALUE)
|
|
571
|
-
? ((fileEntry.unixMode & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)
|
|
572
|
-
: (unixCompatible && ((unixExternalUpper & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)));
|
|
573
623
|
const modeIsDir = fileEntry.unixMode !== UNDEFINED_VALUE && ((fileEntry.unixMode & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_DIR);
|
|
574
624
|
const upperIsDir = ((unixExternalUpper & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_DIR);
|
|
625
|
+
const directory = modeIsDir || upperIsDir || (msDosCompatible && msdosAttributes.directory) || fileEntry.filename.endsWith(DIRECTORY_SIGNATURE);
|
|
626
|
+
const executable = !symlink && !directory && ((fileEntry.unixMode !== UNDEFINED_VALUE)
|
|
627
|
+
? ((fileEntry.unixMode & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)
|
|
628
|
+
: (unixCompatible && ((unixExternalUpper & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)));
|
|
575
629
|
Object.assign(fileEntry, {
|
|
576
630
|
setuid,
|
|
577
631
|
setgid,
|
|
@@ -579,16 +633,19 @@ class ZipReader {
|
|
|
579
633
|
symlink,
|
|
580
634
|
unixExternalUpper,
|
|
581
635
|
executable,
|
|
582
|
-
directory
|
|
636
|
+
directory,
|
|
583
637
|
zipCrypto: fileEntry.encrypted && !fileEntry.extraFieldAES
|
|
584
638
|
});
|
|
585
639
|
const entry = new Entry(fileEntry);
|
|
586
|
-
|
|
640
|
+
if (fileEntry.zip64ExtraFieldMissing) {
|
|
641
|
+
zipReader.entriesMissingZip64ExtraField.add(entry);
|
|
642
|
+
}
|
|
643
|
+
entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader, options);
|
|
587
644
|
entry.arrayBuffer = async options => {
|
|
588
645
|
const writer = new TransformStream();
|
|
589
646
|
const arrayBufferPromise = streamToBlob(writer.readable).then(blob => blob.arrayBuffer());
|
|
590
647
|
arrayBufferPromise.catch(() => { });
|
|
591
|
-
await fileEntry.getData(writer, entry, zipReader
|
|
648
|
+
await fileEntry.getData(writer, entry, zipReader,
|
|
592
649
|
Object.assign({}, options, { preventClose: false }));
|
|
593
650
|
return arrayBufferPromise;
|
|
594
651
|
};
|
|
@@ -635,10 +692,10 @@ class ZipReader {
|
|
|
635
692
|
const extractAppendedData = getOptionValue(zipReader, options, OPTION_EXTRACT_APPENDED_DATA);
|
|
636
693
|
const splitZipSignatureLength = (checkAmbiguity || extractPrependedData) && filesLength &&
|
|
637
694
|
startOffset == SPLIT_ZIP_FILE_SIGNATURE_LENGTH && await startsWithSplitZipMarker(reader) ? SPLIT_ZIP_FILE_SIGNATURE_LENGTH : 0;
|
|
638
|
-
if (checkAmbiguity && (prependedDataLength || (filesLength && startOffset > splitZipSignatureLength))) {
|
|
695
|
+
if (checkAmbiguity && (prependedDataLength > 0 || (filesLength && startOffset > splitZipSignatureLength))) {
|
|
639
696
|
throwAmbiguousArchive(WARNING_PREPENDED_DATA);
|
|
640
697
|
}
|
|
641
|
-
if (prependedDataLength || (filesLength && startOffset > SPLIT_ZIP_FILE_SIGNATURE_LENGTH)) {
|
|
698
|
+
if (prependedDataLength > 0 || (filesLength && startOffset > SPLIT_ZIP_FILE_SIGNATURE_LENGTH)) {
|
|
642
699
|
addWarning(warnings, WARNING_PREPENDED_DATA);
|
|
643
700
|
}
|
|
644
701
|
if (prependedCentralDirectory) {
|
|
@@ -791,6 +848,7 @@ export {
|
|
|
791
848
|
ZipReader,
|
|
792
849
|
isZipFile,
|
|
793
850
|
ZipReaderStream,
|
|
851
|
+
getEntryDataDescriptorLength,
|
|
794
852
|
ERR_BAD_FORMAT,
|
|
795
853
|
ERR_EOCDR_NOT_FOUND,
|
|
796
854
|
ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND,
|
|
@@ -827,6 +885,8 @@ export {
|
|
|
827
885
|
WARNING_PREPENDED_DATA,
|
|
828
886
|
WARNING_PREPENDED_CENTRAL_DIRECTORY,
|
|
829
887
|
WARNING_TRAILING_CENTRAL_DIRECTORY_DATA,
|
|
888
|
+
WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET,
|
|
889
|
+
WARNING_MISSING_ZIP64_EXTRA_FIELD,
|
|
830
890
|
WARNING_DUPLICATE_FILENAME,
|
|
831
891
|
WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY,
|
|
832
892
|
WARNING_MULTIPLE_END_OF_CENTRAL_DIRECTORY,
|
|
@@ -845,8 +905,12 @@ class ZipEntry {
|
|
|
845
905
|
});
|
|
846
906
|
}
|
|
847
907
|
|
|
848
|
-
async getData(writer, fileEntry,
|
|
908
|
+
async getData(writer, fileEntry, zipReader, options = {}) {
|
|
849
909
|
const zipEntry = this;
|
|
910
|
+
const { readRanges, directoryOffset } = zipReader;
|
|
911
|
+
if (zipEntry.zip64ExtraFieldMissing) {
|
|
912
|
+
throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
|
|
913
|
+
}
|
|
850
914
|
const config = getConfiguration();
|
|
851
915
|
const {
|
|
852
916
|
reader,
|
|
@@ -917,10 +981,10 @@ class ZipEntry {
|
|
|
917
981
|
}
|
|
918
982
|
validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
|
|
919
983
|
const { lastAccessDate, creationDate, uid, gid } = localDirectory;
|
|
920
|
-
if (lastAccessDate) {
|
|
984
|
+
if (lastAccessDate && fileEntry.lastAccessDate === UNDEFINED_VALUE) {
|
|
921
985
|
fileEntry.lastAccessDate = lastAccessDate;
|
|
922
986
|
}
|
|
923
|
-
if (creationDate) {
|
|
987
|
+
if (creationDate && fileEntry.creationDate === UNDEFINED_VALUE) {
|
|
924
988
|
fileEntry.creationDate = creationDate;
|
|
925
989
|
}
|
|
926
990
|
if (uid !== UNDEFINED_VALUE && fileEntry.uid === UNDEFINED_VALUE) {
|
|
@@ -949,7 +1013,7 @@ class ZipEntry {
|
|
|
949
1013
|
throw new Error(ERR_ENCRYPTED);
|
|
950
1014
|
}
|
|
951
1015
|
}
|
|
952
|
-
if (dataOffset + compressedSize > reader.size) {
|
|
1016
|
+
if (dataOffset + compressedSize > Math.min(reader.size, directoryOffset)) {
|
|
953
1017
|
throw new Error(ERR_ENTRY_DATA_OUT_OF_BOUNDS);
|
|
954
1018
|
}
|
|
955
1019
|
const size = compressedSize;
|
|
@@ -1015,6 +1079,7 @@ class ZipEntry {
|
|
|
1015
1079
|
extraFieldZip64: extraFieldZip64 || localDirectory.extraFieldZip64,
|
|
1016
1080
|
readRanges
|
|
1017
1081
|
});
|
|
1082
|
+
validateDataDescriptor(zipEntry, localDirectory, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
|
|
1018
1083
|
}
|
|
1019
1084
|
let writable, abortError, aborted;
|
|
1020
1085
|
try {
|
|
@@ -1034,10 +1099,7 @@ class ZipEntry {
|
|
|
1034
1099
|
writer.size += writtenSize;
|
|
1035
1100
|
}
|
|
1036
1101
|
} catch (error) {
|
|
1037
|
-
|
|
1038
|
-
if (failedOutputSize !== UNDEFINED_VALUE) {
|
|
1039
|
-
writer.size += failedOutputSize;
|
|
1040
|
-
} else if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
|
|
1102
|
+
if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
|
|
1041
1103
|
writer.size += error.outputSize;
|
|
1042
1104
|
}
|
|
1043
1105
|
if (!checkPasswordOnly || !isErrorObject(error) || error.message != ERR_ABORT_CHECK_PASSWORD) {
|
|
@@ -1065,13 +1127,7 @@ class ZipEntry {
|
|
|
1065
1127
|
}
|
|
1066
1128
|
|
|
1067
1129
|
function detectEncryptedCentralDirectory(directoryView) {
|
|
1068
|
-
|
|
1069
|
-
for (let offset = 0; offset < maxOffset; offset++) {
|
|
1070
|
-
if (getUint32(directoryView, offset) == ARCHIVE_EXTRA_DATA_SIGNATURE) {
|
|
1071
|
-
return true;
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
|
-
return false;
|
|
1130
|
+
return directoryView.byteLength >= 4 && getUint32(directoryView, 0) == ARCHIVE_EXTRA_DATA_SIGNATURE;
|
|
1075
1131
|
}
|
|
1076
1132
|
|
|
1077
1133
|
function getWrappedFilesLength(directoryView, directoryArray, offset) {
|
|
@@ -1084,6 +1140,70 @@ function getWrappedFilesLength(directoryView, directoryArray, offset) {
|
|
|
1084
1140
|
return wrappedFilesLength % (MAX_16_BITS + 1) ? 0 : wrappedFilesLength;
|
|
1085
1141
|
}
|
|
1086
1142
|
|
|
1143
|
+
function pointsAtDirectory(reader, offset) {
|
|
1144
|
+
return startsWithSignature(reader, offset, CENTRAL_FILE_HEADER_SIGNATURE);
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
async function startsWithSignature(reader, offset, signature) {
|
|
1148
|
+
if (offset < 0 || offset + 4 > reader.size) {
|
|
1149
|
+
return false;
|
|
1150
|
+
}
|
|
1151
|
+
const signatureArray = await readUint8Array(reader, offset, 4);
|
|
1152
|
+
return getUint32(getDataView(signatureArray), 0) == signature;
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
async function findDigitalSignatureRecordOffset(reader, endOffset) {
|
|
1156
|
+
const startOffset = Math.max(0, endOffset - (6 + MAX_16_BITS));
|
|
1157
|
+
const tailArray = await readUint8Array(reader, startOffset, endOffset - startOffset);
|
|
1158
|
+
const tailView = getDataView(tailArray);
|
|
1159
|
+
for (let indexByte = tailArray.length - 6; indexByte >= 0; indexByte--) {
|
|
1160
|
+
if (getUint32(tailView, indexByte) == DIGITAL_SIGNATURE_RECORD_SIGNATURE &&
|
|
1161
|
+
indexByte + 6 + getUint16(tailView, indexByte + 4) == tailArray.length) {
|
|
1162
|
+
return startOffset + indexByte;
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
async function findZip64EndOfDirectoryOffset(reader, endOffset) {
|
|
1168
|
+
const startOffset = Math.max(0, endOffset - (ZIP64_END_OF_CENTRAL_DIR_LENGTH + MAX_16_BITS));
|
|
1169
|
+
const tailArray = await readUint8Array(reader, startOffset, endOffset - startOffset);
|
|
1170
|
+
const tailView = getDataView(tailArray);
|
|
1171
|
+
for (let indexByte = tailArray.length - ZIP64_END_OF_CENTRAL_DIR_LENGTH; indexByte >= 0; indexByte--) {
|
|
1172
|
+
if (getUint32(tailView, indexByte) == ZIP64_END_OF_CENTRAL_DIR_SIGNATURE &&
|
|
1173
|
+
indexByte + 12 + Number(tailView.getBigUint64(indexByte + 4, true)) == tailArray.length) {
|
|
1174
|
+
return startOffset + indexByte;
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
function getFirstLocalHeaderOffset(directoryArray, directoryView) {
|
|
1180
|
+
if (directoryArray.length < CENTRAL_FILE_HEADER_LENGTH) {
|
|
1181
|
+
return UNDEFINED_VALUE;
|
|
1182
|
+
}
|
|
1183
|
+
const localHeaderOffset = getUint32(directoryView, 42);
|
|
1184
|
+
if (localHeaderOffset != MAX_32_BITS) {
|
|
1185
|
+
return localHeaderOffset;
|
|
1186
|
+
}
|
|
1187
|
+
let offsetExtraField = CENTRAL_FILE_HEADER_LENGTH + getUint16(directoryView, 28);
|
|
1188
|
+
const endExtraField = Math.min(offsetExtraField + getUint16(directoryView, 30), directoryArray.length);
|
|
1189
|
+
while (offsetExtraField + 4 <= endExtraField) {
|
|
1190
|
+
const type = getUint16(directoryView, offsetExtraField);
|
|
1191
|
+
const size = getUint16(directoryView, offsetExtraField + 2);
|
|
1192
|
+
if (type == EXTRAFIELD_TYPE_ZIP64) {
|
|
1193
|
+
let offsetValue = offsetExtraField + 4;
|
|
1194
|
+
if (getUint32(directoryView, 24) == MAX_32_BITS) {
|
|
1195
|
+
offsetValue += 8;
|
|
1196
|
+
}
|
|
1197
|
+
if (getUint32(directoryView, 20) == MAX_32_BITS) {
|
|
1198
|
+
offsetValue += 8;
|
|
1199
|
+
}
|
|
1200
|
+
return offsetValue + 8 <= endExtraField ? getBigUint64(directoryView, offsetValue) : UNDEFINED_VALUE;
|
|
1201
|
+
}
|
|
1202
|
+
offsetExtraField += 4 + size;
|
|
1203
|
+
}
|
|
1204
|
+
return UNDEFINED_VALUE;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1087
1207
|
function readDigitalSignature(signatureRecordArray) {
|
|
1088
1208
|
if (signatureRecordArray.length >= 6) {
|
|
1089
1209
|
const signatureRecordView = getDataView(signatureRecordArray);
|
|
@@ -1173,6 +1293,7 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
|
|
|
1173
1293
|
uncompressedSize: getUint32(dataView, offset + HEADER_OFFSET_UNCOMPRESSED_SIZE)
|
|
1174
1294
|
});
|
|
1175
1295
|
const extraFieldZip64 = extraField.get(EXTRAFIELD_TYPE_ZIP64);
|
|
1296
|
+
let extraFieldZip64Missing;
|
|
1176
1297
|
if (extraFieldZip64) {
|
|
1177
1298
|
if (!readExtraFieldZip64(extraFieldZip64, directory, localDirectory)) {
|
|
1178
1299
|
malformedExtraField = true;
|
|
@@ -1182,7 +1303,7 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
|
|
|
1182
1303
|
if (localDirectory) {
|
|
1183
1304
|
malformedExtraField = true;
|
|
1184
1305
|
} else {
|
|
1185
|
-
|
|
1306
|
+
extraFieldZip64Missing = true;
|
|
1186
1307
|
}
|
|
1187
1308
|
}
|
|
1188
1309
|
const extraFieldUnicodePath = extraField.get(EXTRAFIELD_TYPE_UNICODE_PATH);
|
|
@@ -1215,6 +1336,11 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
|
|
|
1215
1336
|
readExtraFieldUnixDates(extraFieldUnixType1, directory);
|
|
1216
1337
|
directory.extraFieldUnixType1 = extraFieldUnixType1;
|
|
1217
1338
|
}
|
|
1339
|
+
const extraFieldExtendedTimestamp = extraField.get(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
|
|
1340
|
+
if (extraFieldExtendedTimestamp) {
|
|
1341
|
+
readExtraFieldExtendedTimestamp(extraFieldExtendedTimestamp, directory, localDirectory);
|
|
1342
|
+
directory.extraFieldExtendedTimestamp = extraFieldExtendedTimestamp;
|
|
1343
|
+
}
|
|
1218
1344
|
const extraFieldNTFS = extraField.get(EXTRAFIELD_TYPE_NTFS);
|
|
1219
1345
|
if (extraFieldNTFS) {
|
|
1220
1346
|
readExtraFieldNTFS(extraFieldNTFS, directory);
|
|
@@ -1231,15 +1357,13 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
|
|
|
1231
1357
|
readExtraFieldUnix(extraFieldUnix, unixIdsRead ? {} : directory, false);
|
|
1232
1358
|
directory.extraFieldUnix = extraFieldUnix;
|
|
1233
1359
|
}
|
|
1234
|
-
const extraFieldExtendedTimestamp = extraField.get(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
|
|
1235
|
-
if (extraFieldExtendedTimestamp) {
|
|
1236
|
-
readExtraFieldExtendedTimestamp(extraFieldExtendedTimestamp, directory, localDirectory);
|
|
1237
|
-
directory.extraFieldExtendedTimestamp = extraFieldExtendedTimestamp;
|
|
1238
|
-
}
|
|
1239
1360
|
const extraFieldUSDZ = extraField.get(EXTRAFIELD_TYPE_USDZ);
|
|
1240
1361
|
if (extraFieldUSDZ) {
|
|
1241
1362
|
directory.extraFieldUSDZ = extraFieldUSDZ;
|
|
1242
1363
|
}
|
|
1364
|
+
if (extraFieldZip64Missing) {
|
|
1365
|
+
throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
|
|
1366
|
+
}
|
|
1243
1367
|
return malformedExtraField;
|
|
1244
1368
|
}
|
|
1245
1369
|
|
|
@@ -1454,13 +1578,7 @@ async function detectOverlappingEntry({
|
|
|
1454
1578
|
let dataDescriptorLength = 0;
|
|
1455
1579
|
if (dataDescriptor) {
|
|
1456
1580
|
const zip64 = Boolean(extraFieldZip64);
|
|
1457
|
-
const
|
|
1458
|
-
const dataDescriptorView = getDataView(dataDescriptorArray);
|
|
1459
|
-
const candidates = [[zip64, true], [zip64, false], [!zip64, true], [!zip64, false]]
|
|
1460
|
-
.map(([zip64Layout, signature]) => readDataDescriptor(dataDescriptorView, zip64Layout, signature))
|
|
1461
|
-
.filter(candidate => candidate && candidate.compressedSize == compressedSize && candidate.uncompressedSize == uncompressedSize);
|
|
1462
|
-
const localDataDescriptor = candidates.find(candidate => candidate.crc32 == crc32) || candidates[0] ||
|
|
1463
|
-
readDataDescriptor(dataDescriptorView, zip64, true) || readDataDescriptor(dataDescriptorView, zip64, false);
|
|
1581
|
+
const localDataDescriptor = await readLocalDataDescriptor(reader, dataOffset + compressedSize, { crc32, compressedSize, uncompressedSize, zip64 });
|
|
1464
1582
|
if (localDataDescriptor) {
|
|
1465
1583
|
fileEntry.localDirectory.dataDescriptor = localDataDescriptor;
|
|
1466
1584
|
dataDescriptorLength = getDataDescriptorLength(localDataDescriptor.zip64, localDataDescriptor.signature);
|
|
@@ -1530,6 +1648,25 @@ function mergeRanges(sortedRanges, pendingRanges) {
|
|
|
1530
1648
|
return mergedRanges;
|
|
1531
1649
|
}
|
|
1532
1650
|
|
|
1651
|
+
async function readLocalDataDescriptor(reader, offset, { crc32, compressedSize, uncompressedSize, zip64 }) {
|
|
1652
|
+
const maxLength = DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH;
|
|
1653
|
+
const length = Math.min(maxLength, Math.max(0, reader.size - offset));
|
|
1654
|
+
const dataDescriptorArray = length ? await readUint8Array(reader, offset, length) : EMPTY_UINT8_ARRAY;
|
|
1655
|
+
const dataDescriptorView = getDataView(dataDescriptorArray);
|
|
1656
|
+
const candidates = [[zip64, true], [zip64, false], [!zip64, true], [!zip64, false]]
|
|
1657
|
+
.map(([zip64Layout, signature]) => readDataDescriptor(dataDescriptorView, zip64Layout, signature))
|
|
1658
|
+
.filter(candidate => candidate && candidate.compressedSize == compressedSize && candidate.uncompressedSize == uncompressedSize);
|
|
1659
|
+
return candidates.find(candidate => candidate.crc32 == crc32) || candidates[0] ||
|
|
1660
|
+
readDataDescriptor(dataDescriptorView, zip64, true) || readDataDescriptor(dataDescriptorView, zip64, false);
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
async function getEntryDataDescriptorLength(reader, offset, { crc32, compressedSize, uncompressedSize, extraFieldZip64 }) {
|
|
1664
|
+
const dataDescriptor = await readLocalDataDescriptor(reader, offset, { crc32, compressedSize, uncompressedSize, zip64: Boolean(extraFieldZip64) });
|
|
1665
|
+
if (dataDescriptor && dataDescriptor.compressedSize == compressedSize && dataDescriptor.uncompressedSize == uncompressedSize) {
|
|
1666
|
+
return getDataDescriptorLength(dataDescriptor.zip64, dataDescriptor.signature);
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1533
1670
|
function readDataDescriptor(dataDescriptorView, zip64, signature) {
|
|
1534
1671
|
const offset = signature ? DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH : 0;
|
|
1535
1672
|
if (dataDescriptorView.byteLength < getDataDescriptorLength(zip64, signature) ||
|
|
@@ -1789,6 +1926,18 @@ function validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, chec
|
|
|
1789
1926
|
}
|
|
1790
1927
|
}
|
|
1791
1928
|
|
|
1929
|
+
function validateDataDescriptor(zipEntry, localDirectory, warnings) {
|
|
1930
|
+
const { dataDescriptor } = localDirectory;
|
|
1931
|
+
const maskedLocalDirectory = zipEntry.decryptedDirectory &&
|
|
1932
|
+
(localDirectory.rawBitFlag & BITFLAG_MASKED_LOCAL_HEADERS) == BITFLAG_MASKED_LOCAL_HEADERS;
|
|
1933
|
+
if (dataDescriptor && !maskedLocalDirectory &&
|
|
1934
|
+
((zipEntry.crc32 !== UNDEFINED_VALUE && dataDescriptor.crc32 != zipEntry.crc32) ||
|
|
1935
|
+
dataDescriptor.compressedSize != zipEntry.compressedSize ||
|
|
1936
|
+
dataDescriptor.uncompressedSize != zipEntry.uncompressedSize)) {
|
|
1937
|
+
reportAmbiguity(!warnings, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES);
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1792
1941
|
function reportAmbiguity(reject, warnings, reason) {
|
|
1793
1942
|
if (reject) {
|
|
1794
1943
|
throwAmbiguousArchive(reason);
|