@zip.js/zip.js 2.8.58 → 2.8.59

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.
@@ -74,6 +74,7 @@ const BITFLAG_LEVEL_MAX_MASK = 0b010;
74
74
  const BITFLAG_LEVEL_FAST_MASK = 0b100;
75
75
  const BITFLAG_LEVEL_SUPER_FAST_MASK = 0b110;
76
76
  const BITFLAG_DATA_DESCRIPTOR = 0b1000;
77
+ const BITFLAG_COMPRESSED_PATCHED_DATA = 0b100000;
77
78
  const BITFLAG_STRONG_ENCRYPTION = 0b1000000;
78
79
  const BITFLAG_LANG_ENCODING_FLAG = 0b100000000000;
79
80
  const BITFLAG_MASKED_LOCAL_HEADERS = 0b10000000000000;
@@ -171,6 +172,7 @@ export {
171
172
  BITFLAG_LEVEL_FAST_MASK,
172
173
  BITFLAG_LEVEL_SUPER_FAST_MASK,
173
174
  BITFLAG_DATA_DESCRIPTOR,
175
+ BITFLAG_COMPRESSED_PATCHED_DATA,
174
176
  BITFLAG_STRONG_ENCRYPTION,
175
177
  BITFLAG_LANG_ENCODING_FLAG,
176
178
  BITFLAG_MASKED_LOCAL_HEADERS,
@@ -70,6 +70,7 @@ const OPTION_OFFSET = "offset";
70
70
  const OPTION_USDZ = "usdz";
71
71
  const OPTION_UNIX_EXTRA_FIELD_TYPE = "unixExtraFieldType";
72
72
  const OPTION_LOCAL_EXTRA_FIELD = "localExtraField";
73
+ const OPTION_CENTRAL_EXTRA_FIELD = "centralExtraField";
73
74
  const OPTION_STRICTNESS = "strictness";
74
75
  const OPTION_FILENAME_VALIDATION = "filenameValidation";
75
76
  const OPTION_NORMALIZE_FILENAME = "normalizeFilename";
@@ -124,6 +125,7 @@ export {
124
125
  OPTION_USDZ,
125
126
  OPTION_UNIX_EXTRA_FIELD_TYPE,
126
127
  OPTION_LOCAL_EXTRA_FIELD,
128
+ OPTION_CENTRAL_EXTRA_FIELD,
127
129
  OPTION_STRICTNESS,
128
130
  OPTION_FILENAME_VALIDATION,
129
131
  OPTION_NORMALIZE_FILENAME,
@@ -68,6 +68,7 @@ import {
68
68
  ZIP64_END_OF_CENTRAL_DIR_LENGTH,
69
69
  BITFLAG_ENCRYPTED,
70
70
  BITFLAG_STRONG_ENCRYPTION,
71
+ BITFLAG_COMPRESSED_PATCHED_DATA,
71
72
  BITFLAG_MASKED_LOCAL_HEADERS,
72
73
  BITFLAG_LEVEL,
73
74
  BITFLAG_DATA_DESCRIPTOR,
@@ -196,6 +197,21 @@ const ERR_INVALID_STRICTNESS = "Invalid strictness (must be 'strict', 'balanced'
196
197
  const ERR_INVALID_FILENAME_VALIDATION = "Invalid filenameValidation (must be 'strict', 'balanced' or 'tolerant')";
197
198
  const ERR_INVALID_MAX_APPENDED_DATA_SIZE = "Invalid maxAppendedDataSize (must be a number greater than or equal to 0)";
198
199
  const ERR_UNSUPPORTED_UINT64 = "64-bit value exceeds Number.MAX_SAFE_INTEGER";
200
+ const WARNING_UNSORTED_CENTRAL_DIRECTORY = "unsorted central directory";
201
+ const WARNING_UNKNOWN_VERSION = "unknown version needed to extract";
202
+ const WARNING_COMPRESSED_PATCHED_DATA = "compressed patched data";
203
+ const WARNING_MALFORMED_EXTRA_FIELD = "malformed extra field";
204
+ const WARNING_UNKNOWN_ZIP64_EXTENSIBLE_DATA = "unknown zip64 extensible data";
205
+ const WARNING_WRAPPED_ENTRIES_COUNT = "wrapped entries count";
206
+ const WARNING_APPENDED_DATA = "appended data";
207
+ const WARNING_PREPENDED_DATA = "prepended data";
208
+ const WARNING_TRAILING_CENTRAL_DIRECTORY_DATA = "trailing central directory data";
209
+ const WARNING_DUPLICATE_FILENAME = "duplicate filename";
210
+ const WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY = "mismatched zip64 end of central directory record";
211
+ const WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG = "mismatched local file header (general purpose bit flag)";
212
+ const WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD = "mismatched local file header (compression method)";
213
+ const WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES = "mismatched local file header (crc32 or sizes)";
214
+ const MAX_KNOWN_VERSION = 63;
199
215
  const DRIVE_LETTER_REGEXP = /^[a-zA-Z]:/;
200
216
  const CHARSET_UTF8 = "utf-8";
201
217
  const PROPERTY_NAME_UTF8_SUFFIX = "UTF8";
@@ -246,6 +262,7 @@ class ZipReader {
246
262
  if (reader.size < END_OF_CENTRAL_DIR_LENGTH) {
247
263
  throw new Error(ERR_BAD_FORMAT);
248
264
  }
265
+ const warnings = zipReader.warnings = [];
249
266
  const strictness = getStrictness(options, zipReader.options);
250
267
  const checkAmbiguity = strictness == STRICTNESS_STRICT;
251
268
  const rejectAmbiguousEndOfDirectory = strictness != STRICTNESS_TOLERANT;
@@ -269,8 +286,12 @@ class ZipReader {
269
286
  const commentOffset = endOfDirectoryInfo.offset;
270
287
  const commentLength = getUint16(endOfDirectoryView, 20);
271
288
  const appendedDataOffset = commentOffset + END_OF_CENTRAL_DIR_LENGTH + commentLength;
272
- if (reader.size - appendedDataOffset > maxAppendedDataSize) {
273
- throwAmbiguousArchive("appended data");
289
+ const appendedDataLength = reader.size - appendedDataOffset;
290
+ if (appendedDataLength > maxAppendedDataSize) {
291
+ throwAmbiguousArchive(WARNING_APPENDED_DATA);
292
+ }
293
+ if (appendedDataLength > 0) {
294
+ addWarning(warnings, WARNING_APPENDED_DATA);
274
295
  }
275
296
  let lastDiskNumber = getUint16(endOfDirectoryView, 4);
276
297
  const expectedLastDiskNumber = reader.lastDiskNumber || 0;
@@ -324,23 +345,23 @@ class ZipReader {
324
345
  }
325
346
  if (lastDiskNumber == MAX_16_BITS) {
326
347
  lastDiskNumber = getUint32(endOfDirectoryView, 16);
327
- } else if (checkAmbiguity && lastDiskNumber != getUint32(endOfDirectoryView, 16)) {
328
- throwAmbiguousArchive("mismatched zip64 end of central directory record");
348
+ } else if (lastDiskNumber != getUint32(endOfDirectoryView, 16)) {
349
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY);
329
350
  }
330
351
  if (diskNumber == MAX_16_BITS) {
331
352
  diskNumber = getUint32(endOfDirectoryView, 20);
332
- } else if (checkAmbiguity && diskNumber != getUint32(endOfDirectoryView, 20)) {
333
- throwAmbiguousArchive("mismatched zip64 end of central directory record");
353
+ } else if (diskNumber != getUint32(endOfDirectoryView, 20)) {
354
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY);
334
355
  }
335
356
  if (filesLength == MAX_16_BITS) {
336
357
  filesLength = getBigUint64(endOfDirectoryView, 32);
337
- } else if (checkAmbiguity && filesLength != getBigUint64(endOfDirectoryView, 32)) {
338
- throwAmbiguousArchive("mismatched zip64 end of central directory record");
358
+ } else if (filesLength != getBigUint64(endOfDirectoryView, 32)) {
359
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY);
339
360
  }
340
361
  if (directoryDataLength == MAX_32_BITS) {
341
362
  directoryDataLength = getBigUint64(endOfDirectoryView, 40);
342
- } else if (checkAmbiguity && directoryDataLength != getBigUint64(endOfDirectoryView, 40)) {
343
- throwAmbiguousArchive("mismatched zip64 end of central directory record");
363
+ } else if (directoryDataLength != getBigUint64(endOfDirectoryView, 40)) {
364
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY);
344
365
  }
345
366
  directoryDataOffset = getDiskOffset(reader, diskNumber) + getBigUint64(endOfDirectoryView, 48) + prependedDataLength;
346
367
  }
@@ -409,14 +430,22 @@ class ZipReader {
409
430
  declaredDirectoryDataLength = directoryArray.length;
410
431
  decryptedDirectory = true;
411
432
  }
433
+ if (directoryEncryptionInfo && !decryptedDirectory &&
434
+ (directoryArray.length < 4 || getUint32(directoryView, 0) == CENTRAL_FILE_HEADER_SIGNATURE)) {
435
+ addWarning(warnings, WARNING_UNKNOWN_ZIP64_EXTENSIBLE_DATA);
436
+ }
412
437
  startOffset = directoryDataOffset;
413
438
  const filenameEncoding = getOptionValue(zipReader, options, OPTION_FILENAME_ENCODING);
414
439
  const commentEncoding = getOptionValue(zipReader, options, OPTION_COMMENT_ENCODING);
415
- const filenames = checkAmbiguity ? new Set() : UNDEFINED_VALUE;
440
+ const filenames = new Set();
416
441
  let duplicateFilename;
442
+ let previousEntryPosition = -1;
417
443
  const recoverWrappedFilesLength = !checkAmbiguity && !zip64EndOfDirectory;
418
444
  if (!filesLength && recoverWrappedFilesLength) {
419
445
  filesLength = getWrappedFilesLength(directoryView, directoryArray, offset);
446
+ if (filesLength) {
447
+ addWarning(warnings, WARNING_WRAPPED_ENTRIES_COUNT);
448
+ }
420
449
  }
421
450
  for (let indexFile = 0; indexFile < filesLength; indexFile++) {
422
451
  const fileEntry = new ZipEntry(reader, zipReader.options);
@@ -495,15 +524,26 @@ class ZipReader {
495
524
  filename,
496
525
  comment
497
526
  });
498
- readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6);
527
+ if (readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6)) {
528
+ addWarning(warnings, WARNING_MALFORMED_EXTRA_FIELD, filename);
529
+ }
499
530
  fileEntry.offset += prependedDataLength;
500
- startOffset = Math.min(getDiskOffset(reader, fileEntry.diskNumberStart) + fileEntry.offset, startOffset);
501
- if (checkAmbiguity) {
502
- if (filenames.has(fileEntry.filename)) {
503
- duplicateFilename = true;
504
- }
505
- filenames.add(fileEntry.filename);
531
+ const entryPosition = getDiskOffset(reader, fileEntry.diskNumberStart) + fileEntry.offset;
532
+ startOffset = Math.min(entryPosition, startOffset);
533
+ if (entryPosition < previousEntryPosition) {
534
+ addWarning(warnings, WARNING_UNSORTED_CENTRAL_DIRECTORY, filename);
535
+ }
536
+ previousEntryPosition = entryPosition;
537
+ if ((fileEntry.version & MAX_8_BITS) > MAX_KNOWN_VERSION) {
538
+ addWarning(warnings, WARNING_UNKNOWN_VERSION, filename);
539
+ }
540
+ if ((fileEntry.rawBitFlag & BITFLAG_COMPRESSED_PATCHED_DATA) == BITFLAG_COMPRESSED_PATCHED_DATA) {
541
+ addWarning(warnings, WARNING_COMPRESSED_PATCHED_DATA, filename);
542
+ }
543
+ if (filenames.has(fileEntry.filename)) {
544
+ duplicateFilename = true;
506
545
  }
546
+ filenames.add(fileEntry.filename);
507
547
  const unixExternalUpper = (fileEntry.externalFileAttributes >> 16) & MAX_16_BITS;
508
548
  if (fileEntry.unixMode === UNDEFINED_VALUE && (unixExternalUpper & (FILE_ATTR_UNIX_DEFAULT_MASK | FILE_ATTR_UNIX_EXECUTABLE_MASK | FILE_ATTR_UNIX_TYPE_DIR)) != 0) {
509
549
  fileEntry.unixMode = unixExternalUpper;
@@ -541,7 +581,11 @@ class ZipReader {
541
581
  };
542
582
  offset = endOffset;
543
583
  if (indexFile == filesLength - 1 && recoverWrappedFilesLength) {
544
- filesLength += getWrappedFilesLength(directoryView, directoryArray, offset);
584
+ const wrappedFilesLength = getWrappedFilesLength(directoryView, directoryArray, offset);
585
+ if (wrappedFilesLength) {
586
+ filesLength += wrappedFilesLength;
587
+ addWarning(warnings, WARNING_WRAPPED_ENTRIES_COUNT);
588
+ }
545
589
  }
546
590
  const { onprogress } = options;
547
591
  if (onprogress) {
@@ -567,18 +611,22 @@ class ZipReader {
567
611
  zipReader.digitalSignature = digitalSignature;
568
612
  offsetAfterSignature = offset + 6 + digitalSignature.length;
569
613
  }
570
- if (checkAmbiguity && offset != declaredDirectoryDataLength && offsetAfterSignature != declaredDirectoryDataLength) {
571
- throwAmbiguousArchive("trailing central directory data");
614
+ if ((offset != declaredDirectoryDataLength && offsetAfterSignature != declaredDirectoryDataLength) ||
615
+ (!decryptedDirectory && offset != directoryDataLength && offsetAfterSignature != directoryDataLength)) {
616
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_TRAILING_CENTRAL_DIRECTORY_DATA);
572
617
  }
573
618
  if (duplicateFilename) {
574
- throwAmbiguousArchive("duplicate filename");
619
+ reportAmbiguity(checkAmbiguity, warnings, WARNING_DUPLICATE_FILENAME);
575
620
  }
576
621
  const extractPrependedData = getOptionValue(zipReader, options, OPTION_EXTRACT_PREPENDED_DATA);
577
622
  const extractAppendedData = getOptionValue(zipReader, options, OPTION_EXTRACT_APPENDED_DATA);
578
623
  const splitZipSignatureLength = (checkAmbiguity || extractPrependedData) && filesLength &&
579
624
  startOffset == SPLIT_ZIP_FILE_SIGNATURE_LENGTH && await startsWithSplitZipMarker(reader) ? SPLIT_ZIP_FILE_SIGNATURE_LENGTH : 0;
580
625
  if (checkAmbiguity && (prependedDataLength || (filesLength && startOffset > splitZipSignatureLength))) {
581
- throwAmbiguousArchive("prepended data");
626
+ throwAmbiguousArchive(WARNING_PREPENDED_DATA);
627
+ }
628
+ if (prependedDataLength || (filesLength && startOffset > SPLIT_ZIP_FILE_SIGNATURE_LENGTH)) {
629
+ addWarning(warnings, WARNING_PREPENDED_DATA);
582
630
  }
583
631
  if (extractPrependedData) {
584
632
  zipReader.prependedData = startOffset > splitZipSignatureLength ?
@@ -648,8 +696,31 @@ class ZipReaderStream {
648
696
  }
649
697
  }
650
698
 
699
+ async function isZipFile(reader, options = {}) {
700
+ reader = new GenericReader(reader);
701
+ await initStream(reader);
702
+ if (reader.size === UNDEFINED_VALUE || !reader.readUint8Array) {
703
+ reader = new BlobReader(await streamToBlob(reader.readable));
704
+ await initStream(reader);
705
+ }
706
+ if (reader.size < END_OF_CENTRAL_DIR_LENGTH) {
707
+ return false;
708
+ }
709
+ const strictness = getStrictness(options, {});
710
+ const rejectAmbiguousEndOfDirectory = strictness != STRICTNESS_TOLERANT;
711
+ const maxAppendedDataSize = getMaxAppendedDataSize(options[OPTION_MAX_APPENDED_DATA_SIZE], strictness);
712
+ const { endOfDirectoryInfo, endOfDirectoryReachingEndCount } = await findEndOfCentralDirectory(reader, rejectAmbiguousEndOfDirectory, maxAppendedDataSize);
713
+ if (!endOfDirectoryInfo || (strictness == STRICTNESS_STRICT && endOfDirectoryReachingEndCount > 1)) {
714
+ return false;
715
+ }
716
+ const commentLength = getUint16(getDataView(endOfDirectoryInfo), 20);
717
+ const appendedDataOffset = endOfDirectoryInfo.offset + END_OF_CENTRAL_DIR_LENGTH + commentLength;
718
+ return reader.size - appendedDataOffset <= maxAppendedDataSize;
719
+ }
720
+
651
721
  export {
652
722
  ZipReader,
723
+ isZipFile,
653
724
  ZipReaderStream,
654
725
  ERR_BAD_FORMAT,
655
726
  ERR_EOCDR_NOT_FOUND,
@@ -676,7 +747,21 @@ export {
676
747
  ERR_INVALID_FILENAME_VALIDATION,
677
748
  ERR_INVALID_MAX_APPENDED_DATA_SIZE,
678
749
  ERR_UNSUPPORTED_UINT64,
679
- ERR_WORKER_STARTUP_TIMEOUT
750
+ ERR_WORKER_STARTUP_TIMEOUT,
751
+ WARNING_UNSORTED_CENTRAL_DIRECTORY,
752
+ WARNING_UNKNOWN_VERSION,
753
+ WARNING_COMPRESSED_PATCHED_DATA,
754
+ WARNING_MALFORMED_EXTRA_FIELD,
755
+ WARNING_UNKNOWN_ZIP64_EXTENSIBLE_DATA,
756
+ WARNING_WRAPPED_ENTRIES_COUNT,
757
+ WARNING_APPENDED_DATA,
758
+ WARNING_PREPENDED_DATA,
759
+ WARNING_TRAILING_CENTRAL_DIRECTORY_DATA,
760
+ WARNING_DUPLICATE_FILENAME,
761
+ WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY,
762
+ WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG,
763
+ WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD,
764
+ WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES
680
765
  };
681
766
 
682
767
  class ZipEntry {
@@ -710,6 +795,7 @@ class ZipEntry {
710
795
  dataDescriptor
711
796
  } = bitFlag;
712
797
  const localDirectory = fileEntry.localDirectory = {};
798
+ const warnings = fileEntry.warnings = [];
713
799
  const localHeaderOffset = getDiskOffset(reader, diskNumberStart) + offset;
714
800
  const dataArray = await readUint8Array(reader, localHeaderOffset, HEADER_SIZE);
715
801
  const dataView = getDataView(dataArray);
@@ -750,10 +836,10 @@ class ZipEntry {
750
836
  if (checkLocalFilename) {
751
837
  localDirectory.rawFilename = rawLocalFilename;
752
838
  }
753
- readCommonFooter(zipEntry, localDirectory, dataView, 4, true);
754
- if (checkLocalDirectory) {
755
- validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename);
839
+ if (readCommonFooter(zipEntry, localDirectory, dataView, 4, true)) {
840
+ addWarning(warnings, WARNING_MALFORMED_EXTRA_FIELD);
756
841
  }
842
+ validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename, checkLocalDirectory ? UNDEFINED_VALUE : warnings);
757
843
  const { lastAccessDate, creationDate, uid, gid } = localDirectory;
758
844
  if (lastAccessDate) {
759
845
  fileEntry.lastAccessDate = lastAccessDate;
@@ -979,6 +1065,7 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
979
1065
  const extraField = directory.extraField = new Map();
980
1066
  const rawExtraFieldView = getDataView(rawExtraField);
981
1067
  let offsetExtraField = 0;
1068
+ let malformedExtraField = false;
982
1069
  try {
983
1070
  while (offsetExtraField < rawExtraField.length) {
984
1071
  const type = getUint16(rawExtraFieldView, offsetExtraField);
@@ -990,7 +1077,10 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
990
1077
  offsetExtraField += 4 + size;
991
1078
  }
992
1079
  } catch {
993
- // ignored
1080
+ malformedExtraField = true;
1081
+ }
1082
+ if (offsetExtraField > rawExtraField.length) {
1083
+ malformedExtraField = true;
994
1084
  }
995
1085
  const compressionMethod = getUint16(dataView, offset + 4);
996
1086
  Object.assign(directory, {
@@ -1058,6 +1148,7 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
1058
1148
  if (extraFieldUSDZ) {
1059
1149
  directory.extraFieldUSDZ = extraFieldUSDZ;
1060
1150
  }
1151
+ return malformedExtraField;
1061
1152
  }
1062
1153
 
1063
1154
  function readExtraFieldZip64(extraFieldZip64, directory) {
@@ -1519,27 +1610,46 @@ async function readSignature(reader, view, anchoredOffset, signatureOffset, size
1519
1610
  return UNDEFINED_VALUE;
1520
1611
  }
1521
1612
 
1522
- function validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename) {
1613
+ function validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename, warnings) {
1523
1614
  const { rawFilename } = zipEntry;
1615
+ const reject = !warnings;
1524
1616
  const maskedLocalDirectory = zipEntry.decryptedDirectory &&
1525
1617
  (localDirectory.rawBitFlag & BITFLAG_MASKED_LOCAL_HEADERS) == BITFLAG_MASKED_LOCAL_HEADERS;
1526
1618
  if (checkLocalFilename && !maskedLocalDirectory &&
1527
1619
  (rawLocalFilename.length != rawFilename.length ||
1528
1620
  rawLocalFilename.some((byteValue, indexByte) => byteValue != rawFilename[indexByte]))) {
1529
- throwAmbiguousArchive("mismatched local file header (filename)");
1621
+ reportAmbiguity(reject, warnings, "mismatched local file header (filename)");
1530
1622
  }
1531
1623
  if ((localDirectory.rawBitFlag & BITFLAG_AMBIGUITY_MASK) != (zipEntry.rawBitFlag & BITFLAG_AMBIGUITY_MASK)) {
1532
- throwAmbiguousArchive("mismatched local file header (general purpose bit flag)");
1624
+ reportAmbiguity(reject, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG);
1533
1625
  }
1534
1626
  if (localDirectory.compressionMethod != zipEntry.compressionMethod) {
1535
- throwAmbiguousArchive("mismatched local file header (compression method)");
1627
+ reportAmbiguity(reject, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD);
1536
1628
  }
1537
1629
  if (!localDirectory.bitFlag.dataDescriptor && !maskedLocalDirectory &&
1538
1630
  (localDirectory.crc32 || localDirectory.compressedSize || localDirectory.uncompressedSize) &&
1539
1631
  (localDirectory.crc32 != zipEntry.crc32 ||
1540
1632
  localDirectory.compressedSize != zipEntry.compressedSize ||
1541
1633
  localDirectory.uncompressedSize != zipEntry.uncompressedSize)) {
1542
- throwAmbiguousArchive("mismatched local file header (crc32 or sizes)");
1634
+ reportAmbiguity(reject, warnings, WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES);
1635
+ }
1636
+ }
1637
+
1638
+ function reportAmbiguity(reject, warnings, reason) {
1639
+ if (reject) {
1640
+ throwAmbiguousArchive(reason);
1641
+ } else {
1642
+ addWarning(warnings, reason);
1643
+ }
1644
+ }
1645
+
1646
+ function addWarning(warnings, reason, filename) {
1647
+ if (!warnings.some(warning => warning.reason == reason)) {
1648
+ const warning = { reason };
1649
+ if (filename !== UNDEFINED_VALUE) {
1650
+ warning.filename = filename;
1651
+ }
1652
+ warnings.push(warning);
1543
1653
  }
1544
1654
  }
1545
1655
 
@@ -230,6 +230,7 @@ import {
230
230
  OPTION_USDZ,
231
231
  OPTION_UNIX_EXTRA_FIELD_TYPE,
232
232
  OPTION_LOCAL_EXTRA_FIELD,
233
+ OPTION_CENTRAL_EXTRA_FIELD,
233
234
  OPTION_SIGN_CENTRAL_DIRECTORY,
234
235
  TEXT_TYPE_FILENAME,
235
236
  TEXT_TYPE_COMMENT,
@@ -423,6 +424,7 @@ class ZipWriter {
423
424
  rawExtraFieldNTFS: EMPTY_UINT8_ARRAY,
424
425
  rawExtraFieldUnix: EMPTY_UINT8_ARRAY,
425
426
  rawExtraField,
427
+ rawCentralExtraField: EMPTY_UINT8_ARRAY,
426
428
  extendedTimestamp: false,
427
429
  headerArray,
428
430
  headerView
@@ -596,7 +598,6 @@ async function prependZipEntries(zipWriter, reader) {
596
598
 
597
599
  async function addFileEntry(zipWriter, name, reader, options) {
598
600
  options = Object.assign({}, options);
599
- name = name.trim();
600
601
  if (getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY) && !name.endsWith(DIRECTORY_SIGNATURE)) {
601
602
  name += DIRECTORY_SIGNATURE;
602
603
  }
@@ -725,7 +726,6 @@ async function addFile(zipWriter, name, reader, options) {
725
726
  }
726
727
 
727
728
  function resolveAttributes(zipWriter, name, options) {
728
- name = name.trim();
729
729
  let msDosCompatible = getOptionValue(zipWriter, options, PROPERTY_NAME_MS_DOS_COMPATIBLE);
730
730
  let versionMadeBy = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION_MADE_BY, msDosCompatible ? VERSION_MADE_BY_MSDOS : VERSION_MADE_BY_UNIX);
731
731
  const executable = getOptionValue(zipWriter, options, PROPERTY_NAME_EXECUTABLE);
@@ -930,6 +930,7 @@ function resolveMetadata(zipWriter, name, options) {
930
930
  }
931
931
  const rawExtraField = serializeExtraField(options[PROPERTY_NAME_EXTRA_FIELD]);
932
932
  const rawLocalExtraField = serializeExtraField(options[OPTION_LOCAL_EXTRA_FIELD]);
933
+ const rawCentralExtraField = serializeExtraField(options[OPTION_CENTRAL_EXTRA_FIELD]);
933
934
  return {
934
935
  comment,
935
936
  resolvedOptions: {
@@ -965,7 +966,8 @@ function resolveMetadata(zipWriter, name, options) {
965
966
  dataDescriptor,
966
967
  zip64,
967
968
  rawExtraField,
968
- rawLocalExtraField
969
+ rawLocalExtraField,
970
+ rawCentralExtraField
969
971
  }
970
972
  };
971
973
  }
@@ -1107,7 +1109,6 @@ async function getEntriesSize(writerOptions, entries, writeOrderGuaranteed, comm
1107
1109
  let { name } = entry;
1108
1110
  const { size } = entry;
1109
1111
  const options = Object.assign({}, entry.options);
1110
- name = name.trim();
1111
1112
  if (getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY) && !name.endsWith(DIRECTORY_SIGNATURE)) {
1112
1113
  name += DIRECTORY_SIGNATURE;
1113
1114
  }
@@ -1363,6 +1364,7 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1363
1364
  versionMadeBy,
1364
1365
  rawComment,
1365
1366
  rawExtraField,
1367
+ rawCentralExtraField,
1366
1368
  useWebWorkers,
1367
1369
  transferStreams,
1368
1370
  onstart,
@@ -1406,6 +1408,7 @@ async function createFileEntry(reader, writer, { diskNumberStart, lockFileEntry
1406
1408
  rawExtraFieldUnix,
1407
1409
  rawExtraFieldAES,
1408
1410
  rawExtraField,
1411
+ rawCentralExtraField,
1409
1412
  extendedTimestamp,
1410
1413
  msDosCompatible,
1411
1414
  internalFileAttributes,
@@ -1906,6 +1909,7 @@ function createDirectoryRecords(files) {
1906
1909
  rawExtraFieldNTFS,
1907
1910
  rawExtraFieldUnix,
1908
1911
  rawExtraField,
1912
+ rawCentralExtraField,
1909
1913
  extendedTimestamp,
1910
1914
  extraFieldExtendedTimestampFlag,
1911
1915
  lastModDate,
@@ -1960,7 +1964,8 @@ function createDirectoryRecords(files) {
1960
1964
  rawExtraFieldNTFS,
1961
1965
  rawExtraFieldUnix,
1962
1966
  rawExtraFieldTimestamp,
1963
- rawExtraField);
1967
+ rawExtraField,
1968
+ rawCentralExtraField);
1964
1969
  if (extraFieldLength > MAX_16_BITS) {
1965
1970
  throw new Error(ERR_INVALID_EXTRAFIELD_DATA);
1966
1971
  }
@@ -1988,6 +1993,7 @@ async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
1988
1993
  rawExtraFieldNTFS,
1989
1994
  rawExtraFieldUnix,
1990
1995
  rawExtraField,
1996
+ rawCentralExtraField,
1991
1997
  rawComment,
1992
1998
  versionMadeBy,
1993
1999
  headerArray,
@@ -2002,7 +2008,7 @@ async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
2002
2008
  uncompressedSize,
2003
2009
  compressedSize
2004
2010
  } = fileEntry;
2005
- const extraFieldLength = getLength(rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraFieldUnix, rawExtraField);
2011
+ const extraFieldLength = getLength(rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraFieldUnix, rawExtraField, rawCentralExtraField);
2006
2012
  const directoryRecordLength = CENTRAL_FILE_HEADER_LENGTH + getLength(rawFilename, rawComment) + extraFieldLength;
2007
2013
  if (exceedsAvailableSize(writer, offset + directoryRecordLength - directoryDiskOffset)) {
2008
2014
  await writeData(writer, directoryArray.slice(directoryDiskOffset, offset));
@@ -2040,6 +2046,7 @@ async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
2040
2046
  directoryRecord.writeBytes(rawExtraFieldNTFS);
2041
2047
  directoryRecord.writeBytes(rawExtraFieldUnix);
2042
2048
  directoryRecord.writeBytes(rawExtraField);
2049
+ directoryRecord.writeBytes(rawCentralExtraField);
2043
2050
  directoryRecord.writeBytes(rawComment);
2044
2051
  arraySet(directoryArray, directoryRecord.array, offset);
2045
2052
  offset += directoryRecordLength;
@@ -31,6 +31,7 @@ import "./core/codec-worker-web.js";
31
31
  export {
32
32
  ZipReader,
33
33
  ZipReaderStream,
34
+ isZipFile,
34
35
  ERR_BAD_FORMAT,
35
36
  ERR_EOCDR_NOT_FOUND,
36
37
  ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND,
@@ -56,5 +57,19 @@ export {
56
57
  ERR_INVALID_FILENAME_VALIDATION,
57
58
  ERR_INVALID_MAX_APPENDED_DATA_SIZE,
58
59
  ERR_UNSUPPORTED_UINT64,
59
- ERR_WORKER_STARTUP_TIMEOUT
60
+ ERR_WORKER_STARTUP_TIMEOUT,
61
+ WARNING_UNSORTED_CENTRAL_DIRECTORY,
62
+ WARNING_UNKNOWN_VERSION,
63
+ WARNING_COMPRESSED_PATCHED_DATA,
64
+ WARNING_MALFORMED_EXTRA_FIELD,
65
+ WARNING_UNKNOWN_ZIP64_EXTENSIBLE_DATA,
66
+ WARNING_WRAPPED_ENTRIES_COUNT,
67
+ WARNING_APPENDED_DATA,
68
+ WARNING_PREPENDED_DATA,
69
+ WARNING_TRAILING_CENTRAL_DIRECTORY_DATA,
70
+ WARNING_DUPLICATE_FILENAME,
71
+ WARNING_MISMATCHED_ZIP64_END_OF_CENTRAL_DIRECTORY,
72
+ WARNING_MISMATCHED_LOCAL_FILE_HEADER_BIT_FLAG,
73
+ WARNING_MISMATCHED_LOCAL_FILE_HEADER_COMPRESSION_METHOD,
74
+ WARNING_MISMATCHED_LOCAL_FILE_HEADER_CRC32_OR_SIZES
60
75
  } from "./core/zip-reader.js";
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "A JavaScript library to zip and unzip files in the browser, Deno and Node.js",
4
4
  "author": "Gildas Lormeau",
5
5
  "license": "BSD-3-Clause",
6
- "version": "2.8.58",
6
+ "version": "2.8.59",
7
7
  "type": "module",
8
8
  "keywords": [
9
9
  "zip",