@zip.js/zip.js 2.8.52 → 2.8.54

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.
Files changed (49) hide show
  1. package/README.md +1 -1
  2. package/deno.json +2 -1
  3. package/dist/zip-core-external.js +839 -528
  4. package/dist/zip-core-external.min.js +1 -1
  5. package/dist/zip-core.js +847 -527
  6. package/dist/zip-core.min.js +1 -1
  7. package/dist/zip-fs-core-external.js +880 -557
  8. package/dist/zip-fs-core-external.min.js +1 -1
  9. package/dist/zip-fs-core.js +829 -538
  10. package/dist/zip-fs-core.min.js +1 -1
  11. package/dist/zip-fs-external.js +880 -557
  12. package/dist/zip-fs-external.min.js +1 -1
  13. package/dist/zip-fs-native.js +895 -558
  14. package/dist/zip-fs-native.min.js +1 -1
  15. package/dist/zip-fs.js +894 -557
  16. package/dist/zip-fs.min.js +1 -1
  17. package/dist/zip-legacy.js +849 -529
  18. package/dist/zip-legacy.min.js +1 -1
  19. package/dist/zip-native.js +849 -529
  20. package/dist/zip-native.min.js +1 -1
  21. package/dist/zip-web-worker-native.js +1 -1
  22. package/dist/zip-web-worker.js +1 -1
  23. package/dist/zip.js +848 -528
  24. package/dist/zip.min.js +1 -1
  25. package/eslint.config.mjs +2 -1
  26. package/index-native.cjs +895 -558
  27. package/index-native.min.js +1 -1
  28. package/index.cjs +894 -557
  29. package/index.d.cts +672 -78
  30. package/index.d.ts +672 -78
  31. package/index.min.js +1 -1
  32. package/lib/core/codec-worker-web.js +13 -4
  33. package/lib/core/configuration.js +62 -21
  34. package/lib/core/constants.js +16 -1
  35. package/lib/core/io.js +44 -29
  36. package/lib/core/options.js +46 -2
  37. package/lib/core/streams/aes-crypto-stream.js +12 -12
  38. package/lib/core/util/decode-text.js +11 -2
  39. package/lib/core/web-worker-base.js +3 -2
  40. package/lib/core/web-worker-inline-native.js +1 -1
  41. package/lib/core/web-worker-inline-wasm.js +1 -1
  42. package/lib/core/zip-entry.js +8 -0
  43. package/lib/core/zip-fs.js +38 -27
  44. package/lib/core/zip-reader.js +149 -60
  45. package/lib/core/zip-writer.js +329 -189
  46. package/lib/zip-core-base.js +6 -1
  47. package/lib/zip-core-reader.js +1 -0
  48. package/lib/zip-core-writer.js +5 -0
  49. package/package.json +11 -3
@@ -37,6 +37,8 @@ import {
37
37
  COMPRESSION_METHOD_STORE,
38
38
  COMPRESSION_METHOD_AES,
39
39
  SPLIT_ZIP_FILE_SIGNATURE,
40
+ TEMPORARY_SPLIT_ZIP_FILE_SIGNATURE,
41
+ SPLIT_ZIP_FILE_SIGNATURE_LENGTH,
40
42
  DATA_DESCRIPTOR_RECORD_SIGNATURE,
41
43
  DATA_DESCRIPTOR_RECORD_LENGTH,
42
44
  DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH,
@@ -90,7 +92,6 @@ import {
90
92
  FILE_ATTR_UNIX_SETGID_MASK,
91
93
  FILE_ATTR_UNIX_STICKY_MASK,
92
94
  NUMBER_TYPE,
93
- STRING_TYPE,
94
95
  EMPTY_UINT8_ARRAY
95
96
  } from "./constants.js";
96
97
  import { getConfiguration } from "./configuration.js";
@@ -150,6 +151,7 @@ import {
150
151
  OPTION_CHECK_OVERLAPPING_ENTRY_ONLY,
151
152
  OPTION_CHECK_OVERLAPPING_ENTRY,
152
153
  OPTION_CHECK_AMBIGUITY,
154
+ OPTION_CHECK_LOCAL_DIRECTORY,
153
155
  OPTION_CHECK_SIGNATURE,
154
156
  OPTION_CHECK_CRC32,
155
157
  OPTION_CHECK_AUTHENTICATION_CODE,
@@ -166,7 +168,11 @@ import {
166
168
  OPTION_USE_WEB_WORKERS,
167
169
  OPTION_USE_COMPRESSION_STREAM,
168
170
  OPTION_TRANSFER_STREAMS,
169
- OPTION_PREVENT_CLOSE
171
+ OPTION_PREVENT_CLOSE,
172
+ checkFunctionOption,
173
+ checkSignalOption,
174
+ checkPasswordOption,
175
+ toNumber
170
176
  } from "./options.js";
171
177
 
172
178
  const ERR_BAD_FORMAT = "File format is not recognized";
@@ -180,6 +186,7 @@ const ERR_UNSUPPORTED_ENCRYPTION = "Encryption method not supported";
180
186
  const ERR_UNSUPPORTED_COMPRESSION = "Compression method not supported";
181
187
  const ERR_SPLIT_ZIP_FILE = "Split zip file";
182
188
  const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
189
+ const ERR_ENTRY_DATA_OUT_OF_BOUNDS = "Entry data out of bounds";
183
190
  const ERR_AMBIGUOUS_ARCHIVE = "Ambiguous archive";
184
191
  const ERR_ENCRYPTED_CENTRAL_DIRECTORY = "Encrypted central directory is not supported";
185
192
  const ERR_UNSAFE_FILENAME = "Unsafe filename";
@@ -209,6 +216,7 @@ const ZIP64_EXTRACTION = {
209
216
  }
210
217
  };
211
218
  const MAX_END_OF_CENTRAL_DIR_PROBES = 64;
219
+ const MAX_DEFLATE_EXPANSION_RATIO = 1032;
212
220
  const CENTRAL_DIRECTORY_UNREACHABLE = 0;
213
221
  const CENTRAL_DIRECTORY_PLAUSIBLE = 1;
214
222
  const CENTRAL_DIRECTORY_REACHABLE = 2;
@@ -219,7 +227,6 @@ class ZipReader {
219
227
  Object.assign(this, {
220
228
  reader: new GenericReader(reader),
221
229
  options,
222
- config: getConfiguration(),
223
230
  readRanges: new Map()
224
231
  });
225
232
  }
@@ -227,7 +234,6 @@ class ZipReader {
227
234
  async* getEntriesGenerator(options = {}) {
228
235
  const zipReader = this;
229
236
  let { reader } = zipReader;
230
- const { config } = zipReader;
231
237
  await initStream(reader);
232
238
  if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
233
239
  reader = new BlobReader(await streamToBlob(reader.readable));
@@ -236,7 +242,7 @@ class ZipReader {
236
242
  if (reader.size < END_OF_CENTRAL_DIR_LENGTH) {
237
243
  throw new Error(ERR_BAD_FORMAT);
238
244
  }
239
- const strictness = getStrictness(getOptionValue(zipReader, options, OPTION_STRICTNESS), getOptionValue(zipReader, options, OPTION_CHECK_AMBIGUITY));
245
+ const strictness = getStrictness(options, zipReader.options);
240
246
  const checkAmbiguity = strictness == STRICTNESS_STRICT;
241
247
  const rejectAmbiguousEndOfDirectory = strictness != STRICTNESS_TOLERANT;
242
248
  const maxAppendedDataSize = getMaxAppendedDataSize(getOptionValue(zipReader, options, OPTION_MAX_APPENDED_DATA_SIZE), strictness);
@@ -244,9 +250,7 @@ class ZipReader {
244
250
  const normalizeFilename = getOptionValue(zipReader, options, OPTION_NORMALIZE_FILENAME);
245
251
  const { endOfDirectoryInfo, endOfDirectoryReachingEndCount } = await findEndOfCentralDirectory(reader, rejectAmbiguousEndOfDirectory, maxAppendedDataSize);
246
252
  if (!endOfDirectoryInfo) {
247
- const signatureArray = await readUint8Array(reader, 0, 4);
248
- const signatureView = getDataView(signatureArray);
249
- if (getUint32(signatureView) == SPLIT_ZIP_FILE_SIGNATURE) {
253
+ if (await startsWithSplitZipSignature(reader)) {
250
254
  throw new Error(ERR_SPLIT_ZIP_FILE);
251
255
  } else {
252
256
  throw new Error(ERR_EOCDR_NOT_FOUND);
@@ -385,7 +389,7 @@ class ZipReader {
385
389
  }
386
390
  zipReader.directoryOffset = directoryDataOffset;
387
391
  zipReader.directoryLength = declaredDirectoryDataLength;
388
- const decryptCentralDirectory = getOptionValue(zipReader, options, OPTION_DECRYPT_CENTRAL_DIRECTORY);
392
+ const decryptCentralDirectory = getFunctionOptionValue(zipReader, options, OPTION_DECRYPT_CENTRAL_DIRECTORY);
389
393
  let decryptedDirectory;
390
394
  if (decryptCentralDirectory && filesLength && directoryArray.length >= 4 &&
391
395
  getUint32(directoryView, 0) != CENTRAL_FILE_HEADER_SIGNATURE &&
@@ -401,7 +405,7 @@ class ZipReader {
401
405
  const filenames = checkAmbiguity ? new Set() : UNDEFINED_VALUE;
402
406
  let duplicateFilename;
403
407
  for (let indexFile = 0; indexFile < filesLength; indexFile++) {
404
- const fileEntry = new ZipEntry(reader, config, zipReader.options);
408
+ const fileEntry = new ZipEntry(reader, zipReader.options);
405
409
  if (offset + CENTRAL_FILE_HEADER_LENGTH > directoryArray.length || getUint32(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE) {
406
410
  if (indexFile == 0 && !decryptedDirectory && (zip64EndOfDirectoryVersion2 || detectEncryptedCentralDirectory(directoryView))) {
407
411
  throw new Error(ERR_ENCRYPTED_CENTRAL_DIRECTORY);
@@ -432,7 +436,7 @@ class ZipReader {
432
436
  archive: Boolean(msdosAttributesRaw & FILE_ATTR_MSDOS_ARCHIVE_MASK)
433
437
  };
434
438
  const offsetFileEntry = getUint32(directoryView, offset + 42);
435
- const decode = getOptionValue(zipReader, options, OPTION_DECODE_TEXT) || decodeText;
439
+ const decode = getFunctionOptionValue(zipReader, options, OPTION_DECODE_TEXT) || decodeText;
436
440
  const rawFilenameEncoding = filenameUTF8 ? CHARSET_UTF8 : filenameEncoding || CHARSET_CP437;
437
441
  const rawCommentEncoding = commentUTF8 ? CHARSET_UTF8 : commentEncoding || CHARSET_CP437;
438
442
  let filename = decode(rawFilename, rawFilenameEncoding, TEXT_TYPE_FILENAME);
@@ -458,6 +462,7 @@ class ZipReader {
458
462
  index: indexFile,
459
463
  versionMadeBy,
460
464
  msDosCompatible,
465
+ zip64: false,
461
466
  compressedSize: 0,
462
467
  uncompressedSize: 0,
463
468
  commentLength,
@@ -544,13 +549,17 @@ class ZipReader {
544
549
  if (duplicateFilename) {
545
550
  throwAmbiguousArchive("duplicate filename");
546
551
  }
547
- if (checkAmbiguity && (prependedDataLength || (filesLength && startOffset > 0))) {
548
- throwAmbiguousArchive("prepended data");
549
- }
550
552
  const extractPrependedData = getOptionValue(zipReader, options, OPTION_EXTRACT_PREPENDED_DATA);
551
553
  const extractAppendedData = getOptionValue(zipReader, options, OPTION_EXTRACT_APPENDED_DATA);
554
+ const splitZipSignatureLength = (checkAmbiguity || extractPrependedData) && filesLength &&
555
+ startOffset == SPLIT_ZIP_FILE_SIGNATURE_LENGTH && await startsWithSplitZipMarker(reader) ? SPLIT_ZIP_FILE_SIGNATURE_LENGTH : 0;
556
+ if (checkAmbiguity && (prependedDataLength || (filesLength && startOffset > splitZipSignatureLength))) {
557
+ throwAmbiguousArchive("prepended data");
558
+ }
552
559
  if (extractPrependedData) {
553
- zipReader.prependedData = startOffset > 0 ? await readUint8Array(reader, 0, startOffset) : EMPTY_UINT8_ARRAY;
560
+ zipReader.prependedData = startOffset > splitZipSignatureLength ?
561
+ await readUint8Array(reader, splitZipSignatureLength, startOffset - splitZipSignatureLength) :
562
+ EMPTY_UINT8_ARRAY;
554
563
  }
555
564
  zipReader.comment = commentLength ? await readUint8Array(reader, commentOffset + END_OF_CENTRAL_DIR_LENGTH, commentLength) : EMPTY_UINT8_ARRAY;
556
565
  if (extractAppendedData) {
@@ -635,6 +644,7 @@ export {
635
644
  ERR_INVALID_COMPRESSED_DATA,
636
645
  ERR_SPLIT_ZIP_FILE,
637
646
  ERR_OVERLAPPING_ENTRY,
647
+ ERR_ENTRY_DATA_OUT_OF_BOUNDS,
638
648
  ERR_AMBIGUOUS_ARCHIVE,
639
649
  ERR_ENCRYPTED_CENTRAL_DIRECTORY,
640
650
  ERR_UNSAFE_FILENAME,
@@ -646,16 +656,16 @@ export {
646
656
 
647
657
  class ZipEntry {
648
658
 
649
- constructor(reader, config, options) {
659
+ constructor(reader, options) {
650
660
  Object.assign(this, {
651
661
  reader,
652
- config,
653
662
  options
654
663
  });
655
664
  }
656
665
 
657
666
  async getData(writer, fileEntry, readRanges, options = {}) {
658
667
  const zipEntry = this;
668
+ const config = getConfiguration();
659
669
  const {
660
670
  reader,
661
671
  index,
@@ -664,7 +674,6 @@ class ZipEntry {
664
674
  extraFieldAES,
665
675
  extraFieldZip64,
666
676
  compressionMethod,
667
- config,
668
677
  bitFlag,
669
678
  rawBitFlag,
670
679
  crc32,
@@ -682,6 +691,7 @@ class ZipEntry {
682
691
  let password = getOptionValue(zipEntry, options, OPTION_PASSWORD);
683
692
  let rawPassword = getOptionValue(zipEntry, options, OPTION_RAW_PASSWORD);
684
693
  const passThrough = getOptionValue(zipEntry, options, OPTION_PASS_THROUGH);
694
+ checkPasswordOption(password, rawPassword);
685
695
  password = password && password.length && password;
686
696
  rawPassword = rawPassword && rawPassword.length && rawPassword;
687
697
  if (extraFieldAES) {
@@ -697,9 +707,12 @@ class ZipEntry {
697
707
  extraFieldLength,
698
708
  filenameLength
699
709
  } = localDirectory;
700
- const checkAmbiguity = getStrictness(getOptionValue(zipEntry, options, OPTION_STRICTNESS), getOptionValue(zipEntry, options, OPTION_CHECK_AMBIGUITY)) == STRICTNESS_STRICT;
710
+ const checkLocalDirectoryOption = getOptionValue(zipEntry, options, OPTION_CHECK_LOCAL_DIRECTORY);
711
+ const entryStrictness = getStrictness(options, zipEntry.options);
712
+ const checkLocalDirectory = getCheckLocalDirectory(checkLocalDirectoryOption, entryStrictness);
713
+ const checkLocalFilename = getCheckLocalFilename(checkLocalDirectoryOption, entryStrictness);
701
714
  let rawLocalFilename = EMPTY_UINT8_ARRAY;
702
- if (checkAmbiguity && (filenameLength || extraFieldLength)) {
715
+ if (checkLocalFilename && (filenameLength || extraFieldLength)) {
703
716
  const trailingDataArray = await readUint8Array(reader, localHeaderOffset + HEADER_SIZE, filenameLength + extraFieldLength);
704
717
  rawLocalFilename = trailingDataArray.subarray(0, filenameLength);
705
718
  localDirectory.rawExtraField = trailingDataArray.subarray(filenameLength);
@@ -708,17 +721,26 @@ class ZipEntry {
708
721
  await readUint8Array(reader, localHeaderOffset + HEADER_SIZE + filenameLength, extraFieldLength) :
709
722
  EMPTY_UINT8_ARRAY;
710
723
  }
724
+ if (checkLocalFilename) {
725
+ localDirectory.rawFilename = rawLocalFilename;
726
+ }
711
727
  readCommonFooter(zipEntry, localDirectory, dataView, 4, true);
712
- if (checkAmbiguity) {
713
- checkLocalDirectory(zipEntry, localDirectory, rawLocalFilename);
728
+ if (checkLocalDirectory) {
729
+ validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename);
714
730
  }
715
- const { lastAccessDate, creationDate } = localDirectory;
731
+ const { lastAccessDate, creationDate, uid, gid } = localDirectory;
716
732
  if (lastAccessDate) {
717
733
  fileEntry.lastAccessDate = lastAccessDate;
718
734
  }
719
735
  if (creationDate) {
720
736
  fileEntry.creationDate = creationDate;
721
737
  }
738
+ if (uid !== UNDEFINED_VALUE && fileEntry.uid === UNDEFINED_VALUE) {
739
+ fileEntry.uid = uid;
740
+ }
741
+ if (gid !== UNDEFINED_VALUE && fileEntry.gid === UNDEFINED_VALUE) {
742
+ fileEntry.gid = gid;
743
+ }
722
744
  const encrypted = zipEntry.encrypted && localDirectory.encrypted && !passThrough;
723
745
  const zipCrypto = encrypted && !extraFieldAES;
724
746
  if (!passThrough) {
@@ -739,9 +761,12 @@ class ZipEntry {
739
761
  }
740
762
  }
741
763
  const dataOffset = localHeaderOffset + HEADER_SIZE + filenameLength + extraFieldLength;
764
+ if (dataOffset + compressedSize > reader.size) {
765
+ throw new Error(ERR_ENTRY_DATA_OUT_OF_BOUNDS);
766
+ }
742
767
  const size = compressedSize;
743
768
  const readable = toCompatibleReadable(reader.createReadable({ offset: dataOffset, size }));
744
- const signal = getOptionValue(zipEntry, options, OPTION_SIGNAL);
769
+ const signal = checkSignalOption(getOptionValue(zipEntry, options, OPTION_SIGNAL));
745
770
  const checkPasswordOnly = getOptionValue(zipEntry, options, OPTION_CHECK_PASSWORD_ONLY);
746
771
  let checkOverlappingEntry = getOptionValue(zipEntry, options, OPTION_CHECK_OVERLAPPING_ENTRY);
747
772
  const checkOverlappingEntryOnly = getOptionValue(zipEntry, options, OPTION_CHECK_OVERLAPPING_ENTRY_ONLY);
@@ -749,6 +774,8 @@ class ZipEntry {
749
774
  checkOverlappingEntry = true;
750
775
  }
751
776
  const { onstart, onprogress, onend } = options;
777
+ const compressed = compressionMethod != COMPRESSION_METHOD_STORE && !passThrough;
778
+ const outputSize = passThrough ? compressedSize : uncompressedSize;
752
779
  const deflate64 = compressionMethod == COMPRESSION_METHOD_DEFLATE_64;
753
780
  let useCompressionStream = getOptionValue(zipEntry, options, OPTION_USE_COMPRESSION_STREAM);
754
781
  if (deflate64) {
@@ -769,9 +796,9 @@ class ZipEntry {
769
796
  checkCrc32,
770
797
  checkAuthenticationCode: getOptionValue(zipEntry, options, OPTION_CHECK_AUTHENTICATION_CODE),
771
798
  passwordVerification: zipCrypto && (dataDescriptor ? ((rawLastModDate >>> 8) & MAX_8_BITS) : ((crc32 >>> 24) & MAX_8_BITS)),
772
- outputSize: passThrough ? compressedSize : uncompressedSize,
799
+ outputSize,
773
800
  crc32,
774
- compressed: compressionMethod != 0 && !passThrough,
801
+ compressed,
775
802
  encrypted,
776
803
  useWebWorkers: getOptionValue(zipEntry, options, OPTION_USE_WEB_WORKERS),
777
804
  useCompressionStream,
@@ -808,11 +835,11 @@ class ZipEntry {
808
835
  writer = new WritableStream();
809
836
  }
810
837
  writer = new GenericWriter(writer);
811
- await initStream(writer, passThrough ? compressedSize : uncompressedSize);
838
+ await initStream(writer, getDecodableOutputSize(outputSize, compressedSize, compressed));
812
839
  ({ writable } = writer);
813
- const { outputSize } = await runWorker({ readable, writable }, workerOptions);
814
- writer.size += outputSize;
815
- if (outputSize != (passThrough ? compressedSize : uncompressedSize)) {
840
+ const { outputSize: writtenSize } = await runWorker({ readable, writable }, workerOptions);
841
+ writer.size += writtenSize;
842
+ if (writtenSize != outputSize) {
816
843
  throw new Error(ERR_INVALID_UNCOMPRESSED_SIZE);
817
844
  }
818
845
  }
@@ -954,10 +981,12 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
954
981
  directory.extraFieldNTFS = extraFieldNTFS;
955
982
  }
956
983
  const extraFieldUnix = extraField.get(EXTRAFIELD_TYPE_UNIX);
984
+ let unixIdsRead;
957
985
  if (extraFieldUnix) {
958
- readExtraFieldUnix(extraFieldUnix, directory, false);
986
+ unixIdsRead = readExtraFieldUnix(extraFieldUnix, directory, false);
959
987
  directory.extraFieldUnix = extraFieldUnix;
960
- } else {
988
+ }
989
+ if (!unixIdsRead) {
961
990
  const extraFieldInfoZip = extraField.get(EXTRAFIELD_TYPE_INFOZIP);
962
991
  if (extraFieldInfoZip) {
963
992
  readExtraFieldUnix(extraFieldInfoZip, directory, true);
@@ -1060,7 +1089,7 @@ function readExtraFieldNTFS(extraFieldNTFS, directory) {
1060
1089
  const creationDate = getDateNTFS(rawCreationDate);
1061
1090
  const extraFieldData = { lastModDate, lastAccessDate, creationDate };
1062
1091
  Object.assign(extraFieldNTFS, extraFieldData);
1063
- Object.assign(directory, extraFieldData);
1092
+ Object.assign(directory, extraFieldData, { rawLastAccessDate, rawCreationDate });
1064
1093
  }
1065
1094
  }
1066
1095
 
@@ -1104,6 +1133,7 @@ function readExtraFieldUnix(extraField, directory, isInfoZip) {
1104
1133
  if (gid !== UNDEFINED_VALUE) {
1105
1134
  directory.gid = gid;
1106
1135
  }
1136
+ return uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE;
1107
1137
  } catch {
1108
1138
  // ignored
1109
1139
  }
@@ -1176,26 +1206,25 @@ async function detectOverlappingEntry({
1176
1206
  }
1177
1207
  if (dataDescriptorLength) {
1178
1208
  const dataDescriptorArray = await readUint8Array(reader, dataOffset + compressedSize, dataDescriptorLength + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH);
1179
- const dataDescriptorSignature = dataDescriptorArray.length == dataDescriptorLength + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH &&
1180
- getUint32(getDataView(dataDescriptorArray), 0) == DATA_DESCRIPTOR_RECORD_SIGNATURE;
1181
- if (dataDescriptorSignature) {
1182
- const readCrc32 = getUint32(getDataView(dataDescriptorArray), 4);
1183
- let readCompressedSize;
1184
- let readUncompressedSize;
1185
- if (extraFieldZip64) {
1186
- readCompressedSize = getBigUint64(getDataView(dataDescriptorArray), 8);
1187
- readUncompressedSize = getBigUint64(getDataView(dataDescriptorArray), 16);
1188
- } else {
1189
- readCompressedSize = getUint32(getDataView(dataDescriptorArray), 8);
1190
- readUncompressedSize = getUint32(getDataView(dataDescriptorArray), 12);
1191
- }
1192
- const matchCrc32 = (fileEntry.encrypted && !fileEntry.zipCrypto) || readCrc32 == crc32;
1209
+ const dataDescriptorView = getDataView(dataDescriptorArray);
1210
+ let signature = dataDescriptorArray.length == dataDescriptorLength + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH &&
1211
+ getUint32(dataDescriptorView, 0) == DATA_DESCRIPTOR_RECORD_SIGNATURE;
1212
+ if (signature) {
1213
+ const signedDataDescriptor = readDataDescriptor(dataDescriptorView, DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH, extraFieldZip64);
1214
+ const matchCrc32 = (fileEntry.encrypted && !fileEntry.zipCrypto) || signedDataDescriptor.crc32 == crc32;
1193
1215
  if (matchCrc32 &&
1194
- readCompressedSize == compressedSize &&
1195
- readUncompressedSize == uncompressedSize) {
1216
+ signedDataDescriptor.compressedSize == compressedSize &&
1217
+ signedDataDescriptor.uncompressedSize == uncompressedSize) {
1196
1218
  dataDescriptorLength += DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH;
1219
+ } else {
1220
+ signature = false;
1197
1221
  }
1198
1222
  }
1223
+ if (dataDescriptorArray.length >= dataDescriptorLength) {
1224
+ const localDataDescriptor = readDataDescriptor(dataDescriptorView, signature ? DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH : 0, extraFieldZip64);
1225
+ localDataDescriptor.signature = signature;
1226
+ fileEntry.localDirectory.dataDescriptor = localDataDescriptor;
1227
+ }
1199
1228
  }
1200
1229
  const range = {
1201
1230
  start: offset,
@@ -1212,22 +1241,80 @@ async function detectOverlappingEntry({
1212
1241
  readRanges.set(index, range);
1213
1242
  }
1214
1243
 
1244
+ function readDataDescriptor(dataDescriptorView, offset, extraFieldZip64) {
1245
+ const crc32 = getUint32(dataDescriptorView, offset);
1246
+ let compressedSize;
1247
+ let uncompressedSize;
1248
+ if (extraFieldZip64) {
1249
+ compressedSize = getBigUint64(dataDescriptorView, offset + 4);
1250
+ uncompressedSize = getBigUint64(dataDescriptorView, offset + 12);
1251
+ } else {
1252
+ compressedSize = getUint32(dataDescriptorView, offset + 4);
1253
+ uncompressedSize = getUint32(dataDescriptorView, offset + 8);
1254
+ }
1255
+ return { crc32, compressedSize, uncompressedSize };
1256
+ }
1257
+
1215
1258
  function getDiskOffset(reader, diskNumber) {
1216
1259
  return reader.getDiskOffset ? reader.getDiskOffset(diskNumber) : 0;
1217
1260
  }
1218
1261
 
1262
+ async function startsWithSplitZipSignature(reader) {
1263
+ return await getFirstSignature(reader) == SPLIT_ZIP_FILE_SIGNATURE;
1264
+ }
1265
+
1266
+ async function startsWithSplitZipMarker(reader) {
1267
+ const signature = await getFirstSignature(reader);
1268
+ return signature == SPLIT_ZIP_FILE_SIGNATURE || signature == TEMPORARY_SPLIT_ZIP_FILE_SIGNATURE;
1269
+ }
1270
+
1271
+ async function getFirstSignature(reader) {
1272
+ const signatureArray = await readUint8Array(reader, 0, SPLIT_ZIP_FILE_SIGNATURE_LENGTH);
1273
+ return getUint32(getDataView(signatureArray));
1274
+ }
1275
+
1219
1276
  function isStrictnessValue(value) {
1220
1277
  return value === STRICTNESS_STRICT || value === STRICTNESS_BALANCED || value === STRICTNESS_TOLERANT;
1221
1278
  }
1222
1279
 
1223
- function getStrictness(strictness, checkAmbiguity) {
1224
- if (strictness === UNDEFINED_VALUE) {
1225
- return checkAmbiguity ? STRICTNESS_STRICT : STRICTNESS_BALANCED;
1280
+ function getDecodableOutputSize(outputSize, compressedSize, compressed) {
1281
+ return Math.min(outputSize, compressed ? compressedSize * MAX_DEFLATE_EXPANSION_RATIO : compressedSize);
1282
+ }
1283
+
1284
+ function getStrictness(options, inheritedOptions) {
1285
+ return resolveStrictness(options, resolveStrictness(inheritedOptions, STRICTNESS_BALANCED));
1286
+ }
1287
+
1288
+ function resolveStrictness(options, inheritedStrictness) {
1289
+ const strictness = options[OPTION_STRICTNESS];
1290
+ if (strictness !== UNDEFINED_VALUE) {
1291
+ if (!isStrictnessValue(strictness)) {
1292
+ throw new Error(ERR_INVALID_STRICTNESS);
1293
+ }
1294
+ return strictness;
1295
+ }
1296
+ const checkAmbiguity = options[OPTION_CHECK_AMBIGUITY];
1297
+ if (checkAmbiguity === UNDEFINED_VALUE) {
1298
+ return inheritedStrictness;
1299
+ }
1300
+ if (checkAmbiguity) {
1301
+ return STRICTNESS_STRICT;
1302
+ }
1303
+ return inheritedStrictness == STRICTNESS_TOLERANT ? STRICTNESS_TOLERANT : STRICTNESS_BALANCED;
1304
+ }
1305
+
1306
+ function getCheckLocalDirectory(checkLocalDirectory, strictness) {
1307
+ if (checkLocalDirectory === UNDEFINED_VALUE) {
1308
+ return strictness != STRICTNESS_TOLERANT;
1226
1309
  }
1227
- if (!isStrictnessValue(strictness)) {
1228
- throw new Error(ERR_INVALID_STRICTNESS);
1310
+ return Boolean(checkLocalDirectory);
1311
+ }
1312
+
1313
+ function getCheckLocalFilename(checkLocalFilename, strictness) {
1314
+ if (checkLocalFilename === UNDEFINED_VALUE) {
1315
+ return strictness == STRICTNESS_STRICT;
1229
1316
  }
1230
- return strictness;
1317
+ return Boolean(checkLocalFilename);
1231
1318
  }
1232
1319
 
1233
1320
  function getFilenameValidation(filenameValidation, strictness) {
@@ -1375,10 +1462,11 @@ async function readSignature(reader, view, anchoredOffset, signatureOffset, size
1375
1462
  return UNDEFINED_VALUE;
1376
1463
  }
1377
1464
 
1378
- function checkLocalDirectory(zipEntry, localDirectory, rawLocalFilename) {
1465
+ function validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename) {
1379
1466
  const { rawFilename } = zipEntry;
1380
- if (rawLocalFilename.length != rawFilename.length ||
1381
- rawLocalFilename.some((byteValue, indexByte) => byteValue != rawFilename[indexByte])) {
1467
+ if (checkLocalFilename &&
1468
+ (rawLocalFilename.length != rawFilename.length ||
1469
+ rawLocalFilename.some((byteValue, indexByte) => byteValue != rawFilename[indexByte]))) {
1382
1470
  throwAmbiguousArchive("mismatched local file header (filename)");
1383
1471
  }
1384
1472
  if ((localDirectory.rawBitFlag & BITFLAG_AMBIGUITY_MASK) != (zipEntry.rawBitFlag & BITFLAG_AMBIGUITY_MASK)) {
@@ -1406,10 +1494,11 @@ function getOptionValue(zipReader, options, name) {
1406
1494
  return options[name] === UNDEFINED_VALUE ? zipReader.options[name] : options[name];
1407
1495
  }
1408
1496
 
1409
- function toNumber(value) {
1410
- return typeof value == STRING_TYPE && value.trim() ? Number(value) : value;
1497
+ function getFunctionOptionValue(zipReader, options, name) {
1498
+ return checkFunctionOption(getOptionValue(zipReader, options, name));
1411
1499
  }
1412
1500
 
1501
+
1413
1502
  function getDate(timeRaw) {
1414
1503
  const date = (timeRaw & 0xffff0000) >> 16, time = timeRaw & MAX_16_BITS;
1415
1504
  return new Date(1980 + ((date & 0xFE00) >> 9), ((date & 0x01E0) >> 5) - 1, date & 0x001F, (time & 0xF800) >> 11, (time & 0x07E0) >> 5, (time & 0x001F) * 2, 0);