@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.
package/index.cjs CHANGED
@@ -120,6 +120,10 @@ const MIN_DATE = new Date(1980, 0, 1);
120
120
 
121
121
  const UNDEFINED_VALUE = undefined;
122
122
  const INFINITY_VALUE = Infinity;
123
+ const ERR_LOCAL_FILE_HEADER_NOT_FOUND = "Local file header not found";
124
+ const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
125
+ const ERR_EXTRAFIELD_ZIP64_NOT_FOUND = "Zip64 extra field not found";
126
+
123
127
  const UNDEFINED_TYPE = "undefined";
124
128
  const FUNCTION_TYPE = "function";
125
129
  const OBJECT_TYPE = "object";
@@ -4779,12 +4783,9 @@ const ERR_BAD_FORMAT = "File format is not recognized";
4779
4783
  const ERR_EOCDR_NOT_FOUND = "End of central directory not found";
4780
4784
  const ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND = "End of Zip64 central directory locator not found";
4781
4785
  const ERR_CENTRAL_DIRECTORY_NOT_FOUND = "Central directory header not found";
4782
- const ERR_LOCAL_FILE_HEADER_NOT_FOUND = "Local file header not found";
4783
- const ERR_EXTRAFIELD_ZIP64_NOT_FOUND = "Zip64 extra field not found";
4784
4786
  const ERR_ENCRYPTED = "File contains encrypted entry";
4785
4787
  const ERR_UNSUPPORTED_ENCRYPTION = "Encryption method not supported";
4786
4788
  const ERR_SPLIT_ZIP_FILE = "Split zip file";
4787
- const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
4788
4789
  const ERR_ENTRY_DATA_OUT_OF_BOUNDS = "Entry data out of bounds";
4789
4790
  const ERR_AMBIGUOUS_ARCHIVE = "Ambiguous archive";
4790
4791
  const ERR_ENCRYPTED_CENTRAL_DIRECTORY = "Encrypted central directory is not supported";
@@ -4849,7 +4850,8 @@ class ZipReader {
4849
4850
  Object.assign(this, {
4850
4851
  reader: new GenericReader(reader),
4851
4852
  options,
4852
- readRanges: { indexes: new Set(), sortedRanges: [], pendingRanges: [] }
4853
+ readRanges: { indexes: new Set(), sortedRanges: [], pendingRanges: [] },
4854
+ entriesMissingZip64ExtraField: new WeakSet()
4853
4855
  });
4854
4856
  }
4855
4857
 
@@ -4920,16 +4922,22 @@ class ZipReader {
4920
4922
  directoryDataOffset = getDiskOffset$1(reader, getUint32$1(endOfDirectoryLocatorView, 4)) + getBigUint64(endOfDirectoryLocatorView, 8);
4921
4923
  let endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4922
4924
  let endOfDirectoryView = getDataView(endOfDirectoryArray);
4923
- const expectedDirectoryDataOffset = endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH - ZIP64_END_OF_CENTRAL_DIR_LENGTH;
4924
- if ((endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) &&
4925
- directoryDataOffset != expectedDirectoryDataOffset && expectedDirectoryDataOffset >= 0) {
4926
- const originalDirectoryDataOffset = directoryDataOffset;
4927
- directoryDataOffset = expectedDirectoryDataOffset;
4928
- if (directoryDataOffset > originalDirectoryDataOffset) {
4929
- prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
4925
+ if (endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
4926
+ const locatorOffset = endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH;
4927
+ let expectedDirectoryDataOffset = locatorOffset - ZIP64_END_OF_CENTRAL_DIR_LENGTH;
4928
+ if (!await startsWithSignature(reader, expectedDirectoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_SIGNATURE)) {
4929
+ expectedDirectoryDataOffset = await findZip64EndOfDirectoryOffset(reader, locatorOffset);
4930
+ }
4931
+ if (expectedDirectoryDataOffset !== UNDEFINED_VALUE && expectedDirectoryDataOffset >= 0 &&
4932
+ directoryDataOffset != expectedDirectoryDataOffset) {
4933
+ const originalDirectoryDataOffset = directoryDataOffset;
4934
+ directoryDataOffset = expectedDirectoryDataOffset;
4935
+ if (directoryDataOffset > originalDirectoryDataOffset) {
4936
+ prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
4937
+ }
4938
+ endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4939
+ endOfDirectoryView = getDataView(endOfDirectoryArray);
4930
4940
  }
4931
- endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4932
- endOfDirectoryView = getDataView(endOfDirectoryArray);
4933
4941
  }
4934
4942
  if (endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
4935
4943
  throw new Error(ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND);
@@ -4974,10 +4982,6 @@ class ZipReader {
4974
4982
  let declaredDirectoryDataLength = directoryDataLength;
4975
4983
  const centralDirectoryEndOffset = endOfDirectoryInfo.offset -
4976
4984
  (zip64EndOfDirectory ? zip64EndOfDirectoryLength + ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH : 0);
4977
- if (directoryDataOffset >= reader.size) {
4978
- prependedDataLength = reader.size - directoryDataOffset - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
4979
- directoryDataOffset = reader.size - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
4980
- }
4981
4985
  if (expectedLastDiskNumber != lastDiskNumber) {
4982
4986
  throw new Error(ERR_SPLIT_ZIP_FILE);
4983
4987
  }
@@ -4985,52 +4989,64 @@ class ZipReader {
4985
4989
  throw new Error(ERR_BAD_FORMAT);
4986
4990
  }
4987
4991
  let offset = 0;
4988
- let directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
4992
+ let directoryArray = directoryDataOffset < reader.size ?
4993
+ await readUint8Array(reader, directoryDataOffset, directoryDataLength) :
4994
+ EMPTY_UINT8_ARRAY;
4989
4995
  let directoryView = getDataView(directoryArray);
4990
4996
  if (directoryDataLength) {
4991
- if (directoryArray.length < 4) {
4992
- throw new Error(ERR_BAD_FORMAT);
4993
- }
4994
4997
  let expectedDirectoryDataOffset = centralDirectoryEndOffset - directoryDataLength;
4995
4998
  if (directoryDataOffset != expectedDirectoryDataOffset && diskNumber == lastDiskNumber) {
4996
- const storedPointsAtDirectory = getUint32$1(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
4999
+ const storedPointsAtDirectory = directoryArray.length >= 4 && (
5000
+ getUint32$1(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
4997
5001
  Boolean(directoryEncryptionInfo && directoryEncryptionInfo.compressedSize) ||
4998
- detectEncryptedCentralDirectory(directoryView);
5002
+ detectEncryptedCentralDirectory(directoryView));
5003
+ if (!storedPointsAtDirectory && expectedDirectoryDataOffset < 0) {
5004
+ throw new Error(ERR_BAD_FORMAT);
5005
+ }
4999
5006
  let reconcile = !storedPointsAtDirectory;
5000
5007
  if (expectedDirectoryDataOffset >= 0 && expectedDirectoryDataOffset + 4 <= reader.size) {
5001
- const expectedPointsAtDirectory = await pointsAtDirectory(reader, expectedDirectoryDataOffset);
5002
- if (storedPointsAtDirectory) {
5003
- reconcile = expectedPointsAtDirectory;
5004
- } else if (!expectedPointsAtDirectory) {
5008
+ let expectedPointsAtDirectory = await pointsAtDirectory(reader, expectedDirectoryDataOffset);
5009
+ if (!expectedPointsAtDirectory) {
5005
5010
  const signatureRecordOffset = await findDigitalSignatureRecordOffset(reader, centralDirectoryEndOffset);
5006
5011
  const signedDirectoryDataOffset = signatureRecordOffset - directoryDataLength;
5007
5012
  if (signatureRecordOffset !== UNDEFINED_VALUE && signedDirectoryDataOffset >= 0 &&
5008
5013
  await pointsAtDirectory(reader, signedDirectoryDataOffset)) {
5009
5014
  expectedDirectoryDataOffset = signedDirectoryDataOffset;
5015
+ expectedPointsAtDirectory = true;
5010
5016
  }
5011
5017
  }
5018
+ if (storedPointsAtDirectory) {
5019
+ reconcile = expectedPointsAtDirectory;
5020
+ }
5012
5021
  }
5013
- if (reconcile) {
5022
+ if (reconcile && expectedDirectoryDataOffset != directoryDataOffset) {
5014
5023
  const originalDirectoryDataOffset = directoryDataOffset;
5015
5024
  directoryDataOffset = expectedDirectoryDataOffset;
5016
5025
  directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
5017
5026
  directoryView = getDataView(directoryArray);
5018
- if (directoryDataOffset > originalDirectoryDataOffset) {
5019
- prependedDataLength += directoryDataOffset - originalDirectoryDataOffset;
5027
+ const offsetDelta = directoryDataOffset - originalDirectoryDataOffset;
5028
+ const localHeaderOffset = getFirstLocalHeaderOffset(directoryArray, directoryView);
5029
+ const localHeaderFound = localHeaderOffset !== UNDEFINED_VALUE &&
5030
+ await startsWithSignature(reader, localHeaderOffset, LOCAL_FILE_HEADER_SIGNATURE);
5031
+ const shiftedLocalHeaderFound = localHeaderOffset !== UNDEFINED_VALUE &&
5032
+ await startsWithSignature(reader, localHeaderOffset + offsetDelta, LOCAL_FILE_HEADER_SIGNATURE);
5033
+ if (offsetDelta > 0 && !(localHeaderFound && !shiftedLocalHeaderFound)) {
5034
+ prependedDataLength += offsetDelta;
5020
5035
  prependedCentralDirectory = storedPointsAtDirectory;
5021
5036
  } else {
5022
5037
  reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET);
5023
- const offsetDelta = directoryDataOffset - originalDirectoryDataOffset;
5024
- if (directoryArray.length >= CENTRAL_FILE_HEADER_LENGTH) {
5025
- const localHeaderOffset = getUint32$1(directoryView, 42);
5026
- if (!await startsWithSignature(reader, localHeaderOffset, LOCAL_FILE_HEADER_SIGNATURE) &&
5027
- await startsWithSignature(reader, localHeaderOffset + offsetDelta, LOCAL_FILE_HEADER_SIGNATURE)) {
5028
- prependedDataLength += offsetDelta;
5029
- }
5038
+ if (offsetDelta < 0 && !localHeaderFound && shiftedLocalHeaderFound) {
5039
+ prependedDataLength += offsetDelta;
5030
5040
  }
5031
5041
  }
5032
5042
  }
5033
5043
  }
5044
+ if (directoryArray.length < 4) {
5045
+ throw new Error(ERR_BAD_FORMAT);
5046
+ }
5047
+ } else if (!filesLength && directoryDataOffset < centralDirectoryEndOffset && diskNumber == lastDiskNumber) {
5048
+ prependedDataLength += centralDirectoryEndOffset - directoryDataOffset;
5049
+ directoryDataOffset = centralDirectoryEndOffset;
5034
5050
  }
5035
5051
  const expectedDirectoryDataLength = centralDirectoryEndOffset - directoryDataOffset;
5036
5052
  if (directoryDataLength != expectedDirectoryDataLength && expectedDirectoryDataLength >= 0 && diskNumber == lastDiskNumber) {
@@ -5072,8 +5088,10 @@ class ZipReader {
5072
5088
  addWarning(warnings, WARNING_WRAPPED_ENTRIES_COUNT);
5073
5089
  }
5074
5090
  }
5091
+ const entryOptions = Object.assign({}, zipReader.options, options, { [OPTION_STRICTNESS]: strictness });
5092
+ delete entryOptions[OPTION_CHECK_AMBIGUITY];
5075
5093
  for (let indexFile = 0; indexFile < filesLength; indexFile++) {
5076
- const fileEntry = new ZipEntry$1(reader, zipReader.options);
5094
+ const fileEntry = new ZipEntry$1(reader, entryOptions);
5077
5095
  if (offset + CENTRAL_FILE_HEADER_LENGTH > directoryArray.length || getUint32$1(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE) {
5078
5096
  if (indexFile == 0 && !decryptedDirectory && (zip64EndOfDirectoryVersion2 || detectEncryptedCentralDirectory(directoryView))) {
5079
5097
  throw new Error(ERR_ENCRYPTED_CENTRAL_DIRECTORY);
@@ -5164,13 +5182,17 @@ class ZipReader {
5164
5182
  if (malformedExtraField) {
5165
5183
  addWarning(warnings, WARNING_MALFORMED_EXTRA_FIELD, filename);
5166
5184
  }
5167
- fileEntry.offset += prependedDataLength;
5168
- const entryPosition = getDiskOffset$1(reader, fileEntry.diskNumberStart) + fileEntry.offset;
5169
- startOffset = Math.min(entryPosition, startOffset);
5170
- if (entryPosition < previousEntryPosition) {
5171
- addWarning(warnings, WARNING_UNSORTED_CENTRAL_DIRECTORY, filename);
5185
+ if (fileEntry.zip64ExtraFieldMissing && fileEntry.offset == MAX_32_BITS) {
5186
+ startOffset = 0;
5187
+ } else {
5188
+ fileEntry.offset += prependedDataLength;
5189
+ const entryPosition = getDiskOffset$1(reader, fileEntry.diskNumberStart) + fileEntry.offset;
5190
+ startOffset = Math.min(entryPosition, startOffset);
5191
+ if (entryPosition < previousEntryPosition) {
5192
+ addWarning(warnings, WARNING_UNSORTED_CENTRAL_DIRECTORY, filename);
5193
+ }
5194
+ previousEntryPosition = entryPosition;
5172
5195
  }
5173
- previousEntryPosition = entryPosition;
5174
5196
  if ((fileEntry.version & MAX_8_BITS) > MAX_KNOWN_VERSION) {
5175
5197
  addWarning(warnings, WARNING_UNKNOWN_VERSION, filename);
5176
5198
  }
@@ -5207,12 +5229,15 @@ class ZipReader {
5207
5229
  zipCrypto: fileEntry.encrypted && !fileEntry.extraFieldAES
5208
5230
  });
5209
5231
  const entry = new Entry(fileEntry);
5210
- entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader.readRanges, options);
5232
+ if (fileEntry.zip64ExtraFieldMissing) {
5233
+ zipReader.entriesMissingZip64ExtraField.add(entry);
5234
+ }
5235
+ entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader, options);
5211
5236
  entry.arrayBuffer = async options => {
5212
5237
  const writer = new TransformStream();
5213
5238
  const arrayBufferPromise = streamToBlob(writer.readable).then(blob => blob.arrayBuffer());
5214
5239
  arrayBufferPromise.catch(() => { });
5215
- await fileEntry.getData(writer, entry, zipReader.readRanges,
5240
+ await fileEntry.getData(writer, entry, zipReader,
5216
5241
  Object.assign({}, options, { preventClose: false }));
5217
5242
  return arrayBufferPromise;
5218
5243
  };
@@ -5420,8 +5445,9 @@ let ZipEntry$1 = class ZipEntry {
5420
5445
  });
5421
5446
  }
5422
5447
 
5423
- async getData(writer, fileEntry, readRanges, options = {}) {
5448
+ async getData(writer, fileEntry, zipReader, options = {}) {
5424
5449
  const zipEntry = this;
5450
+ const { readRanges, directoryOffset } = zipReader;
5425
5451
  if (zipEntry.zip64ExtraFieldMissing) {
5426
5452
  throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5427
5453
  }
@@ -5495,10 +5521,10 @@ let ZipEntry$1 = class ZipEntry {
5495
5521
  }
5496
5522
  validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
5497
5523
  const { lastAccessDate, creationDate, uid, gid } = localDirectory;
5498
- if (lastAccessDate) {
5524
+ if (lastAccessDate && fileEntry.lastAccessDate === UNDEFINED_VALUE) {
5499
5525
  fileEntry.lastAccessDate = lastAccessDate;
5500
5526
  }
5501
- if (creationDate) {
5527
+ if (creationDate && fileEntry.creationDate === UNDEFINED_VALUE) {
5502
5528
  fileEntry.creationDate = creationDate;
5503
5529
  }
5504
5530
  if (uid !== UNDEFINED_VALUE && fileEntry.uid === UNDEFINED_VALUE) {
@@ -5527,7 +5553,7 @@ let ZipEntry$1 = class ZipEntry {
5527
5553
  throw new Error(ERR_ENCRYPTED);
5528
5554
  }
5529
5555
  }
5530
- if (dataOffset + compressedSize > reader.size) {
5556
+ if (dataOffset + compressedSize > Math.min(reader.size, directoryOffset)) {
5531
5557
  throw new Error(ERR_ENTRY_DATA_OUT_OF_BOUNDS);
5532
5558
  }
5533
5559
  const size = compressedSize;
@@ -5593,6 +5619,7 @@ let ZipEntry$1 = class ZipEntry {
5593
5619
  extraFieldZip64: extraFieldZip64 || localDirectory.extraFieldZip64,
5594
5620
  readRanges
5595
5621
  });
5622
+ validateDataDescriptor(zipEntry, localDirectory, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
5596
5623
  }
5597
5624
  let writable, abortError, aborted;
5598
5625
  try {
@@ -5612,10 +5639,7 @@ let ZipEntry$1 = class ZipEntry {
5612
5639
  writer.size += writtenSize;
5613
5640
  }
5614
5641
  } catch (error) {
5615
- const { outputSize: failedOutputSize } = workerOptions;
5616
- if (failedOutputSize !== UNDEFINED_VALUE) {
5617
- writer.size += failedOutputSize;
5618
- } else if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
5642
+ if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
5619
5643
  writer.size += error.outputSize;
5620
5644
  }
5621
5645
  if (!checkPasswordOnly || !isErrorObject(error) || error.message != ERR_ABORT_CHECK_PASSWORD) {
@@ -5643,13 +5667,7 @@ let ZipEntry$1 = class ZipEntry {
5643
5667
  };
5644
5668
 
5645
5669
  function detectEncryptedCentralDirectory(directoryView) {
5646
- const maxOffset = Math.min(directoryView.byteLength, 1024) - 3;
5647
- for (let offset = 0; offset < maxOffset; offset++) {
5648
- if (getUint32$1(directoryView, offset) == ARCHIVE_EXTRA_DATA_SIGNATURE) {
5649
- return true;
5650
- }
5651
- }
5652
- return false;
5670
+ return directoryView.byteLength >= 4 && getUint32$1(directoryView, 0) == ARCHIVE_EXTRA_DATA_SIGNATURE;
5653
5671
  }
5654
5672
 
5655
5673
  function getWrappedFilesLength(directoryView, directoryArray, offset) {
@@ -5686,6 +5704,46 @@ async function findDigitalSignatureRecordOffset(reader, endOffset) {
5686
5704
  }
5687
5705
  }
5688
5706
 
5707
+ async function findZip64EndOfDirectoryOffset(reader, endOffset) {
5708
+ const startOffset = Math.max(0, endOffset - (ZIP64_END_OF_CENTRAL_DIR_LENGTH + MAX_16_BITS));
5709
+ const tailArray = await readUint8Array(reader, startOffset, endOffset - startOffset);
5710
+ const tailView = getDataView(tailArray);
5711
+ for (let indexByte = tailArray.length - ZIP64_END_OF_CENTRAL_DIR_LENGTH; indexByte >= 0; indexByte--) {
5712
+ if (getUint32$1(tailView, indexByte) == ZIP64_END_OF_CENTRAL_DIR_SIGNATURE &&
5713
+ indexByte + 12 + Number(tailView.getBigUint64(indexByte + 4, true)) == tailArray.length) {
5714
+ return startOffset + indexByte;
5715
+ }
5716
+ }
5717
+ }
5718
+
5719
+ function getFirstLocalHeaderOffset(directoryArray, directoryView) {
5720
+ if (directoryArray.length < CENTRAL_FILE_HEADER_LENGTH) {
5721
+ return UNDEFINED_VALUE;
5722
+ }
5723
+ const localHeaderOffset = getUint32$1(directoryView, 42);
5724
+ if (localHeaderOffset != MAX_32_BITS) {
5725
+ return localHeaderOffset;
5726
+ }
5727
+ let offsetExtraField = CENTRAL_FILE_HEADER_LENGTH + getUint16$1(directoryView, 28);
5728
+ const endExtraField = Math.min(offsetExtraField + getUint16$1(directoryView, 30), directoryArray.length);
5729
+ while (offsetExtraField + 4 <= endExtraField) {
5730
+ const type = getUint16$1(directoryView, offsetExtraField);
5731
+ const size = getUint16$1(directoryView, offsetExtraField + 2);
5732
+ if (type == EXTRAFIELD_TYPE_ZIP64) {
5733
+ let offsetValue = offsetExtraField + 4;
5734
+ if (getUint32$1(directoryView, 24) == MAX_32_BITS) {
5735
+ offsetValue += 8;
5736
+ }
5737
+ if (getUint32$1(directoryView, 20) == MAX_32_BITS) {
5738
+ offsetValue += 8;
5739
+ }
5740
+ return offsetValue + 8 <= endExtraField ? getBigUint64(directoryView, offsetValue) : UNDEFINED_VALUE;
5741
+ }
5742
+ offsetExtraField += 4 + size;
5743
+ }
5744
+ return UNDEFINED_VALUE;
5745
+ }
5746
+
5689
5747
  function readDigitalSignature(signatureRecordArray) {
5690
5748
  if (signatureRecordArray.length >= 6) {
5691
5749
  const signatureRecordView = getDataView(signatureRecordArray);
@@ -5775,6 +5833,7 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
5775
5833
  uncompressedSize: getUint32$1(dataView, offset + HEADER_OFFSET_UNCOMPRESSED_SIZE)
5776
5834
  });
5777
5835
  const extraFieldZip64 = extraField.get(EXTRAFIELD_TYPE_ZIP64);
5836
+ let extraFieldZip64Missing;
5778
5837
  if (extraFieldZip64) {
5779
5838
  if (!readExtraFieldZip64(extraFieldZip64, directory, localDirectory)) {
5780
5839
  malformedExtraField = true;
@@ -5784,7 +5843,7 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
5784
5843
  if (localDirectory) {
5785
5844
  malformedExtraField = true;
5786
5845
  } else {
5787
- throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5846
+ extraFieldZip64Missing = true;
5788
5847
  }
5789
5848
  }
5790
5849
  const extraFieldUnicodePath = extraField.get(EXTRAFIELD_TYPE_UNICODE_PATH);
@@ -5842,6 +5901,9 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
5842
5901
  if (extraFieldUSDZ) {
5843
5902
  directory.extraFieldUSDZ = extraFieldUSDZ;
5844
5903
  }
5904
+ if (extraFieldZip64Missing) {
5905
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5906
+ }
5845
5907
  return malformedExtraField;
5846
5908
  }
5847
5909
 
@@ -6056,13 +6118,7 @@ async function detectOverlappingEntry({
6056
6118
  let dataDescriptorLength = 0;
6057
6119
  if (dataDescriptor) {
6058
6120
  const zip64 = Boolean(extraFieldZip64);
6059
- const dataDescriptorArray = await readUint8Array(reader, dataOffset + compressedSize, DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH);
6060
- const dataDescriptorView = getDataView(dataDescriptorArray);
6061
- const candidates = [[zip64, true], [zip64, false], [!zip64, true], [!zip64, false]]
6062
- .map(([zip64Layout, signature]) => readDataDescriptor(dataDescriptorView, zip64Layout, signature))
6063
- .filter(candidate => candidate && candidate.compressedSize == compressedSize && candidate.uncompressedSize == uncompressedSize);
6064
- const localDataDescriptor = candidates.find(candidate => candidate.crc32 == crc32) || candidates[0] ||
6065
- readDataDescriptor(dataDescriptorView, zip64, true) || readDataDescriptor(dataDescriptorView, zip64, false);
6121
+ const localDataDescriptor = await readLocalDataDescriptor(reader, dataOffset + compressedSize, { crc32, compressedSize, uncompressedSize, zip64 });
6066
6122
  if (localDataDescriptor) {
6067
6123
  fileEntry.localDirectory.dataDescriptor = localDataDescriptor;
6068
6124
  dataDescriptorLength = getDataDescriptorLength(localDataDescriptor.zip64, localDataDescriptor.signature);
@@ -6132,6 +6188,25 @@ function mergeRanges(sortedRanges, pendingRanges) {
6132
6188
  return mergedRanges;
6133
6189
  }
6134
6190
 
6191
+ async function readLocalDataDescriptor(reader, offset, { crc32, compressedSize, uncompressedSize, zip64 }) {
6192
+ const maxLength = DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH;
6193
+ const length = Math.min(maxLength, Math.max(0, reader.size - offset));
6194
+ const dataDescriptorArray = length ? await readUint8Array(reader, offset, length) : EMPTY_UINT8_ARRAY;
6195
+ const dataDescriptorView = getDataView(dataDescriptorArray);
6196
+ const candidates = [[zip64, true], [zip64, false], [!zip64, true], [!zip64, false]]
6197
+ .map(([zip64Layout, signature]) => readDataDescriptor(dataDescriptorView, zip64Layout, signature))
6198
+ .filter(candidate => candidate && candidate.compressedSize == compressedSize && candidate.uncompressedSize == uncompressedSize);
6199
+ return candidates.find(candidate => candidate.crc32 == crc32) || candidates[0] ||
6200
+ readDataDescriptor(dataDescriptorView, zip64, true) || readDataDescriptor(dataDescriptorView, zip64, false);
6201
+ }
6202
+
6203
+ async function getEntryDataDescriptorLength(reader, offset, { crc32, compressedSize, uncompressedSize, extraFieldZip64 }) {
6204
+ const dataDescriptor = await readLocalDataDescriptor(reader, offset, { crc32, compressedSize, uncompressedSize, zip64: Boolean(extraFieldZip64) });
6205
+ if (dataDescriptor && dataDescriptor.compressedSize == compressedSize && dataDescriptor.uncompressedSize == uncompressedSize) {
6206
+ return getDataDescriptorLength(dataDescriptor.zip64, dataDescriptor.signature);
6207
+ }
6208
+ }
6209
+
6135
6210
  function readDataDescriptor(dataDescriptorView, zip64, signature) {
6136
6211
  const offset = signature ? DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH : 0;
6137
6212
  if (dataDescriptorView.byteLength < getDataDescriptorLength(zip64, signature) ||
@@ -6391,6 +6466,18 @@ function validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, chec
6391
6466
  }
6392
6467
  }
6393
6468
 
6469
+ function validateDataDescriptor(zipEntry, localDirectory, warnings) {
6470
+ const { dataDescriptor } = localDirectory;
6471
+ const maskedLocalDirectory = zipEntry.decryptedDirectory &&
6472
+ (localDirectory.rawBitFlag & BITFLAG_MASKED_LOCAL_HEADERS) == BITFLAG_MASKED_LOCAL_HEADERS;
6473
+ if (dataDescriptor && !maskedLocalDirectory &&
6474
+ ((zipEntry.crc32 !== UNDEFINED_VALUE && dataDescriptor.crc32 != zipEntry.crc32) ||
6475
+ dataDescriptor.compressedSize != zipEntry.compressedSize ||
6476
+ dataDescriptor.uncompressedSize != zipEntry.uncompressedSize)) {
6477
+ reportAmbiguity(!warnings, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES);
6478
+ }
6479
+ }
6480
+
6394
6481
  function reportAmbiguity(reject, warnings, reason) {
6395
6482
  if (reject) {
6396
6483
  throwAmbiguousArchive(reason);
@@ -6493,6 +6580,7 @@ var zipReader = /*#__PURE__*/Object.freeze({
6493
6580
  WARNING_WRAPPED_ENTRIES_COUNT: WARNING_WRAPPED_ENTRIES_COUNT,
6494
6581
  ZipReader: ZipReader,
6495
6582
  ZipReaderStream: ZipReaderStream,
6583
+ getEntryDataDescriptorLength: getEntryDataDescriptorLength,
6496
6584
  isZipFile: isZipFile
6497
6585
  });
6498
6586
 
@@ -6616,136 +6704,7 @@ class ZipWriter {
6616
6704
  }
6617
6705
 
6618
6706
  appendZip(reader, options = {}) {
6619
- return watchPromiseError(this, this.appendZipEntries(reader, options));
6620
- }
6621
-
6622
- async appendZipEntries(reader, options = {}) {
6623
- const zipWriter = this;
6624
- const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
6625
- const filter = checkFunctionOption(options.filter);
6626
- while (pendingAddFileCalls.size) {
6627
- await Promise.allSettled(Array.from(pendingAddFileCalls));
6628
- }
6629
- let resolveAppendZip;
6630
- const promiseAppendZip = new Promise(resolve => resolveAppendZip = resolve);
6631
- pendingAddFileCalls.add(promiseAppendZip);
6632
- const appendedFilenames = [];
6633
- let releaseLockWriter;
6634
- try {
6635
- reader = new GenericReader(reader);
6636
- await initStream(reader);
6637
- if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
6638
- reader = new BlobReader(await streamToBlob(reader.readable));
6639
- await initStream(reader);
6640
- }
6641
- const { ZipReader } = await Promise.resolve().then(function () { return zipReader; });
6642
- const zipReader$1 = new ZipReader(reader);
6643
- const entries = await zipReader$1.getEntries();
6644
- const keptEntries = [];
6645
- for (const entry of entries) {
6646
- if (!filter || await filter(entry)) {
6647
- keptEntries.push(entry);
6648
- }
6649
- }
6650
- await zipReader$1.close();
6651
- await initStream(zipWriter.writer);
6652
- const { directoryOffset } = zipReader$1;
6653
- keptEntries.forEach(({ filename }) => {
6654
- if (filenames.has(filename)) {
6655
- throw new Error(ERR_DUPLICATED_NAME);
6656
- }
6657
- filenames.add(filename);
6658
- appendedFilenames.push(filename);
6659
- });
6660
- zipWriter.writerLocked = true;
6661
- const { lockWriter } = zipWriter;
6662
- zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
6663
- zipWriter.writerLocked = false;
6664
- resolve();
6665
- });
6666
- await lockWriter;
6667
- if (zipWriter.addSplitZipSignature) {
6668
- delete zipWriter.addSplitZipSignature;
6669
- if (!await startsWithSplitZipSignature(reader)) {
6670
- await writeData(zipWriter.writer, getSplitZipSignatureArray());
6671
- zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6672
- }
6673
- }
6674
- const entryPositions = await copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, Boolean(filter));
6675
- keptEntries.forEach(entry => {
6676
- const {
6677
- version,
6678
- rawLastModDate,
6679
- rawFilename,
6680
- bitFlag,
6681
- encrypted,
6682
- uncompressedSize,
6683
- compressedSize,
6684
- extraFieldZip64
6685
- } = entry;
6686
- let {
6687
- compressionMethod,
6688
- rawExtraField,
6689
- } = entry;
6690
- const { level, languageEncodingFlag, dataDescriptor } = bitFlag;
6691
- rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
6692
- if (entry.extraFieldAES) {
6693
- compressionMethod = COMPRESSION_METHOD_AES;
6694
- }
6695
- const extraFieldLength = getLength(rawExtraField);
6696
- const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
6697
- const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
6698
- const bitFlagValue = (getBitFlag(level, languageEncodingFlag, dataDescriptor, encrypted, compressionMethod) & ~BITFLAG_LEVEL) | (level << 1);
6699
- const {
6700
- headerArray,
6701
- headerView
6702
- } = getHeaderArrayData({
6703
- version,
6704
- bitFlag: bitFlagValue,
6705
- compressionMethod,
6706
- uncompressedSize,
6707
- compressedSize,
6708
- rawLastModDate,
6709
- rawFilename,
6710
- zip64CompressedSize,
6711
- zip64UncompressedSize,
6712
- extraFieldLength
6713
- });
6714
- const { crc32 } = entry;
6715
- if (crc32 !== UNDEFINED_VALUE) {
6716
- setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
6717
- }
6718
- const { offset, diskNumberStart } = entryPositions.get(entry);
6719
- Object.assign(entry, {
6720
- zip64Enabled: true,
6721
- zip64UncompressedSize,
6722
- zip64CompressedSize,
6723
- offset,
6724
- diskNumberStart,
6725
- zip64DiskNumberStart: false,
6726
- rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
6727
- rawExtraFieldAES: EMPTY_UINT8_ARRAY,
6728
- rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
6729
- rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
6730
- rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
6731
- rawCentralExtraFieldUnix: EMPTY_UINT8_ARRAY,
6732
- rawExtraField,
6733
- rawCentralExtraField: EMPTY_UINT8_ARRAY,
6734
- headerArray,
6735
- headerView
6736
- });
6737
- fileEntries.set(entry.filename, entry);
6738
- });
6739
- } catch (error) {
6740
- appendedFilenames.forEach(filename => filenames.delete(filename));
6741
- throw error;
6742
- } finally {
6743
- resolveAppendZip();
6744
- pendingAddFileCalls.delete(promiseAppendZip);
6745
- if (releaseLockWriter) {
6746
- releaseLockWriter();
6747
- }
6748
- }
6707
+ return watchPromiseError(this, appendZipEntries(this, reader, options));
6749
6708
  }
6750
6709
 
6751
6710
  add(name = "", reader, options = {}) {
@@ -6907,11 +6866,140 @@ function watchPromiseError(zipWriter, promise) {
6907
6866
  return watchedPromise;
6908
6867
  }
6909
6868
 
6869
+ async function appendZipEntries(zipWriter, reader, options = {}) {
6870
+ const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
6871
+ const filter = checkFunctionOption(options.filter);
6872
+ while (pendingAddFileCalls.size) {
6873
+ await Promise.allSettled(Array.from(pendingAddFileCalls));
6874
+ }
6875
+ let resolveAppendZip;
6876
+ const promiseAppendZip = new Promise(resolve => resolveAppendZip = resolve);
6877
+ pendingAddFileCalls.add(promiseAppendZip);
6878
+ const appendedFilenames = [];
6879
+ let releaseLockWriter;
6880
+ try {
6881
+ reader = new GenericReader(reader);
6882
+ await initStream(reader);
6883
+ if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
6884
+ reader = new BlobReader(await streamToBlob(reader.readable));
6885
+ await initStream(reader);
6886
+ }
6887
+ const { ZipReader, getEntryDataDescriptorLength } = await Promise.resolve().then(function () { return zipReader; });
6888
+ const zipReader$1 = new ZipReader(reader);
6889
+ const entries = await zipReader$1.getEntries();
6890
+ const keptEntries = [];
6891
+ for (const entry of entries) {
6892
+ if (!filter || await filter(entry)) {
6893
+ keptEntries.push(entry);
6894
+ }
6895
+ }
6896
+ await zipReader$1.close();
6897
+ await initStream(zipWriter.writer);
6898
+ const { directoryOffset, entriesMissingZip64ExtraField } = zipReader$1;
6899
+ keptEntries.forEach(entry => {
6900
+ const { filename } = entry;
6901
+ if (entriesMissingZip64ExtraField.has(entry)) {
6902
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
6903
+ }
6904
+ if (filenames.has(filename)) {
6905
+ throw new Error(ERR_DUPLICATED_NAME);
6906
+ }
6907
+ filenames.add(filename);
6908
+ appendedFilenames.push(filename);
6909
+ });
6910
+ zipWriter.writerLocked = true;
6911
+ const { lockWriter } = zipWriter;
6912
+ zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
6913
+ zipWriter.writerLocked = false;
6914
+ resolve();
6915
+ });
6916
+ await lockWriter;
6917
+ if (zipWriter.addSplitZipSignature) {
6918
+ delete zipWriter.addSplitZipSignature;
6919
+ if (filter || !await startsWithSplitZipSignature(reader)) {
6920
+ await writeData(zipWriter.writer, getSplitZipSignatureArray());
6921
+ zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6922
+ }
6923
+ }
6924
+ const entryPositions = await copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, Boolean(filter), getEntryDataDescriptorLength);
6925
+ keptEntries.forEach(entry => {
6926
+ const {
6927
+ version,
6928
+ rawLastModDate,
6929
+ rawFilename,
6930
+ rawBitFlag,
6931
+ uncompressedSize,
6932
+ compressedSize,
6933
+ extraFieldZip64
6934
+ } = entry;
6935
+ let {
6936
+ compressionMethod,
6937
+ rawExtraField,
6938
+ } = entry;
6939
+ rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
6940
+ if (entry.extraFieldAES) {
6941
+ compressionMethod = COMPRESSION_METHOD_AES;
6942
+ }
6943
+ const extraFieldLength = getLength(rawExtraField);
6944
+ const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
6945
+ const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
6946
+ const {
6947
+ headerArray,
6948
+ headerView
6949
+ } = getHeaderArrayData({
6950
+ version,
6951
+ bitFlag: rawBitFlag,
6952
+ compressionMethod,
6953
+ uncompressedSize,
6954
+ compressedSize,
6955
+ rawLastModDate,
6956
+ rawFilename,
6957
+ zip64CompressedSize,
6958
+ zip64UncompressedSize,
6959
+ extraFieldLength
6960
+ });
6961
+ const { crc32 } = entry;
6962
+ if (crc32 !== UNDEFINED_VALUE) {
6963
+ setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
6964
+ }
6965
+ const { offset, diskNumberStart } = entryPositions.get(entry);
6966
+ Object.assign(entry, {
6967
+ zip64Enabled: true,
6968
+ zip64UncompressedSize,
6969
+ zip64CompressedSize,
6970
+ offset,
6971
+ diskNumberStart,
6972
+ zip64DiskNumberStart: false,
6973
+ rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
6974
+ rawExtraFieldAES: EMPTY_UINT8_ARRAY,
6975
+ rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
6976
+ rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
6977
+ rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
6978
+ rawCentralExtraFieldUnix: EMPTY_UINT8_ARRAY,
6979
+ rawExtraField,
6980
+ rawCentralExtraField: EMPTY_UINT8_ARRAY,
6981
+ headerArray,
6982
+ headerView
6983
+ });
6984
+ fileEntries.set(entry.filename, entry);
6985
+ });
6986
+ } catch (error) {
6987
+ appendedFilenames.forEach(filename => filenames.delete(filename));
6988
+ throw error;
6989
+ } finally {
6990
+ resolveAppendZip();
6991
+ pendingAddFileCalls.delete(promiseAppendZip);
6992
+ if (releaseLockWriter) {
6993
+ releaseLockWriter();
6994
+ }
6995
+ }
6996
+ }
6997
+
6910
6998
  async function prependZipEntries(zipWriter, reader) {
6911
6999
  if (zipWriter.filenames.size) {
6912
7000
  throw new Error(ERR_ZIP_NOT_EMPTY);
6913
7001
  }
6914
- await zipWriter.appendZipEntries(reader);
7002
+ await appendZipEntries(zipWriter, reader);
6915
7003
  }
6916
7004
 
6917
7005
  async function addFileEntry(zipWriter, name, reader, options) {
@@ -8683,7 +8771,7 @@ function removeExtraFieldZip64(rawExtraField) {
8683
8771
  return rawExtraField;
8684
8772
  }
8685
8773
 
8686
- async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, compact) {
8774
+ async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, compact, getEntryDataDescriptorLength) {
8687
8775
  const { writer } = zipWriter;
8688
8776
  const entryPositions = new Map();
8689
8777
  if (writer.closeDisk || compact) {
@@ -8691,6 +8779,27 @@ async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOff
8691
8779
  getSourceOffset(reader, firstEntry) - getSourceOffset(reader, secondEntry));
8692
8780
  const regionEnds = getRegionEnds(reader, sortedEntries, directoryOffset);
8693
8781
  const keptEntrySet = new Set(keptEntries);
8782
+ const localHeaderLengths = new Map();
8783
+ for (let indexEntry = 0; indexEntry < sortedEntries.length; indexEntry++) {
8784
+ const entry = sortedEntries[indexEntry];
8785
+ if (keptEntrySet.has(entry)) {
8786
+ const sourceOffset = getSourceOffset(reader, entry);
8787
+ const localHeaderLength = await getLocalHeaderLength(reader, sourceOffset);
8788
+ if (localHeaderLength === UNDEFINED_VALUE) {
8789
+ throw new Error(ERR_LOCAL_FILE_HEADER_NOT_FOUND);
8790
+ }
8791
+ let entryEnd = sourceOffset + localHeaderLength + entry.compressedSize;
8792
+ if (entry.bitFlag.dataDescriptor) {
8793
+ const dataDescriptorLength = await getEntryDataDescriptorLength(reader, entryEnd, entry);
8794
+ entryEnd = dataDescriptorLength === UNDEFINED_VALUE ? Math.max(entryEnd, regionEnds[indexEntry]) : entryEnd + dataDescriptorLength;
8795
+ }
8796
+ if (entryEnd > regionEnds[indexEntry]) {
8797
+ throw new Error(ERR_OVERLAPPING_ENTRY);
8798
+ }
8799
+ regionEnds[indexEntry] = entryEnd;
8800
+ localHeaderLengths.set(entry, localHeaderLength);
8801
+ }
8802
+ }
8694
8803
  let copiedOffset = 0;
8695
8804
  let lastPosition;
8696
8805
  for (let indexEntry = 0; indexEntry < sortedEntries.length; indexEntry++) {
@@ -8703,7 +8812,7 @@ async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOff
8703
8812
  if (!compact) {
8704
8813
  await copyData(zipWriter, reader, copiedOffset, sourceOffset - copiedOffset);
8705
8814
  }
8706
- if (writer.closeDisk && exceedsAvailableSize(writer, await getLocalHeaderLength(reader, sourceOffset))) {
8815
+ if (writer.closeDisk && exceedsAvailableSize(writer, localHeaderLengths.get(entry))) {
8707
8816
  await writer.closeDisk();
8708
8817
  }
8709
8818
  lastPosition = {
@@ -8737,7 +8846,7 @@ function getRegionEnds(reader, sortedEntries, directoryOffset) {
8737
8846
  const sourceOffset = getSourceOffset(reader, sortedEntries[indexEntry]);
8738
8847
  const nextEntry = sortedEntries[indexEntry + 1];
8739
8848
  if (nextEntry && getSourceOffset(reader, nextEntry) > sourceOffset) {
8740
- regionEnd = getSourceOffset(reader, nextEntry);
8849
+ regionEnd = Math.min(getSourceOffset(reader, nextEntry), directoryOffset);
8741
8850
  }
8742
8851
  regionEnds[indexEntry] = regionEnd;
8743
8852
  }
@@ -8751,11 +8860,13 @@ async function copyData(zipWriter, reader, offset, size) {
8751
8860
  try {
8752
8861
  await flushBufferedData(createReadable(reader, { offset, size }), writer, UNDEFINED_VALUE, chunkLength => copiedLength += chunkLength);
8753
8862
  } catch (error) {
8754
- zipWriter.hasCorruptedEntries = true;
8755
- try {
8756
- error.corruptedEntry = true;
8757
- } catch {
8758
- // ignored
8863
+ if (copiedLength) {
8864
+ zipWriter.hasCorruptedEntries = true;
8865
+ try {
8866
+ error.corruptedEntry = true;
8867
+ } catch {
8868
+ // ignored
8869
+ }
8759
8870
  }
8760
8871
  throw error;
8761
8872
  } finally {
@@ -8766,11 +8877,16 @@ async function copyData(zipWriter, reader, offset, size) {
8766
8877
  }
8767
8878
 
8768
8879
  async function getLocalHeaderLength(reader, offset) {
8769
- const headerArray = await readUint8Array(reader, offset, HEADER_SIZE);
8880
+ const headerArray = offset >= 0 && offset + HEADER_SIZE <= reader.size ?
8881
+ await readUint8Array(reader, offset, HEADER_SIZE) :
8882
+ EMPTY_UINT8_ARRAY;
8770
8883
  if (getLength(headerArray) < HEADER_SIZE) {
8771
- return HEADER_SIZE;
8884
+ return UNDEFINED_VALUE;
8772
8885
  }
8773
8886
  const headerView = getDataView(headerArray);
8887
+ if (getUint32(headerView, 0) != LOCAL_FILE_HEADER_SIGNATURE) {
8888
+ return UNDEFINED_VALUE;
8889
+ }
8774
8890
  return HEADER_SIZE +
8775
8891
  getUint16(headerView, HEADER_OFFSET_FILENAME_LENGTH + LOCAL_HEADER_COMMON_OFFSET) +
8776
8892
  getUint16(headerView, HEADER_OFFSET_EXTRAFIELD_LENGTH + LOCAL_HEADER_COMMON_OFFSET);
@@ -9121,7 +9237,7 @@ function formatSupported(StreamClass, format) {
9121
9237
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
9122
9238
  */
9123
9239
 
9124
- const VERSION = "2.20.0";
9240
+ const VERSION = "2.21.0";
9125
9241
 
9126
9242
  /*
9127
9243
  Copyright (c) 2025 Gildas Lormeau. All rights reserved.