@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.
@@ -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,
@@ -26,6 +26,6 @@
26
26
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
27
  */
28
28
 
29
- const VERSION = "2.19.0";
29
+ const VERSION = "2.21.0";
30
30
 
31
31
  export { VERSION };
@@ -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
- const expectedDirectoryDataOffset = endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH - ZIP64_END_OF_CENTRAL_DIR_LENGTH;
331
- if ((endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) &&
332
- directoryDataOffset != expectedDirectoryDataOffset && expectedDirectoryDataOffset >= 0) {
333
- const originalDirectoryDataOffset = directoryDataOffset;
334
- directoryDataOffset = expectedDirectoryDataOffset;
335
- if (directoryDataOffset > originalDirectoryDataOffset) {
336
- prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
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 = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
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
- if (directoryArray.length < 4) {
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 = getUint32(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
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 (!reconcile && expectedDirectoryDataOffset >= 0 && expectedDirectoryDataOffset + 4 <= reader.size) {
406
- const expectedSignatureArray = await readUint8Array(reader, expectedDirectoryDataOffset, 4);
407
- reconcile = getUint32(getDataView(expectedSignatureArray), 0) == CENTRAL_FILE_HEADER_SIGNATURE;
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, zipReader.options);
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
- const malformedExtraField = readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6);
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 += prependedDataLength;
545
- const entryPosition = getDiskOffset(reader, fileEntry.diskNumberStart) + fileEntry.offset;
546
- startOffset = Math.min(entryPosition, startOffset);
547
- if (entryPosition < previousEntryPosition) {
548
- addWarning(warnings, WARNING_UNSORTED_CENTRAL_DIRECTORY, filename);
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: modeIsDir || upperIsDir || (msDosCompatible && msdosAttributes.directory) || fileEntry.filename.endsWith(DIRECTORY_SIGNATURE),
636
+ directory,
583
637
  zipCrypto: fileEntry.encrypted && !fileEntry.extraFieldAES
584
638
  });
585
639
  const entry = new Entry(fileEntry);
586
- entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader.readRanges, options);
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.readRanges,
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, readRanges, options = {}) {
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
- const { outputSize: failedOutputSize } = workerOptions;
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
- const maxOffset = Math.min(directoryView.byteLength, 1024) - 3;
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
- throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
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 dataDescriptorArray = await readUint8Array(reader, dataOffset + compressedSize, DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH);
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);