@zip.js/zip.js 2.8.53 → 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 (47) hide show
  1. package/README.md +1 -1
  2. package/deno.json +2 -1
  3. package/dist/zip-core-external.js +691 -482
  4. package/dist/zip-core-external.min.js +1 -1
  5. package/dist/zip-core.js +698 -481
  6. package/dist/zip-core.min.js +1 -1
  7. package/dist/zip-fs-core-external.js +729 -511
  8. package/dist/zip-fs-core-external.min.js +1 -1
  9. package/dist/zip-fs-core.js +691 -496
  10. package/dist/zip-fs-core.min.js +1 -1
  11. package/dist/zip-fs-external.js +729 -511
  12. package/dist/zip-fs-external.min.js +1 -1
  13. package/dist/zip-fs-native.js +743 -512
  14. package/dist/zip-fs-native.min.js +1 -1
  15. package/dist/zip-fs.js +742 -511
  16. package/dist/zip-fs.min.js +1 -1
  17. package/dist/zip-legacy.js +700 -483
  18. package/dist/zip-legacy.min.js +1 -1
  19. package/dist/zip-native.js +700 -483
  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 +699 -482
  24. package/dist/zip.min.js +1 -1
  25. package/eslint.config.mjs +1 -0
  26. package/index-native.cjs +743 -512
  27. package/index-native.min.js +1 -1
  28. package/index.cjs +742 -511
  29. package/index.d.cts +398 -42
  30. package/index.d.ts +398 -42
  31. package/index.min.js +1 -1
  32. package/lib/core/configuration.js +62 -21
  33. package/lib/core/constants.js +11 -1
  34. package/lib/core/io.js +33 -27
  35. package/lib/core/options.js +44 -2
  36. package/lib/core/streams/aes-crypto-stream.js +12 -12
  37. package/lib/core/util/decode-text.js +11 -2
  38. package/lib/core/web-worker-base.js +3 -2
  39. package/lib/core/web-worker-inline-native.js +1 -1
  40. package/lib/core/web-worker-inline-wasm.js +1 -1
  41. package/lib/core/zip-fs.js +38 -27
  42. package/lib/core/zip-reader.js +58 -26
  43. package/lib/core/zip-writer.js +293 -177
  44. package/lib/zip-core-base.js +6 -1
  45. package/lib/zip-core-reader.js +1 -0
  46. package/lib/zip-core-writer.js +4 -0
  47. package/package.json +8 -2
@@ -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";
@@ -167,7 +168,11 @@ import {
167
168
  OPTION_USE_WEB_WORKERS,
168
169
  OPTION_USE_COMPRESSION_STREAM,
169
170
  OPTION_TRANSFER_STREAMS,
170
- OPTION_PREVENT_CLOSE
171
+ OPTION_PREVENT_CLOSE,
172
+ checkFunctionOption,
173
+ checkSignalOption,
174
+ checkPasswordOption,
175
+ toNumber
171
176
  } from "./options.js";
172
177
 
173
178
  const ERR_BAD_FORMAT = "File format is not recognized";
@@ -181,6 +186,7 @@ const ERR_UNSUPPORTED_ENCRYPTION = "Encryption method not supported";
181
186
  const ERR_UNSUPPORTED_COMPRESSION = "Compression method not supported";
182
187
  const ERR_SPLIT_ZIP_FILE = "Split zip file";
183
188
  const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
189
+ const ERR_ENTRY_DATA_OUT_OF_BOUNDS = "Entry data out of bounds";
184
190
  const ERR_AMBIGUOUS_ARCHIVE = "Ambiguous archive";
185
191
  const ERR_ENCRYPTED_CENTRAL_DIRECTORY = "Encrypted central directory is not supported";
186
192
  const ERR_UNSAFE_FILENAME = "Unsafe filename";
@@ -210,6 +216,7 @@ const ZIP64_EXTRACTION = {
210
216
  }
211
217
  };
212
218
  const MAX_END_OF_CENTRAL_DIR_PROBES = 64;
219
+ const MAX_DEFLATE_EXPANSION_RATIO = 1032;
213
220
  const CENTRAL_DIRECTORY_UNREACHABLE = 0;
214
221
  const CENTRAL_DIRECTORY_PLAUSIBLE = 1;
215
222
  const CENTRAL_DIRECTORY_REACHABLE = 2;
@@ -220,7 +227,6 @@ class ZipReader {
220
227
  Object.assign(this, {
221
228
  reader: new GenericReader(reader),
222
229
  options,
223
- config: getConfiguration(),
224
230
  readRanges: new Map()
225
231
  });
226
232
  }
@@ -228,7 +234,6 @@ class ZipReader {
228
234
  async* getEntriesGenerator(options = {}) {
229
235
  const zipReader = this;
230
236
  let { reader } = zipReader;
231
- const { config } = zipReader;
232
237
  await initStream(reader);
233
238
  if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
234
239
  reader = new BlobReader(await streamToBlob(reader.readable));
@@ -245,9 +250,7 @@ class ZipReader {
245
250
  const normalizeFilename = getOptionValue(zipReader, options, OPTION_NORMALIZE_FILENAME);
246
251
  const { endOfDirectoryInfo, endOfDirectoryReachingEndCount } = await findEndOfCentralDirectory(reader, rejectAmbiguousEndOfDirectory, maxAppendedDataSize);
247
252
  if (!endOfDirectoryInfo) {
248
- const signatureArray = await readUint8Array(reader, 0, 4);
249
- const signatureView = getDataView(signatureArray);
250
- if (getUint32(signatureView) == SPLIT_ZIP_FILE_SIGNATURE) {
253
+ if (await startsWithSplitZipSignature(reader)) {
251
254
  throw new Error(ERR_SPLIT_ZIP_FILE);
252
255
  } else {
253
256
  throw new Error(ERR_EOCDR_NOT_FOUND);
@@ -386,7 +389,7 @@ class ZipReader {
386
389
  }
387
390
  zipReader.directoryOffset = directoryDataOffset;
388
391
  zipReader.directoryLength = declaredDirectoryDataLength;
389
- const decryptCentralDirectory = getOptionValue(zipReader, options, OPTION_DECRYPT_CENTRAL_DIRECTORY);
392
+ const decryptCentralDirectory = getFunctionOptionValue(zipReader, options, OPTION_DECRYPT_CENTRAL_DIRECTORY);
390
393
  let decryptedDirectory;
391
394
  if (decryptCentralDirectory && filesLength && directoryArray.length >= 4 &&
392
395
  getUint32(directoryView, 0) != CENTRAL_FILE_HEADER_SIGNATURE &&
@@ -402,7 +405,7 @@ class ZipReader {
402
405
  const filenames = checkAmbiguity ? new Set() : UNDEFINED_VALUE;
403
406
  let duplicateFilename;
404
407
  for (let indexFile = 0; indexFile < filesLength; indexFile++) {
405
- const fileEntry = new ZipEntry(reader, config, zipReader.options);
408
+ const fileEntry = new ZipEntry(reader, zipReader.options);
406
409
  if (offset + CENTRAL_FILE_HEADER_LENGTH > directoryArray.length || getUint32(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE) {
407
410
  if (indexFile == 0 && !decryptedDirectory && (zip64EndOfDirectoryVersion2 || detectEncryptedCentralDirectory(directoryView))) {
408
411
  throw new Error(ERR_ENCRYPTED_CENTRAL_DIRECTORY);
@@ -433,7 +436,7 @@ class ZipReader {
433
436
  archive: Boolean(msdosAttributesRaw & FILE_ATTR_MSDOS_ARCHIVE_MASK)
434
437
  };
435
438
  const offsetFileEntry = getUint32(directoryView, offset + 42);
436
- const decode = getOptionValue(zipReader, options, OPTION_DECODE_TEXT) || decodeText;
439
+ const decode = getFunctionOptionValue(zipReader, options, OPTION_DECODE_TEXT) || decodeText;
437
440
  const rawFilenameEncoding = filenameUTF8 ? CHARSET_UTF8 : filenameEncoding || CHARSET_CP437;
438
441
  const rawCommentEncoding = commentUTF8 ? CHARSET_UTF8 : commentEncoding || CHARSET_CP437;
439
442
  let filename = decode(rawFilename, rawFilenameEncoding, TEXT_TYPE_FILENAME);
@@ -546,13 +549,17 @@ class ZipReader {
546
549
  if (duplicateFilename) {
547
550
  throwAmbiguousArchive("duplicate filename");
548
551
  }
549
- if (checkAmbiguity && (prependedDataLength || (filesLength && startOffset > 0))) {
550
- throwAmbiguousArchive("prepended data");
551
- }
552
552
  const extractPrependedData = getOptionValue(zipReader, options, OPTION_EXTRACT_PREPENDED_DATA);
553
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
+ }
554
559
  if (extractPrependedData) {
555
- 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;
556
563
  }
557
564
  zipReader.comment = commentLength ? await readUint8Array(reader, commentOffset + END_OF_CENTRAL_DIR_LENGTH, commentLength) : EMPTY_UINT8_ARRAY;
558
565
  if (extractAppendedData) {
@@ -637,6 +644,7 @@ export {
637
644
  ERR_INVALID_COMPRESSED_DATA,
638
645
  ERR_SPLIT_ZIP_FILE,
639
646
  ERR_OVERLAPPING_ENTRY,
647
+ ERR_ENTRY_DATA_OUT_OF_BOUNDS,
640
648
  ERR_AMBIGUOUS_ARCHIVE,
641
649
  ERR_ENCRYPTED_CENTRAL_DIRECTORY,
642
650
  ERR_UNSAFE_FILENAME,
@@ -648,16 +656,16 @@ export {
648
656
 
649
657
  class ZipEntry {
650
658
 
651
- constructor(reader, config, options) {
659
+ constructor(reader, options) {
652
660
  Object.assign(this, {
653
661
  reader,
654
- config,
655
662
  options
656
663
  });
657
664
  }
658
665
 
659
666
  async getData(writer, fileEntry, readRanges, options = {}) {
660
667
  const zipEntry = this;
668
+ const config = getConfiguration();
661
669
  const {
662
670
  reader,
663
671
  index,
@@ -666,7 +674,6 @@ class ZipEntry {
666
674
  extraFieldAES,
667
675
  extraFieldZip64,
668
676
  compressionMethod,
669
- config,
670
677
  bitFlag,
671
678
  rawBitFlag,
672
679
  crc32,
@@ -684,6 +691,7 @@ class ZipEntry {
684
691
  let password = getOptionValue(zipEntry, options, OPTION_PASSWORD);
685
692
  let rawPassword = getOptionValue(zipEntry, options, OPTION_RAW_PASSWORD);
686
693
  const passThrough = getOptionValue(zipEntry, options, OPTION_PASS_THROUGH);
694
+ checkPasswordOption(password, rawPassword);
687
695
  password = password && password.length && password;
688
696
  rawPassword = rawPassword && rawPassword.length && rawPassword;
689
697
  if (extraFieldAES) {
@@ -753,9 +761,12 @@ class ZipEntry {
753
761
  }
754
762
  }
755
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
+ }
756
767
  const size = compressedSize;
757
768
  const readable = toCompatibleReadable(reader.createReadable({ offset: dataOffset, size }));
758
- const signal = getOptionValue(zipEntry, options, OPTION_SIGNAL);
769
+ const signal = checkSignalOption(getOptionValue(zipEntry, options, OPTION_SIGNAL));
759
770
  const checkPasswordOnly = getOptionValue(zipEntry, options, OPTION_CHECK_PASSWORD_ONLY);
760
771
  let checkOverlappingEntry = getOptionValue(zipEntry, options, OPTION_CHECK_OVERLAPPING_ENTRY);
761
772
  const checkOverlappingEntryOnly = getOptionValue(zipEntry, options, OPTION_CHECK_OVERLAPPING_ENTRY_ONLY);
@@ -763,6 +774,8 @@ class ZipEntry {
763
774
  checkOverlappingEntry = true;
764
775
  }
765
776
  const { onstart, onprogress, onend } = options;
777
+ const compressed = compressionMethod != COMPRESSION_METHOD_STORE && !passThrough;
778
+ const outputSize = passThrough ? compressedSize : uncompressedSize;
766
779
  const deflate64 = compressionMethod == COMPRESSION_METHOD_DEFLATE_64;
767
780
  let useCompressionStream = getOptionValue(zipEntry, options, OPTION_USE_COMPRESSION_STREAM);
768
781
  if (deflate64) {
@@ -783,9 +796,9 @@ class ZipEntry {
783
796
  checkCrc32,
784
797
  checkAuthenticationCode: getOptionValue(zipEntry, options, OPTION_CHECK_AUTHENTICATION_CODE),
785
798
  passwordVerification: zipCrypto && (dataDescriptor ? ((rawLastModDate >>> 8) & MAX_8_BITS) : ((crc32 >>> 24) & MAX_8_BITS)),
786
- outputSize: passThrough ? compressedSize : uncompressedSize,
799
+ outputSize,
787
800
  crc32,
788
- compressed: compressionMethod != 0 && !passThrough,
801
+ compressed,
789
802
  encrypted,
790
803
  useWebWorkers: getOptionValue(zipEntry, options, OPTION_USE_WEB_WORKERS),
791
804
  useCompressionStream,
@@ -822,11 +835,11 @@ class ZipEntry {
822
835
  writer = new WritableStream();
823
836
  }
824
837
  writer = new GenericWriter(writer);
825
- await initStream(writer, passThrough ? compressedSize : uncompressedSize);
838
+ await initStream(writer, getDecodableOutputSize(outputSize, compressedSize, compressed));
826
839
  ({ writable } = writer);
827
- const { outputSize } = await runWorker({ readable, writable }, workerOptions);
828
- writer.size += outputSize;
829
- if (outputSize != (passThrough ? compressedSize : uncompressedSize)) {
840
+ const { outputSize: writtenSize } = await runWorker({ readable, writable }, workerOptions);
841
+ writer.size += writtenSize;
842
+ if (writtenSize != outputSize) {
830
843
  throw new Error(ERR_INVALID_UNCOMPRESSED_SIZE);
831
844
  }
832
845
  }
@@ -1246,10 +1259,28 @@ function getDiskOffset(reader, diskNumber) {
1246
1259
  return reader.getDiskOffset ? reader.getDiskOffset(diskNumber) : 0;
1247
1260
  }
1248
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
+
1249
1276
  function isStrictnessValue(value) {
1250
1277
  return value === STRICTNESS_STRICT || value === STRICTNESS_BALANCED || value === STRICTNESS_TOLERANT;
1251
1278
  }
1252
1279
 
1280
+ function getDecodableOutputSize(outputSize, compressedSize, compressed) {
1281
+ return Math.min(outputSize, compressed ? compressedSize * MAX_DEFLATE_EXPANSION_RATIO : compressedSize);
1282
+ }
1283
+
1253
1284
  function getStrictness(options, inheritedOptions) {
1254
1285
  return resolveStrictness(options, resolveStrictness(inheritedOptions, STRICTNESS_BALANCED));
1255
1286
  }
@@ -1463,10 +1494,11 @@ function getOptionValue(zipReader, options, name) {
1463
1494
  return options[name] === UNDEFINED_VALUE ? zipReader.options[name] : options[name];
1464
1495
  }
1465
1496
 
1466
- function toNumber(value) {
1467
- return typeof value == STRING_TYPE && value.trim() ? Number(value) : value;
1497
+ function getFunctionOptionValue(zipReader, options, name) {
1498
+ return checkFunctionOption(getOptionValue(zipReader, options, name));
1468
1499
  }
1469
1500
 
1501
+
1470
1502
  function getDate(timeRaw) {
1471
1503
  const date = (timeRaw & 0xffff0000) >> 16, time = timeRaw & MAX_16_BITS;
1472
1504
  return new Date(1980 + ((date & 0xFE00) >> 9), ((date & 0x01E0) >> 5) - 1, date & 0x001F, (time & 0xF800) >> 11, (time & 0x07E0) >> 5, (time & 0x001F) * 2, 0);