@zip.js/zip.js 2.20.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.20.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";
@@ -258,7 +258,8 @@ class ZipReader {
258
258
  Object.assign(this, {
259
259
  reader: new GenericReader(reader),
260
260
  options,
261
- readRanges: { indexes: new Set(), sortedRanges: [], pendingRanges: [] }
261
+ readRanges: { indexes: new Set(), sortedRanges: [], pendingRanges: [] },
262
+ entriesMissingZip64ExtraField: new WeakSet()
262
263
  });
263
264
  }
264
265
 
@@ -329,16 +330,22 @@ class ZipReader {
329
330
  directoryDataOffset = getDiskOffset(reader, getUint32(endOfDirectoryLocatorView, 4)) + getBigUint64(endOfDirectoryLocatorView, 8);
330
331
  let endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
331
332
  let endOfDirectoryView = getDataView(endOfDirectoryArray);
332
- const expectedDirectoryDataOffset = endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH - ZIP64_END_OF_CENTRAL_DIR_LENGTH;
333
- if ((endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) &&
334
- directoryDataOffset != expectedDirectoryDataOffset && expectedDirectoryDataOffset >= 0) {
335
- const originalDirectoryDataOffset = directoryDataOffset;
336
- directoryDataOffset = expectedDirectoryDataOffset;
337
- if (directoryDataOffset > originalDirectoryDataOffset) {
338
- 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);
339
348
  }
340
- endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
341
- endOfDirectoryView = getDataView(endOfDirectoryArray);
342
349
  }
343
350
  if (endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
344
351
  throw new Error(ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND);
@@ -383,10 +390,6 @@ class ZipReader {
383
390
  let declaredDirectoryDataLength = directoryDataLength;
384
391
  const centralDirectoryEndOffset = endOfDirectoryInfo.offset -
385
392
  (zip64EndOfDirectory ? zip64EndOfDirectoryLength + ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH : 0);
386
- if (directoryDataOffset >= reader.size) {
387
- prependedDataLength = reader.size - directoryDataOffset - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
388
- directoryDataOffset = reader.size - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
389
- }
390
393
  if (expectedLastDiskNumber != lastDiskNumber) {
391
394
  throw new Error(ERR_SPLIT_ZIP_FILE);
392
395
  }
@@ -394,52 +397,64 @@ class ZipReader {
394
397
  throw new Error(ERR_BAD_FORMAT);
395
398
  }
396
399
  let offset = 0;
397
- let directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
400
+ let directoryArray = directoryDataOffset < reader.size ?
401
+ await readUint8Array(reader, directoryDataOffset, directoryDataLength) :
402
+ EMPTY_UINT8_ARRAY;
398
403
  let directoryView = getDataView(directoryArray);
399
404
  if (directoryDataLength) {
400
- if (directoryArray.length < 4) {
401
- throw new Error(ERR_BAD_FORMAT);
402
- }
403
405
  let expectedDirectoryDataOffset = centralDirectoryEndOffset - directoryDataLength;
404
406
  if (directoryDataOffset != expectedDirectoryDataOffset && diskNumber == lastDiskNumber) {
405
- const storedPointsAtDirectory = getUint32(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
407
+ const storedPointsAtDirectory = directoryArray.length >= 4 && (
408
+ getUint32(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
406
409
  Boolean(directoryEncryptionInfo && directoryEncryptionInfo.compressedSize) ||
407
- detectEncryptedCentralDirectory(directoryView);
410
+ detectEncryptedCentralDirectory(directoryView));
411
+ if (!storedPointsAtDirectory && expectedDirectoryDataOffset < 0) {
412
+ throw new Error(ERR_BAD_FORMAT);
413
+ }
408
414
  let reconcile = !storedPointsAtDirectory;
409
415
  if (expectedDirectoryDataOffset >= 0 && expectedDirectoryDataOffset + 4 <= reader.size) {
410
- const expectedPointsAtDirectory = await pointsAtDirectory(reader, expectedDirectoryDataOffset);
411
- if (storedPointsAtDirectory) {
412
- reconcile = expectedPointsAtDirectory;
413
- } else if (!expectedPointsAtDirectory) {
416
+ let expectedPointsAtDirectory = await pointsAtDirectory(reader, expectedDirectoryDataOffset);
417
+ if (!expectedPointsAtDirectory) {
414
418
  const signatureRecordOffset = await findDigitalSignatureRecordOffset(reader, centralDirectoryEndOffset);
415
419
  const signedDirectoryDataOffset = signatureRecordOffset - directoryDataLength;
416
420
  if (signatureRecordOffset !== UNDEFINED_VALUE && signedDirectoryDataOffset >= 0 &&
417
421
  await pointsAtDirectory(reader, signedDirectoryDataOffset)) {
418
422
  expectedDirectoryDataOffset = signedDirectoryDataOffset;
423
+ expectedPointsAtDirectory = true;
419
424
  }
420
425
  }
426
+ if (storedPointsAtDirectory) {
427
+ reconcile = expectedPointsAtDirectory;
428
+ }
421
429
  }
422
- if (reconcile) {
430
+ if (reconcile && expectedDirectoryDataOffset != directoryDataOffset) {
423
431
  const originalDirectoryDataOffset = directoryDataOffset;
424
432
  directoryDataOffset = expectedDirectoryDataOffset;
425
433
  directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
426
434
  directoryView = getDataView(directoryArray);
427
- if (directoryDataOffset > originalDirectoryDataOffset) {
428
- prependedDataLength += directoryDataOffset - originalDirectoryDataOffset;
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;
429
443
  prependedCentralDirectory = storedPointsAtDirectory;
430
444
  } else {
431
445
  reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET);
432
- const offsetDelta = directoryDataOffset - originalDirectoryDataOffset;
433
- if (directoryArray.length >= CENTRAL_FILE_HEADER_LENGTH) {
434
- const localHeaderOffset = getUint32(directoryView, 42);
435
- if (!await startsWithSignature(reader, localHeaderOffset, LOCAL_FILE_HEADER_SIGNATURE) &&
436
- await startsWithSignature(reader, localHeaderOffset + offsetDelta, LOCAL_FILE_HEADER_SIGNATURE)) {
437
- prependedDataLength += offsetDelta;
438
- }
446
+ if (offsetDelta < 0 && !localHeaderFound && shiftedLocalHeaderFound) {
447
+ prependedDataLength += offsetDelta;
439
448
  }
440
449
  }
441
450
  }
442
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;
443
458
  }
444
459
  const expectedDirectoryDataLength = centralDirectoryEndOffset - directoryDataOffset;
445
460
  if (directoryDataLength != expectedDirectoryDataLength && expectedDirectoryDataLength >= 0 && diskNumber == lastDiskNumber) {
@@ -481,8 +496,10 @@ class ZipReader {
481
496
  addWarning(warnings, WARNING_WRAPPED_ENTRIES_COUNT);
482
497
  }
483
498
  }
499
+ const entryOptions = Object.assign({}, zipReader.options, options, { [OPTION_STRICTNESS]: strictness });
500
+ delete entryOptions[OPTION_CHECK_AMBIGUITY];
484
501
  for (let indexFile = 0; indexFile < filesLength; indexFile++) {
485
- const fileEntry = new ZipEntry(reader, zipReader.options);
502
+ const fileEntry = new ZipEntry(reader, entryOptions);
486
503
  if (offset + CENTRAL_FILE_HEADER_LENGTH > directoryArray.length || getUint32(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE) {
487
504
  if (indexFile == 0 && !decryptedDirectory && (zip64EndOfDirectoryVersion2 || detectEncryptedCentralDirectory(directoryView))) {
488
505
  throw new Error(ERR_ENCRYPTED_CENTRAL_DIRECTORY);
@@ -573,13 +590,17 @@ class ZipReader {
573
590
  if (malformedExtraField) {
574
591
  addWarning(warnings, WARNING_MALFORMED_EXTRA_FIELD, filename);
575
592
  }
576
- fileEntry.offset += prependedDataLength;
577
- const entryPosition = getDiskOffset(reader, fileEntry.diskNumberStart) + fileEntry.offset;
578
- startOffset = Math.min(entryPosition, startOffset);
579
- if (entryPosition < previousEntryPosition) {
580
- 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;
581
603
  }
582
- previousEntryPosition = entryPosition;
583
604
  if ((fileEntry.version & MAX_8_BITS) > MAX_KNOWN_VERSION) {
584
605
  addWarning(warnings, WARNING_UNKNOWN_VERSION, filename);
585
606
  }
@@ -616,12 +637,15 @@ class ZipReader {
616
637
  zipCrypto: fileEntry.encrypted && !fileEntry.extraFieldAES
617
638
  });
618
639
  const entry = new Entry(fileEntry);
619
- 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);
620
644
  entry.arrayBuffer = async options => {
621
645
  const writer = new TransformStream();
622
646
  const arrayBufferPromise = streamToBlob(writer.readable).then(blob => blob.arrayBuffer());
623
647
  arrayBufferPromise.catch(() => { });
624
- await fileEntry.getData(writer, entry, zipReader.readRanges,
648
+ await fileEntry.getData(writer, entry, zipReader,
625
649
  Object.assign({}, options, { preventClose: false }));
626
650
  return arrayBufferPromise;
627
651
  };
@@ -824,6 +848,7 @@ export {
824
848
  ZipReader,
825
849
  isZipFile,
826
850
  ZipReaderStream,
851
+ getEntryDataDescriptorLength,
827
852
  ERR_BAD_FORMAT,
828
853
  ERR_EOCDR_NOT_FOUND,
829
854
  ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND,
@@ -880,8 +905,9 @@ class ZipEntry {
880
905
  });
881
906
  }
882
907
 
883
- async getData(writer, fileEntry, readRanges, options = {}) {
908
+ async getData(writer, fileEntry, zipReader, options = {}) {
884
909
  const zipEntry = this;
910
+ const { readRanges, directoryOffset } = zipReader;
885
911
  if (zipEntry.zip64ExtraFieldMissing) {
886
912
  throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
887
913
  }
@@ -955,10 +981,10 @@ class ZipEntry {
955
981
  }
956
982
  validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
957
983
  const { lastAccessDate, creationDate, uid, gid } = localDirectory;
958
- if (lastAccessDate) {
984
+ if (lastAccessDate && fileEntry.lastAccessDate === UNDEFINED_VALUE) {
959
985
  fileEntry.lastAccessDate = lastAccessDate;
960
986
  }
961
- if (creationDate) {
987
+ if (creationDate && fileEntry.creationDate === UNDEFINED_VALUE) {
962
988
  fileEntry.creationDate = creationDate;
963
989
  }
964
990
  if (uid !== UNDEFINED_VALUE && fileEntry.uid === UNDEFINED_VALUE) {
@@ -987,7 +1013,7 @@ class ZipEntry {
987
1013
  throw new Error(ERR_ENCRYPTED);
988
1014
  }
989
1015
  }
990
- if (dataOffset + compressedSize > reader.size) {
1016
+ if (dataOffset + compressedSize > Math.min(reader.size, directoryOffset)) {
991
1017
  throw new Error(ERR_ENTRY_DATA_OUT_OF_BOUNDS);
992
1018
  }
993
1019
  const size = compressedSize;
@@ -1053,6 +1079,7 @@ class ZipEntry {
1053
1079
  extraFieldZip64: extraFieldZip64 || localDirectory.extraFieldZip64,
1054
1080
  readRanges
1055
1081
  });
1082
+ validateDataDescriptor(zipEntry, localDirectory, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
1056
1083
  }
1057
1084
  let writable, abortError, aborted;
1058
1085
  try {
@@ -1072,10 +1099,7 @@ class ZipEntry {
1072
1099
  writer.size += writtenSize;
1073
1100
  }
1074
1101
  } catch (error) {
1075
- const { outputSize: failedOutputSize } = workerOptions;
1076
- if (failedOutputSize !== UNDEFINED_VALUE) {
1077
- writer.size += failedOutputSize;
1078
- } else if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
1102
+ if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
1079
1103
  writer.size += error.outputSize;
1080
1104
  }
1081
1105
  if (!checkPasswordOnly || !isErrorObject(error) || error.message != ERR_ABORT_CHECK_PASSWORD) {
@@ -1103,13 +1127,7 @@ class ZipEntry {
1103
1127
  }
1104
1128
 
1105
1129
  function detectEncryptedCentralDirectory(directoryView) {
1106
- const maxOffset = Math.min(directoryView.byteLength, 1024) - 3;
1107
- for (let offset = 0; offset < maxOffset; offset++) {
1108
- if (getUint32(directoryView, offset) == ARCHIVE_EXTRA_DATA_SIGNATURE) {
1109
- return true;
1110
- }
1111
- }
1112
- return false;
1130
+ return directoryView.byteLength >= 4 && getUint32(directoryView, 0) == ARCHIVE_EXTRA_DATA_SIGNATURE;
1113
1131
  }
1114
1132
 
1115
1133
  function getWrappedFilesLength(directoryView, directoryArray, offset) {
@@ -1146,6 +1164,46 @@ async function findDigitalSignatureRecordOffset(reader, endOffset) {
1146
1164
  }
1147
1165
  }
1148
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
+
1149
1207
  function readDigitalSignature(signatureRecordArray) {
1150
1208
  if (signatureRecordArray.length >= 6) {
1151
1209
  const signatureRecordView = getDataView(signatureRecordArray);
@@ -1235,6 +1293,7 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
1235
1293
  uncompressedSize: getUint32(dataView, offset + HEADER_OFFSET_UNCOMPRESSED_SIZE)
1236
1294
  });
1237
1295
  const extraFieldZip64 = extraField.get(EXTRAFIELD_TYPE_ZIP64);
1296
+ let extraFieldZip64Missing;
1238
1297
  if (extraFieldZip64) {
1239
1298
  if (!readExtraFieldZip64(extraFieldZip64, directory, localDirectory)) {
1240
1299
  malformedExtraField = true;
@@ -1244,7 +1303,7 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
1244
1303
  if (localDirectory) {
1245
1304
  malformedExtraField = true;
1246
1305
  } else {
1247
- throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
1306
+ extraFieldZip64Missing = true;
1248
1307
  }
1249
1308
  }
1250
1309
  const extraFieldUnicodePath = extraField.get(EXTRAFIELD_TYPE_UNICODE_PATH);
@@ -1302,6 +1361,9 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
1302
1361
  if (extraFieldUSDZ) {
1303
1362
  directory.extraFieldUSDZ = extraFieldUSDZ;
1304
1363
  }
1364
+ if (extraFieldZip64Missing) {
1365
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
1366
+ }
1305
1367
  return malformedExtraField;
1306
1368
  }
1307
1369
 
@@ -1516,13 +1578,7 @@ async function detectOverlappingEntry({
1516
1578
  let dataDescriptorLength = 0;
1517
1579
  if (dataDescriptor) {
1518
1580
  const zip64 = Boolean(extraFieldZip64);
1519
- const dataDescriptorArray = await readUint8Array(reader, dataOffset + compressedSize, DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH);
1520
- const dataDescriptorView = getDataView(dataDescriptorArray);
1521
- const candidates = [[zip64, true], [zip64, false], [!zip64, true], [!zip64, false]]
1522
- .map(([zip64Layout, signature]) => readDataDescriptor(dataDescriptorView, zip64Layout, signature))
1523
- .filter(candidate => candidate && candidate.compressedSize == compressedSize && candidate.uncompressedSize == uncompressedSize);
1524
- const localDataDescriptor = candidates.find(candidate => candidate.crc32 == crc32) || candidates[0] ||
1525
- readDataDescriptor(dataDescriptorView, zip64, true) || readDataDescriptor(dataDescriptorView, zip64, false);
1581
+ const localDataDescriptor = await readLocalDataDescriptor(reader, dataOffset + compressedSize, { crc32, compressedSize, uncompressedSize, zip64 });
1526
1582
  if (localDataDescriptor) {
1527
1583
  fileEntry.localDirectory.dataDescriptor = localDataDescriptor;
1528
1584
  dataDescriptorLength = getDataDescriptorLength(localDataDescriptor.zip64, localDataDescriptor.signature);
@@ -1592,6 +1648,25 @@ function mergeRanges(sortedRanges, pendingRanges) {
1592
1648
  return mergedRanges;
1593
1649
  }
1594
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
+
1595
1670
  function readDataDescriptor(dataDescriptorView, zip64, signature) {
1596
1671
  const offset = signature ? DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH : 0;
1597
1672
  if (dataDescriptorView.byteLength < getDataDescriptorLength(zip64, signature) ||
@@ -1851,6 +1926,18 @@ function validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, chec
1851
1926
  }
1852
1927
  }
1853
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
+
1854
1941
  function reportAmbiguity(reject, warnings, reason) {
1855
1942
  if (reject) {
1856
1943
  throwAmbiguousArchive(reason);