@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-native.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";
@@ -4771,12 +4775,9 @@ const ERR_BAD_FORMAT = "File format is not recognized";
4771
4775
  const ERR_EOCDR_NOT_FOUND = "End of central directory not found";
4772
4776
  const ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND = "End of Zip64 central directory locator not found";
4773
4777
  const ERR_CENTRAL_DIRECTORY_NOT_FOUND = "Central directory header not found";
4774
- const ERR_LOCAL_FILE_HEADER_NOT_FOUND = "Local file header not found";
4775
- const ERR_EXTRAFIELD_ZIP64_NOT_FOUND = "Zip64 extra field not found";
4776
4778
  const ERR_ENCRYPTED = "File contains encrypted entry";
4777
4779
  const ERR_UNSUPPORTED_ENCRYPTION = "Encryption method not supported";
4778
4780
  const ERR_SPLIT_ZIP_FILE = "Split zip file";
4779
- const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
4780
4781
  const ERR_ENTRY_DATA_OUT_OF_BOUNDS = "Entry data out of bounds";
4781
4782
  const ERR_AMBIGUOUS_ARCHIVE = "Ambiguous archive";
4782
4783
  const ERR_ENCRYPTED_CENTRAL_DIRECTORY = "Encrypted central directory is not supported";
@@ -4841,7 +4842,8 @@ class ZipReader {
4841
4842
  Object.assign(this, {
4842
4843
  reader: new GenericReader(reader),
4843
4844
  options,
4844
- readRanges: { indexes: new Set(), sortedRanges: [], pendingRanges: [] }
4845
+ readRanges: { indexes: new Set(), sortedRanges: [], pendingRanges: [] },
4846
+ entriesMissingZip64ExtraField: new WeakSet()
4845
4847
  });
4846
4848
  }
4847
4849
 
@@ -4912,16 +4914,22 @@ class ZipReader {
4912
4914
  directoryDataOffset = getDiskOffset$1(reader, getUint32$1(endOfDirectoryLocatorView, 4)) + getBigUint64(endOfDirectoryLocatorView, 8);
4913
4915
  let endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4914
4916
  let endOfDirectoryView = getDataView(endOfDirectoryArray);
4915
- const expectedDirectoryDataOffset = endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH - ZIP64_END_OF_CENTRAL_DIR_LENGTH;
4916
- if ((endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) &&
4917
- directoryDataOffset != expectedDirectoryDataOffset && expectedDirectoryDataOffset >= 0) {
4918
- const originalDirectoryDataOffset = directoryDataOffset;
4919
- directoryDataOffset = expectedDirectoryDataOffset;
4920
- if (directoryDataOffset > originalDirectoryDataOffset) {
4921
- prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
4917
+ if (endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
4918
+ const locatorOffset = endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH;
4919
+ let expectedDirectoryDataOffset = locatorOffset - ZIP64_END_OF_CENTRAL_DIR_LENGTH;
4920
+ if (!await startsWithSignature(reader, expectedDirectoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_SIGNATURE)) {
4921
+ expectedDirectoryDataOffset = await findZip64EndOfDirectoryOffset(reader, locatorOffset);
4922
+ }
4923
+ if (expectedDirectoryDataOffset !== UNDEFINED_VALUE && expectedDirectoryDataOffset >= 0 &&
4924
+ directoryDataOffset != expectedDirectoryDataOffset) {
4925
+ const originalDirectoryDataOffset = directoryDataOffset;
4926
+ directoryDataOffset = expectedDirectoryDataOffset;
4927
+ if (directoryDataOffset > originalDirectoryDataOffset) {
4928
+ prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
4929
+ }
4930
+ endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4931
+ endOfDirectoryView = getDataView(endOfDirectoryArray);
4922
4932
  }
4923
- endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH);
4924
- endOfDirectoryView = getDataView(endOfDirectoryArray);
4925
4933
  }
4926
4934
  if (endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32$1(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
4927
4935
  throw new Error(ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND);
@@ -4966,10 +4974,6 @@ class ZipReader {
4966
4974
  let declaredDirectoryDataLength = directoryDataLength;
4967
4975
  const centralDirectoryEndOffset = endOfDirectoryInfo.offset -
4968
4976
  (zip64EndOfDirectory ? zip64EndOfDirectoryLength + ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH : 0);
4969
- if (directoryDataOffset >= reader.size) {
4970
- prependedDataLength = reader.size - directoryDataOffset - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
4971
- directoryDataOffset = reader.size - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
4972
- }
4973
4977
  if (expectedLastDiskNumber != lastDiskNumber) {
4974
4978
  throw new Error(ERR_SPLIT_ZIP_FILE);
4975
4979
  }
@@ -4977,52 +4981,64 @@ class ZipReader {
4977
4981
  throw new Error(ERR_BAD_FORMAT);
4978
4982
  }
4979
4983
  let offset = 0;
4980
- let directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
4984
+ let directoryArray = directoryDataOffset < reader.size ?
4985
+ await readUint8Array(reader, directoryDataOffset, directoryDataLength) :
4986
+ EMPTY_UINT8_ARRAY;
4981
4987
  let directoryView = getDataView(directoryArray);
4982
4988
  if (directoryDataLength) {
4983
- if (directoryArray.length < 4) {
4984
- throw new Error(ERR_BAD_FORMAT);
4985
- }
4986
4989
  let expectedDirectoryDataOffset = centralDirectoryEndOffset - directoryDataLength;
4987
4990
  if (directoryDataOffset != expectedDirectoryDataOffset && diskNumber == lastDiskNumber) {
4988
- const storedPointsAtDirectory = getUint32$1(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
4991
+ const storedPointsAtDirectory = directoryArray.length >= 4 && (
4992
+ getUint32$1(directoryView, offset) == CENTRAL_FILE_HEADER_SIGNATURE ||
4989
4993
  Boolean(directoryEncryptionInfo && directoryEncryptionInfo.compressedSize) ||
4990
- detectEncryptedCentralDirectory(directoryView);
4994
+ detectEncryptedCentralDirectory(directoryView));
4995
+ if (!storedPointsAtDirectory && expectedDirectoryDataOffset < 0) {
4996
+ throw new Error(ERR_BAD_FORMAT);
4997
+ }
4991
4998
  let reconcile = !storedPointsAtDirectory;
4992
4999
  if (expectedDirectoryDataOffset >= 0 && expectedDirectoryDataOffset + 4 <= reader.size) {
4993
- const expectedPointsAtDirectory = await pointsAtDirectory(reader, expectedDirectoryDataOffset);
4994
- if (storedPointsAtDirectory) {
4995
- reconcile = expectedPointsAtDirectory;
4996
- } else if (!expectedPointsAtDirectory) {
5000
+ let expectedPointsAtDirectory = await pointsAtDirectory(reader, expectedDirectoryDataOffset);
5001
+ if (!expectedPointsAtDirectory) {
4997
5002
  const signatureRecordOffset = await findDigitalSignatureRecordOffset(reader, centralDirectoryEndOffset);
4998
5003
  const signedDirectoryDataOffset = signatureRecordOffset - directoryDataLength;
4999
5004
  if (signatureRecordOffset !== UNDEFINED_VALUE && signedDirectoryDataOffset >= 0 &&
5000
5005
  await pointsAtDirectory(reader, signedDirectoryDataOffset)) {
5001
5006
  expectedDirectoryDataOffset = signedDirectoryDataOffset;
5007
+ expectedPointsAtDirectory = true;
5002
5008
  }
5003
5009
  }
5010
+ if (storedPointsAtDirectory) {
5011
+ reconcile = expectedPointsAtDirectory;
5012
+ }
5004
5013
  }
5005
- if (reconcile) {
5014
+ if (reconcile && expectedDirectoryDataOffset != directoryDataOffset) {
5006
5015
  const originalDirectoryDataOffset = directoryDataOffset;
5007
5016
  directoryDataOffset = expectedDirectoryDataOffset;
5008
5017
  directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength);
5009
5018
  directoryView = getDataView(directoryArray);
5010
- if (directoryDataOffset > originalDirectoryDataOffset) {
5011
- prependedDataLength += directoryDataOffset - originalDirectoryDataOffset;
5019
+ const offsetDelta = directoryDataOffset - originalDirectoryDataOffset;
5020
+ const localHeaderOffset = getFirstLocalHeaderOffset(directoryArray, directoryView);
5021
+ const localHeaderFound = localHeaderOffset !== UNDEFINED_VALUE &&
5022
+ await startsWithSignature(reader, localHeaderOffset, LOCAL_FILE_HEADER_SIGNATURE);
5023
+ const shiftedLocalHeaderFound = localHeaderOffset !== UNDEFINED_VALUE &&
5024
+ await startsWithSignature(reader, localHeaderOffset + offsetDelta, LOCAL_FILE_HEADER_SIGNATURE);
5025
+ if (offsetDelta > 0 && !(localHeaderFound && !shiftedLocalHeaderFound)) {
5026
+ prependedDataLength += offsetDelta;
5012
5027
  prependedCentralDirectory = storedPointsAtDirectory;
5013
5028
  } else {
5014
5029
  reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_CENTRAL_DIRECTORY_OFFSET);
5015
- const offsetDelta = directoryDataOffset - originalDirectoryDataOffset;
5016
- if (directoryArray.length >= CENTRAL_FILE_HEADER_LENGTH) {
5017
- const localHeaderOffset = getUint32$1(directoryView, 42);
5018
- if (!await startsWithSignature(reader, localHeaderOffset, LOCAL_FILE_HEADER_SIGNATURE) &&
5019
- await startsWithSignature(reader, localHeaderOffset + offsetDelta, LOCAL_FILE_HEADER_SIGNATURE)) {
5020
- prependedDataLength += offsetDelta;
5021
- }
5030
+ if (offsetDelta < 0 && !localHeaderFound && shiftedLocalHeaderFound) {
5031
+ prependedDataLength += offsetDelta;
5022
5032
  }
5023
5033
  }
5024
5034
  }
5025
5035
  }
5036
+ if (directoryArray.length < 4) {
5037
+ throw new Error(ERR_BAD_FORMAT);
5038
+ }
5039
+ } else if (!filesLength && directoryDataOffset < centralDirectoryEndOffset && diskNumber == lastDiskNumber) {
5040
+ prependedDataLength += centralDirectoryEndOffset - directoryDataOffset;
5041
+ directoryDataOffset = centralDirectoryEndOffset;
5026
5042
  }
5027
5043
  const expectedDirectoryDataLength = centralDirectoryEndOffset - directoryDataOffset;
5028
5044
  if (directoryDataLength != expectedDirectoryDataLength && expectedDirectoryDataLength >= 0 && diskNumber == lastDiskNumber) {
@@ -5064,8 +5080,10 @@ class ZipReader {
5064
5080
  addWarning(warnings, WARNING_WRAPPED_ENTRIES_COUNT);
5065
5081
  }
5066
5082
  }
5083
+ const entryOptions = Object.assign({}, zipReader.options, options, { [OPTION_STRICTNESS]: strictness });
5084
+ delete entryOptions[OPTION_CHECK_AMBIGUITY];
5067
5085
  for (let indexFile = 0; indexFile < filesLength; indexFile++) {
5068
- const fileEntry = new ZipEntry$1(reader, zipReader.options);
5086
+ const fileEntry = new ZipEntry$1(reader, entryOptions);
5069
5087
  if (offset + CENTRAL_FILE_HEADER_LENGTH > directoryArray.length || getUint32$1(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE) {
5070
5088
  if (indexFile == 0 && !decryptedDirectory && (zip64EndOfDirectoryVersion2 || detectEncryptedCentralDirectory(directoryView))) {
5071
5089
  throw new Error(ERR_ENCRYPTED_CENTRAL_DIRECTORY);
@@ -5156,13 +5174,17 @@ class ZipReader {
5156
5174
  if (malformedExtraField) {
5157
5175
  addWarning(warnings, WARNING_MALFORMED_EXTRA_FIELD, filename);
5158
5176
  }
5159
- fileEntry.offset += prependedDataLength;
5160
- const entryPosition = getDiskOffset$1(reader, fileEntry.diskNumberStart) + fileEntry.offset;
5161
- startOffset = Math.min(entryPosition, startOffset);
5162
- if (entryPosition < previousEntryPosition) {
5163
- addWarning(warnings, WARNING_UNSORTED_CENTRAL_DIRECTORY, filename);
5177
+ if (fileEntry.zip64ExtraFieldMissing && fileEntry.offset == MAX_32_BITS) {
5178
+ startOffset = 0;
5179
+ } else {
5180
+ fileEntry.offset += prependedDataLength;
5181
+ const entryPosition = getDiskOffset$1(reader, fileEntry.diskNumberStart) + fileEntry.offset;
5182
+ startOffset = Math.min(entryPosition, startOffset);
5183
+ if (entryPosition < previousEntryPosition) {
5184
+ addWarning(warnings, WARNING_UNSORTED_CENTRAL_DIRECTORY, filename);
5185
+ }
5186
+ previousEntryPosition = entryPosition;
5164
5187
  }
5165
- previousEntryPosition = entryPosition;
5166
5188
  if ((fileEntry.version & MAX_8_BITS) > MAX_KNOWN_VERSION) {
5167
5189
  addWarning(warnings, WARNING_UNKNOWN_VERSION, filename);
5168
5190
  }
@@ -5199,12 +5221,15 @@ class ZipReader {
5199
5221
  zipCrypto: fileEntry.encrypted && !fileEntry.extraFieldAES
5200
5222
  });
5201
5223
  const entry = new Entry(fileEntry);
5202
- entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader.readRanges, options);
5224
+ if (fileEntry.zip64ExtraFieldMissing) {
5225
+ zipReader.entriesMissingZip64ExtraField.add(entry);
5226
+ }
5227
+ entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader, options);
5203
5228
  entry.arrayBuffer = async options => {
5204
5229
  const writer = new TransformStream();
5205
5230
  const arrayBufferPromise = streamToBlob(writer.readable).then(blob => blob.arrayBuffer());
5206
5231
  arrayBufferPromise.catch(() => { });
5207
- await fileEntry.getData(writer, entry, zipReader.readRanges,
5232
+ await fileEntry.getData(writer, entry, zipReader,
5208
5233
  Object.assign({}, options, { preventClose: false }));
5209
5234
  return arrayBufferPromise;
5210
5235
  };
@@ -5412,8 +5437,9 @@ let ZipEntry$1 = class ZipEntry {
5412
5437
  });
5413
5438
  }
5414
5439
 
5415
- async getData(writer, fileEntry, readRanges, options = {}) {
5440
+ async getData(writer, fileEntry, zipReader, options = {}) {
5416
5441
  const zipEntry = this;
5442
+ const { readRanges, directoryOffset } = zipReader;
5417
5443
  if (zipEntry.zip64ExtraFieldMissing) {
5418
5444
  throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5419
5445
  }
@@ -5487,10 +5513,10 @@ let ZipEntry$1 = class ZipEntry {
5487
5513
  }
5488
5514
  validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
5489
5515
  const { lastAccessDate, creationDate, uid, gid } = localDirectory;
5490
- if (lastAccessDate) {
5516
+ if (lastAccessDate && fileEntry.lastAccessDate === UNDEFINED_VALUE) {
5491
5517
  fileEntry.lastAccessDate = lastAccessDate;
5492
5518
  }
5493
- if (creationDate) {
5519
+ if (creationDate && fileEntry.creationDate === UNDEFINED_VALUE) {
5494
5520
  fileEntry.creationDate = creationDate;
5495
5521
  }
5496
5522
  if (uid !== UNDEFINED_VALUE && fileEntry.uid === UNDEFINED_VALUE) {
@@ -5519,7 +5545,7 @@ let ZipEntry$1 = class ZipEntry {
5519
5545
  throw new Error(ERR_ENCRYPTED);
5520
5546
  }
5521
5547
  }
5522
- if (dataOffset + compressedSize > reader.size) {
5548
+ if (dataOffset + compressedSize > Math.min(reader.size, directoryOffset)) {
5523
5549
  throw new Error(ERR_ENTRY_DATA_OUT_OF_BOUNDS);
5524
5550
  }
5525
5551
  const size = compressedSize;
@@ -5585,6 +5611,7 @@ let ZipEntry$1 = class ZipEntry {
5585
5611
  extraFieldZip64: extraFieldZip64 || localDirectory.extraFieldZip64,
5586
5612
  readRanges
5587
5613
  });
5614
+ validateDataDescriptor(zipEntry, localDirectory, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
5588
5615
  }
5589
5616
  let writable, abortError, aborted;
5590
5617
  try {
@@ -5604,10 +5631,7 @@ let ZipEntry$1 = class ZipEntry {
5604
5631
  writer.size += writtenSize;
5605
5632
  }
5606
5633
  } catch (error) {
5607
- const { outputSize: failedOutputSize } = workerOptions;
5608
- if (failedOutputSize !== UNDEFINED_VALUE) {
5609
- writer.size += failedOutputSize;
5610
- } else if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
5634
+ if (isErrorObject(error) && error.outputSize !== UNDEFINED_VALUE) {
5611
5635
  writer.size += error.outputSize;
5612
5636
  }
5613
5637
  if (!checkPasswordOnly || !isErrorObject(error) || error.message != ERR_ABORT_CHECK_PASSWORD) {
@@ -5635,13 +5659,7 @@ let ZipEntry$1 = class ZipEntry {
5635
5659
  };
5636
5660
 
5637
5661
  function detectEncryptedCentralDirectory(directoryView) {
5638
- const maxOffset = Math.min(directoryView.byteLength, 1024) - 3;
5639
- for (let offset = 0; offset < maxOffset; offset++) {
5640
- if (getUint32$1(directoryView, offset) == ARCHIVE_EXTRA_DATA_SIGNATURE) {
5641
- return true;
5642
- }
5643
- }
5644
- return false;
5662
+ return directoryView.byteLength >= 4 && getUint32$1(directoryView, 0) == ARCHIVE_EXTRA_DATA_SIGNATURE;
5645
5663
  }
5646
5664
 
5647
5665
  function getWrappedFilesLength(directoryView, directoryArray, offset) {
@@ -5678,6 +5696,46 @@ async function findDigitalSignatureRecordOffset(reader, endOffset) {
5678
5696
  }
5679
5697
  }
5680
5698
 
5699
+ async function findZip64EndOfDirectoryOffset(reader, endOffset) {
5700
+ const startOffset = Math.max(0, endOffset - (ZIP64_END_OF_CENTRAL_DIR_LENGTH + MAX_16_BITS));
5701
+ const tailArray = await readUint8Array(reader, startOffset, endOffset - startOffset);
5702
+ const tailView = getDataView(tailArray);
5703
+ for (let indexByte = tailArray.length - ZIP64_END_OF_CENTRAL_DIR_LENGTH; indexByte >= 0; indexByte--) {
5704
+ if (getUint32$1(tailView, indexByte) == ZIP64_END_OF_CENTRAL_DIR_SIGNATURE &&
5705
+ indexByte + 12 + Number(tailView.getBigUint64(indexByte + 4, true)) == tailArray.length) {
5706
+ return startOffset + indexByte;
5707
+ }
5708
+ }
5709
+ }
5710
+
5711
+ function getFirstLocalHeaderOffset(directoryArray, directoryView) {
5712
+ if (directoryArray.length < CENTRAL_FILE_HEADER_LENGTH) {
5713
+ return UNDEFINED_VALUE;
5714
+ }
5715
+ const localHeaderOffset = getUint32$1(directoryView, 42);
5716
+ if (localHeaderOffset != MAX_32_BITS) {
5717
+ return localHeaderOffset;
5718
+ }
5719
+ let offsetExtraField = CENTRAL_FILE_HEADER_LENGTH + getUint16$1(directoryView, 28);
5720
+ const endExtraField = Math.min(offsetExtraField + getUint16$1(directoryView, 30), directoryArray.length);
5721
+ while (offsetExtraField + 4 <= endExtraField) {
5722
+ const type = getUint16$1(directoryView, offsetExtraField);
5723
+ const size = getUint16$1(directoryView, offsetExtraField + 2);
5724
+ if (type == EXTRAFIELD_TYPE_ZIP64) {
5725
+ let offsetValue = offsetExtraField + 4;
5726
+ if (getUint32$1(directoryView, 24) == MAX_32_BITS) {
5727
+ offsetValue += 8;
5728
+ }
5729
+ if (getUint32$1(directoryView, 20) == MAX_32_BITS) {
5730
+ offsetValue += 8;
5731
+ }
5732
+ return offsetValue + 8 <= endExtraField ? getBigUint64(directoryView, offsetValue) : UNDEFINED_VALUE;
5733
+ }
5734
+ offsetExtraField += 4 + size;
5735
+ }
5736
+ return UNDEFINED_VALUE;
5737
+ }
5738
+
5681
5739
  function readDigitalSignature(signatureRecordArray) {
5682
5740
  if (signatureRecordArray.length >= 6) {
5683
5741
  const signatureRecordView = getDataView(signatureRecordArray);
@@ -5767,6 +5825,7 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
5767
5825
  uncompressedSize: getUint32$1(dataView, offset + HEADER_OFFSET_UNCOMPRESSED_SIZE)
5768
5826
  });
5769
5827
  const extraFieldZip64 = extraField.get(EXTRAFIELD_TYPE_ZIP64);
5828
+ let extraFieldZip64Missing;
5770
5829
  if (extraFieldZip64) {
5771
5830
  if (!readExtraFieldZip64(extraFieldZip64, directory, localDirectory)) {
5772
5831
  malformedExtraField = true;
@@ -5776,7 +5835,7 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
5776
5835
  if (localDirectory) {
5777
5836
  malformedExtraField = true;
5778
5837
  } else {
5779
- throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5838
+ extraFieldZip64Missing = true;
5780
5839
  }
5781
5840
  }
5782
5841
  const extraFieldUnicodePath = extraField.get(EXTRAFIELD_TYPE_UNICODE_PATH);
@@ -5834,6 +5893,9 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
5834
5893
  if (extraFieldUSDZ) {
5835
5894
  directory.extraFieldUSDZ = extraFieldUSDZ;
5836
5895
  }
5896
+ if (extraFieldZip64Missing) {
5897
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
5898
+ }
5837
5899
  return malformedExtraField;
5838
5900
  }
5839
5901
 
@@ -6048,13 +6110,7 @@ async function detectOverlappingEntry({
6048
6110
  let dataDescriptorLength = 0;
6049
6111
  if (dataDescriptor) {
6050
6112
  const zip64 = Boolean(extraFieldZip64);
6051
- const dataDescriptorArray = await readUint8Array(reader, dataOffset + compressedSize, DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH);
6052
- const dataDescriptorView = getDataView(dataDescriptorArray);
6053
- const candidates = [[zip64, true], [zip64, false], [!zip64, true], [!zip64, false]]
6054
- .map(([zip64Layout, signature]) => readDataDescriptor(dataDescriptorView, zip64Layout, signature))
6055
- .filter(candidate => candidate && candidate.compressedSize == compressedSize && candidate.uncompressedSize == uncompressedSize);
6056
- const localDataDescriptor = candidates.find(candidate => candidate.crc32 == crc32) || candidates[0] ||
6057
- readDataDescriptor(dataDescriptorView, zip64, true) || readDataDescriptor(dataDescriptorView, zip64, false);
6113
+ const localDataDescriptor = await readLocalDataDescriptor(reader, dataOffset + compressedSize, { crc32, compressedSize, uncompressedSize, zip64 });
6058
6114
  if (localDataDescriptor) {
6059
6115
  fileEntry.localDirectory.dataDescriptor = localDataDescriptor;
6060
6116
  dataDescriptorLength = getDataDescriptorLength(localDataDescriptor.zip64, localDataDescriptor.signature);
@@ -6124,6 +6180,25 @@ function mergeRanges(sortedRanges, pendingRanges) {
6124
6180
  return mergedRanges;
6125
6181
  }
6126
6182
 
6183
+ async function readLocalDataDescriptor(reader, offset, { crc32, compressedSize, uncompressedSize, zip64 }) {
6184
+ const maxLength = DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH;
6185
+ const length = Math.min(maxLength, Math.max(0, reader.size - offset));
6186
+ const dataDescriptorArray = length ? await readUint8Array(reader, offset, length) : EMPTY_UINT8_ARRAY;
6187
+ const dataDescriptorView = getDataView(dataDescriptorArray);
6188
+ const candidates = [[zip64, true], [zip64, false], [!zip64, true], [!zip64, false]]
6189
+ .map(([zip64Layout, signature]) => readDataDescriptor(dataDescriptorView, zip64Layout, signature))
6190
+ .filter(candidate => candidate && candidate.compressedSize == compressedSize && candidate.uncompressedSize == uncompressedSize);
6191
+ return candidates.find(candidate => candidate.crc32 == crc32) || candidates[0] ||
6192
+ readDataDescriptor(dataDescriptorView, zip64, true) || readDataDescriptor(dataDescriptorView, zip64, false);
6193
+ }
6194
+
6195
+ async function getEntryDataDescriptorLength(reader, offset, { crc32, compressedSize, uncompressedSize, extraFieldZip64 }) {
6196
+ const dataDescriptor = await readLocalDataDescriptor(reader, offset, { crc32, compressedSize, uncompressedSize, zip64: Boolean(extraFieldZip64) });
6197
+ if (dataDescriptor && dataDescriptor.compressedSize == compressedSize && dataDescriptor.uncompressedSize == uncompressedSize) {
6198
+ return getDataDescriptorLength(dataDescriptor.zip64, dataDescriptor.signature);
6199
+ }
6200
+ }
6201
+
6127
6202
  function readDataDescriptor(dataDescriptorView, zip64, signature) {
6128
6203
  const offset = signature ? DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH : 0;
6129
6204
  if (dataDescriptorView.byteLength < getDataDescriptorLength(zip64, signature) ||
@@ -6383,6 +6458,18 @@ function validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, chec
6383
6458
  }
6384
6459
  }
6385
6460
 
6461
+ function validateDataDescriptor(zipEntry, localDirectory, warnings) {
6462
+ const { dataDescriptor } = localDirectory;
6463
+ const maskedLocalDirectory = zipEntry.decryptedDirectory &&
6464
+ (localDirectory.rawBitFlag & BITFLAG_MASKED_LOCAL_HEADERS) == BITFLAG_MASKED_LOCAL_HEADERS;
6465
+ if (dataDescriptor && !maskedLocalDirectory &&
6466
+ ((zipEntry.crc32 !== UNDEFINED_VALUE && dataDescriptor.crc32 != zipEntry.crc32) ||
6467
+ dataDescriptor.compressedSize != zipEntry.compressedSize ||
6468
+ dataDescriptor.uncompressedSize != zipEntry.uncompressedSize)) {
6469
+ reportAmbiguity(!warnings, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES);
6470
+ }
6471
+ }
6472
+
6386
6473
  function reportAmbiguity(reject, warnings, reason) {
6387
6474
  if (reject) {
6388
6475
  throwAmbiguousArchive(reason);
@@ -6485,6 +6572,7 @@ var zipReader = /*#__PURE__*/Object.freeze({
6485
6572
  WARNING_WRAPPED_ENTRIES_COUNT: WARNING_WRAPPED_ENTRIES_COUNT,
6486
6573
  ZipReader: ZipReader,
6487
6574
  ZipReaderStream: ZipReaderStream,
6575
+ getEntryDataDescriptorLength: getEntryDataDescriptorLength,
6488
6576
  isZipFile: isZipFile
6489
6577
  });
6490
6578
 
@@ -6608,136 +6696,7 @@ class ZipWriter {
6608
6696
  }
6609
6697
 
6610
6698
  appendZip(reader, options = {}) {
6611
- return watchPromiseError(this, this.appendZipEntries(reader, options));
6612
- }
6613
-
6614
- async appendZipEntries(reader, options = {}) {
6615
- const zipWriter = this;
6616
- const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
6617
- const filter = checkFunctionOption(options.filter);
6618
- while (pendingAddFileCalls.size) {
6619
- await Promise.allSettled(Array.from(pendingAddFileCalls));
6620
- }
6621
- let resolveAppendZip;
6622
- const promiseAppendZip = new Promise(resolve => resolveAppendZip = resolve);
6623
- pendingAddFileCalls.add(promiseAppendZip);
6624
- const appendedFilenames = [];
6625
- let releaseLockWriter;
6626
- try {
6627
- reader = new GenericReader(reader);
6628
- await initStream(reader);
6629
- if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
6630
- reader = new BlobReader(await streamToBlob(reader.readable));
6631
- await initStream(reader);
6632
- }
6633
- const { ZipReader } = await Promise.resolve().then(function () { return zipReader; });
6634
- const zipReader$1 = new ZipReader(reader);
6635
- const entries = await zipReader$1.getEntries();
6636
- const keptEntries = [];
6637
- for (const entry of entries) {
6638
- if (!filter || await filter(entry)) {
6639
- keptEntries.push(entry);
6640
- }
6641
- }
6642
- await zipReader$1.close();
6643
- await initStream(zipWriter.writer);
6644
- const { directoryOffset } = zipReader$1;
6645
- keptEntries.forEach(({ filename }) => {
6646
- if (filenames.has(filename)) {
6647
- throw new Error(ERR_DUPLICATED_NAME);
6648
- }
6649
- filenames.add(filename);
6650
- appendedFilenames.push(filename);
6651
- });
6652
- zipWriter.writerLocked = true;
6653
- const { lockWriter } = zipWriter;
6654
- zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
6655
- zipWriter.writerLocked = false;
6656
- resolve();
6657
- });
6658
- await lockWriter;
6659
- if (zipWriter.addSplitZipSignature) {
6660
- delete zipWriter.addSplitZipSignature;
6661
- if (!await startsWithSplitZipSignature(reader)) {
6662
- await writeData(zipWriter.writer, getSplitZipSignatureArray());
6663
- zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6664
- }
6665
- }
6666
- const entryPositions = await copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, Boolean(filter));
6667
- keptEntries.forEach(entry => {
6668
- const {
6669
- version,
6670
- rawLastModDate,
6671
- rawFilename,
6672
- bitFlag,
6673
- encrypted,
6674
- uncompressedSize,
6675
- compressedSize,
6676
- extraFieldZip64
6677
- } = entry;
6678
- let {
6679
- compressionMethod,
6680
- rawExtraField,
6681
- } = entry;
6682
- const { level, languageEncodingFlag, dataDescriptor } = bitFlag;
6683
- rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
6684
- if (entry.extraFieldAES) {
6685
- compressionMethod = COMPRESSION_METHOD_AES;
6686
- }
6687
- const extraFieldLength = getLength(rawExtraField);
6688
- const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
6689
- const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
6690
- const bitFlagValue = (getBitFlag(level, languageEncodingFlag, dataDescriptor, encrypted, compressionMethod) & ~BITFLAG_LEVEL) | (level << 1);
6691
- const {
6692
- headerArray,
6693
- headerView
6694
- } = getHeaderArrayData({
6695
- version,
6696
- bitFlag: bitFlagValue,
6697
- compressionMethod,
6698
- uncompressedSize,
6699
- compressedSize,
6700
- rawLastModDate,
6701
- rawFilename,
6702
- zip64CompressedSize,
6703
- zip64UncompressedSize,
6704
- extraFieldLength
6705
- });
6706
- const { crc32 } = entry;
6707
- if (crc32 !== UNDEFINED_VALUE) {
6708
- setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
6709
- }
6710
- const { offset, diskNumberStart } = entryPositions.get(entry);
6711
- Object.assign(entry, {
6712
- zip64Enabled: true,
6713
- zip64UncompressedSize,
6714
- zip64CompressedSize,
6715
- offset,
6716
- diskNumberStart,
6717
- zip64DiskNumberStart: false,
6718
- rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
6719
- rawExtraFieldAES: EMPTY_UINT8_ARRAY,
6720
- rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
6721
- rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
6722
- rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
6723
- rawCentralExtraFieldUnix: EMPTY_UINT8_ARRAY,
6724
- rawExtraField,
6725
- rawCentralExtraField: EMPTY_UINT8_ARRAY,
6726
- headerArray,
6727
- headerView
6728
- });
6729
- fileEntries.set(entry.filename, entry);
6730
- });
6731
- } catch (error) {
6732
- appendedFilenames.forEach(filename => filenames.delete(filename));
6733
- throw error;
6734
- } finally {
6735
- resolveAppendZip();
6736
- pendingAddFileCalls.delete(promiseAppendZip);
6737
- if (releaseLockWriter) {
6738
- releaseLockWriter();
6739
- }
6740
- }
6699
+ return watchPromiseError(this, appendZipEntries(this, reader, options));
6741
6700
  }
6742
6701
 
6743
6702
  add(name = "", reader, options = {}) {
@@ -6899,11 +6858,140 @@ function watchPromiseError(zipWriter, promise) {
6899
6858
  return watchedPromise;
6900
6859
  }
6901
6860
 
6861
+ async function appendZipEntries(zipWriter, reader, options = {}) {
6862
+ const { pendingAddFileCalls, filenames, fileEntries } = zipWriter;
6863
+ const filter = checkFunctionOption(options.filter);
6864
+ while (pendingAddFileCalls.size) {
6865
+ await Promise.allSettled(Array.from(pendingAddFileCalls));
6866
+ }
6867
+ let resolveAppendZip;
6868
+ const promiseAppendZip = new Promise(resolve => resolveAppendZip = resolve);
6869
+ pendingAddFileCalls.add(promiseAppendZip);
6870
+ const appendedFilenames = [];
6871
+ let releaseLockWriter;
6872
+ try {
6873
+ reader = new GenericReader(reader);
6874
+ await initStream(reader);
6875
+ if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
6876
+ reader = new BlobReader(await streamToBlob(reader.readable));
6877
+ await initStream(reader);
6878
+ }
6879
+ const { ZipReader, getEntryDataDescriptorLength } = await Promise.resolve().then(function () { return zipReader; });
6880
+ const zipReader$1 = new ZipReader(reader);
6881
+ const entries = await zipReader$1.getEntries();
6882
+ const keptEntries = [];
6883
+ for (const entry of entries) {
6884
+ if (!filter || await filter(entry)) {
6885
+ keptEntries.push(entry);
6886
+ }
6887
+ }
6888
+ await zipReader$1.close();
6889
+ await initStream(zipWriter.writer);
6890
+ const { directoryOffset, entriesMissingZip64ExtraField } = zipReader$1;
6891
+ keptEntries.forEach(entry => {
6892
+ const { filename } = entry;
6893
+ if (entriesMissingZip64ExtraField.has(entry)) {
6894
+ throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
6895
+ }
6896
+ if (filenames.has(filename)) {
6897
+ throw new Error(ERR_DUPLICATED_NAME);
6898
+ }
6899
+ filenames.add(filename);
6900
+ appendedFilenames.push(filename);
6901
+ });
6902
+ zipWriter.writerLocked = true;
6903
+ const { lockWriter } = zipWriter;
6904
+ zipWriter.lockWriter = new Promise(resolve => releaseLockWriter = () => {
6905
+ zipWriter.writerLocked = false;
6906
+ resolve();
6907
+ });
6908
+ await lockWriter;
6909
+ if (zipWriter.addSplitZipSignature) {
6910
+ delete zipWriter.addSplitZipSignature;
6911
+ if (filter || !await startsWithSplitZipSignature(reader)) {
6912
+ await writeData(zipWriter.writer, getSplitZipSignatureArray());
6913
+ zipWriter.offset += SPLIT_ZIP_FILE_SIGNATURE_LENGTH;
6914
+ }
6915
+ }
6916
+ const entryPositions = await copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, Boolean(filter), getEntryDataDescriptorLength);
6917
+ keptEntries.forEach(entry => {
6918
+ const {
6919
+ version,
6920
+ rawLastModDate,
6921
+ rawFilename,
6922
+ rawBitFlag,
6923
+ uncompressedSize,
6924
+ compressedSize,
6925
+ extraFieldZip64
6926
+ } = entry;
6927
+ let {
6928
+ compressionMethod,
6929
+ rawExtraField,
6930
+ } = entry;
6931
+ rawExtraField = removeExtraFieldZip64(rawExtraField || EMPTY_UINT8_ARRAY);
6932
+ if (entry.extraFieldAES) {
6933
+ compressionMethod = COMPRESSION_METHOD_AES;
6934
+ }
6935
+ const extraFieldLength = getLength(rawExtraField);
6936
+ const zip64UncompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.uncompressedSize !== UNDEFINED_VALUE;
6937
+ const zip64CompressedSize = Boolean(extraFieldZip64) && extraFieldZip64.compressedSize !== UNDEFINED_VALUE;
6938
+ const {
6939
+ headerArray,
6940
+ headerView
6941
+ } = getHeaderArrayData({
6942
+ version,
6943
+ bitFlag: rawBitFlag,
6944
+ compressionMethod,
6945
+ uncompressedSize,
6946
+ compressedSize,
6947
+ rawLastModDate,
6948
+ rawFilename,
6949
+ zip64CompressedSize,
6950
+ zip64UncompressedSize,
6951
+ extraFieldLength
6952
+ });
6953
+ const { crc32 } = entry;
6954
+ if (crc32 !== UNDEFINED_VALUE) {
6955
+ setUint32(headerView, HEADER_OFFSET_SIGNATURE, crc32);
6956
+ }
6957
+ const { offset, diskNumberStart } = entryPositions.get(entry);
6958
+ Object.assign(entry, {
6959
+ zip64Enabled: true,
6960
+ zip64UncompressedSize,
6961
+ zip64CompressedSize,
6962
+ offset,
6963
+ diskNumberStart,
6964
+ zip64DiskNumberStart: false,
6965
+ rawExtraFieldZip64: EMPTY_UINT8_ARRAY,
6966
+ rawExtraFieldAES: EMPTY_UINT8_ARRAY,
6967
+ rawExtraFieldExtendedTimestamp: EMPTY_UINT8_ARRAY,
6968
+ rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
6969
+ rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
6970
+ rawCentralExtraFieldUnix: EMPTY_UINT8_ARRAY,
6971
+ rawExtraField,
6972
+ rawCentralExtraField: EMPTY_UINT8_ARRAY,
6973
+ headerArray,
6974
+ headerView
6975
+ });
6976
+ fileEntries.set(entry.filename, entry);
6977
+ });
6978
+ } catch (error) {
6979
+ appendedFilenames.forEach(filename => filenames.delete(filename));
6980
+ throw error;
6981
+ } finally {
6982
+ resolveAppendZip();
6983
+ pendingAddFileCalls.delete(promiseAppendZip);
6984
+ if (releaseLockWriter) {
6985
+ releaseLockWriter();
6986
+ }
6987
+ }
6988
+ }
6989
+
6902
6990
  async function prependZipEntries(zipWriter, reader) {
6903
6991
  if (zipWriter.filenames.size) {
6904
6992
  throw new Error(ERR_ZIP_NOT_EMPTY);
6905
6993
  }
6906
- await zipWriter.appendZipEntries(reader);
6994
+ await appendZipEntries(zipWriter, reader);
6907
6995
  }
6908
6996
 
6909
6997
  async function addFileEntry(zipWriter, name, reader, options) {
@@ -8675,7 +8763,7 @@ function removeExtraFieldZip64(rawExtraField) {
8675
8763
  return rawExtraField;
8676
8764
  }
8677
8765
 
8678
- async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, compact) {
8766
+ async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOffset, compact, getEntryDataDescriptorLength) {
8679
8767
  const { writer } = zipWriter;
8680
8768
  const entryPositions = new Map();
8681
8769
  if (writer.closeDisk || compact) {
@@ -8683,6 +8771,27 @@ async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOff
8683
8771
  getSourceOffset(reader, firstEntry) - getSourceOffset(reader, secondEntry));
8684
8772
  const regionEnds = getRegionEnds(reader, sortedEntries, directoryOffset);
8685
8773
  const keptEntrySet = new Set(keptEntries);
8774
+ const localHeaderLengths = new Map();
8775
+ for (let indexEntry = 0; indexEntry < sortedEntries.length; indexEntry++) {
8776
+ const entry = sortedEntries[indexEntry];
8777
+ if (keptEntrySet.has(entry)) {
8778
+ const sourceOffset = getSourceOffset(reader, entry);
8779
+ const localHeaderLength = await getLocalHeaderLength(reader, sourceOffset);
8780
+ if (localHeaderLength === UNDEFINED_VALUE) {
8781
+ throw new Error(ERR_LOCAL_FILE_HEADER_NOT_FOUND);
8782
+ }
8783
+ let entryEnd = sourceOffset + localHeaderLength + entry.compressedSize;
8784
+ if (entry.bitFlag.dataDescriptor) {
8785
+ const dataDescriptorLength = await getEntryDataDescriptorLength(reader, entryEnd, entry);
8786
+ entryEnd = dataDescriptorLength === UNDEFINED_VALUE ? Math.max(entryEnd, regionEnds[indexEntry]) : entryEnd + dataDescriptorLength;
8787
+ }
8788
+ if (entryEnd > regionEnds[indexEntry]) {
8789
+ throw new Error(ERR_OVERLAPPING_ENTRY);
8790
+ }
8791
+ regionEnds[indexEntry] = entryEnd;
8792
+ localHeaderLengths.set(entry, localHeaderLength);
8793
+ }
8794
+ }
8686
8795
  let copiedOffset = 0;
8687
8796
  let lastPosition;
8688
8797
  for (let indexEntry = 0; indexEntry < sortedEntries.length; indexEntry++) {
@@ -8695,7 +8804,7 @@ async function copyZipData(zipWriter, reader, entries, keptEntries, directoryOff
8695
8804
  if (!compact) {
8696
8805
  await copyData(zipWriter, reader, copiedOffset, sourceOffset - copiedOffset);
8697
8806
  }
8698
- if (writer.closeDisk && exceedsAvailableSize(writer, await getLocalHeaderLength(reader, sourceOffset))) {
8807
+ if (writer.closeDisk && exceedsAvailableSize(writer, localHeaderLengths.get(entry))) {
8699
8808
  await writer.closeDisk();
8700
8809
  }
8701
8810
  lastPosition = {
@@ -8729,7 +8838,7 @@ function getRegionEnds(reader, sortedEntries, directoryOffset) {
8729
8838
  const sourceOffset = getSourceOffset(reader, sortedEntries[indexEntry]);
8730
8839
  const nextEntry = sortedEntries[indexEntry + 1];
8731
8840
  if (nextEntry && getSourceOffset(reader, nextEntry) > sourceOffset) {
8732
- regionEnd = getSourceOffset(reader, nextEntry);
8841
+ regionEnd = Math.min(getSourceOffset(reader, nextEntry), directoryOffset);
8733
8842
  }
8734
8843
  regionEnds[indexEntry] = regionEnd;
8735
8844
  }
@@ -8743,11 +8852,13 @@ async function copyData(zipWriter, reader, offset, size) {
8743
8852
  try {
8744
8853
  await flushBufferedData(createReadable(reader, { offset, size }), writer, UNDEFINED_VALUE, chunkLength => copiedLength += chunkLength);
8745
8854
  } catch (error) {
8746
- zipWriter.hasCorruptedEntries = true;
8747
- try {
8748
- error.corruptedEntry = true;
8749
- } catch {
8750
- // ignored
8855
+ if (copiedLength) {
8856
+ zipWriter.hasCorruptedEntries = true;
8857
+ try {
8858
+ error.corruptedEntry = true;
8859
+ } catch {
8860
+ // ignored
8861
+ }
8751
8862
  }
8752
8863
  throw error;
8753
8864
  } finally {
@@ -8758,11 +8869,16 @@ async function copyData(zipWriter, reader, offset, size) {
8758
8869
  }
8759
8870
 
8760
8871
  async function getLocalHeaderLength(reader, offset) {
8761
- const headerArray = await readUint8Array(reader, offset, HEADER_SIZE);
8872
+ const headerArray = offset >= 0 && offset + HEADER_SIZE <= reader.size ?
8873
+ await readUint8Array(reader, offset, HEADER_SIZE) :
8874
+ EMPTY_UINT8_ARRAY;
8762
8875
  if (getLength(headerArray) < HEADER_SIZE) {
8763
- return HEADER_SIZE;
8876
+ return UNDEFINED_VALUE;
8764
8877
  }
8765
8878
  const headerView = getDataView(headerArray);
8879
+ if (getUint32(headerView, 0) != LOCAL_FILE_HEADER_SIGNATURE) {
8880
+ return UNDEFINED_VALUE;
8881
+ }
8766
8882
  return HEADER_SIZE +
8767
8883
  getUint16(headerView, HEADER_OFFSET_FILENAME_LENGTH + LOCAL_HEADER_COMMON_OFFSET) +
8768
8884
  getUint16(headerView, HEADER_OFFSET_EXTRAFIELD_LENGTH + LOCAL_HEADER_COMMON_OFFSET);
@@ -9113,7 +9229,7 @@ function formatSupported(StreamClass, format) {
9113
9229
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
9114
9230
  */
9115
9231
 
9116
- const VERSION = "2.20.0";
9232
+ const VERSION = "2.21.0";
9117
9233
 
9118
9234
  /*
9119
9235
  Copyright (c) 2025 Gildas Lormeau. All rights reserved.