@zip.js/zip.js 2.8.51 → 2.8.53

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 (41) hide show
  1. package/README.md +8 -3
  2. package/deno.json +1 -1
  3. package/dist/zip-core-external.js +241 -95
  4. package/dist/zip-core-external.min.js +1 -1
  5. package/dist/zip-core.js +243 -94
  6. package/dist/zip-core.min.js +1 -1
  7. package/dist/zip-fs-core-external.js +564 -199
  8. package/dist/zip-fs-core-external.min.js +1 -1
  9. package/dist/zip-fs-core.js +551 -194
  10. package/dist/zip-fs-core.min.js +1 -1
  11. package/dist/zip-fs-external.js +564 -199
  12. package/dist/zip-fs-external.min.js +1 -1
  13. package/dist/zip-fs-native.js +567 -198
  14. package/dist/zip-fs-native.min.js +1 -1
  15. package/dist/zip-fs.js +567 -198
  16. package/dist/zip-fs.min.js +1 -1
  17. package/dist/zip-legacy.js +243 -94
  18. package/dist/zip-legacy.min.js +1 -1
  19. package/dist/zip-native.js +243 -94
  20. package/dist/zip-native.min.js +1 -1
  21. package/dist/zip.js +243 -94
  22. package/dist/zip.min.js +1 -1
  23. package/eslint.config.mjs +1 -1
  24. package/index-native.cjs +567 -198
  25. package/index-native.min.js +1 -1
  26. package/index.cjs +567 -198
  27. package/index.d.cts +551 -45
  28. package/index.d.ts +551 -45
  29. package/index.min.js +1 -1
  30. package/lib/core/codec-worker-web.js +13 -4
  31. package/lib/core/codec-worker.js +2 -0
  32. package/lib/core/constants.js +9 -0
  33. package/lib/core/io.js +43 -4
  34. package/lib/core/options.js +2 -0
  35. package/lib/core/util/compatible-streams.js +3 -8
  36. package/lib/core/zip-entry.js +16 -0
  37. package/lib/core/zip-fs.js +236 -107
  38. package/lib/core/zip-reader.js +106 -40
  39. package/lib/core/zip-writer.js +171 -41
  40. package/lib/zip-core-writer.js +3 -0
  41. package/package.json +9 -7
@@ -76,6 +76,7 @@ import {
76
76
  FILE_ATTR_MSDOS_ARCHIVE_MASK,
77
77
  FILE_ATTR_UNIX_TYPE_MASK,
78
78
  FILE_ATTR_UNIX_TYPE_DIR,
79
+ FILE_ATTR_UNIX_TYPE_SYMLINK,
79
80
  FILE_ATTR_UNIX_EXECUTABLE_MASK,
80
81
  FILE_ATTR_UNIX_DEFAULT_MASK,
81
82
  DIRECTORY_SIGNATURE,
@@ -111,7 +112,8 @@ import {
111
112
  readUint8Array,
112
113
  BlobReader,
113
114
  GenericReader,
114
- GenericWriter
115
+ GenericWriter,
116
+ ownsWritable
115
117
  } from "./io.js";
116
118
  import { decodeText } from "./util/decode-text.js";
117
119
  import { getDataView } from "./util/array.js";
@@ -148,6 +150,7 @@ import {
148
150
  OPTION_CHECK_OVERLAPPING_ENTRY_ONLY,
149
151
  OPTION_CHECK_OVERLAPPING_ENTRY,
150
152
  OPTION_CHECK_AMBIGUITY,
153
+ OPTION_CHECK_LOCAL_DIRECTORY,
151
154
  OPTION_CHECK_SIGNATURE,
152
155
  OPTION_CHECK_CRC32,
153
156
  OPTION_CHECK_AUTHENTICATION_CODE,
@@ -234,7 +237,7 @@ class ZipReader {
234
237
  if (reader.size < END_OF_CENTRAL_DIR_LENGTH) {
235
238
  throw new Error(ERR_BAD_FORMAT);
236
239
  }
237
- const strictness = getStrictness(getOptionValue(zipReader, options, OPTION_STRICTNESS), getOptionValue(zipReader, options, OPTION_CHECK_AMBIGUITY));
240
+ const strictness = getStrictness(options, zipReader.options);
238
241
  const checkAmbiguity = strictness == STRICTNESS_STRICT;
239
242
  const rejectAmbiguousEndOfDirectory = strictness != STRICTNESS_TOLERANT;
240
243
  const maxAppendedDataSize = getMaxAppendedDataSize(getOptionValue(zipReader, options, OPTION_MAX_APPENDED_DATA_SIZE), strictness);
@@ -456,6 +459,7 @@ class ZipReader {
456
459
  index: indexFile,
457
460
  versionMadeBy,
458
461
  msDosCompatible,
462
+ zip64: false,
459
463
  compressedSize: 0,
460
464
  uncompressedSize: 0,
461
465
  commentLength,
@@ -489,15 +493,18 @@ class ZipReader {
489
493
  const setuid = Boolean(fileEntry.unixMode & FILE_ATTR_UNIX_SETUID_MASK);
490
494
  const setgid = Boolean(fileEntry.unixMode & FILE_ATTR_UNIX_SETGID_MASK);
491
495
  const sticky = Boolean(fileEntry.unixMode & FILE_ATTR_UNIX_STICKY_MASK);
492
- const executable = (fileEntry.unixMode !== UNDEFINED_VALUE)
496
+ const unixType = fileEntry.unixMode === UNDEFINED_VALUE ? unixExternalUpper : fileEntry.unixMode;
497
+ const symlink = (unixType & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_SYMLINK;
498
+ const executable = !symlink && ((fileEntry.unixMode !== UNDEFINED_VALUE)
493
499
  ? ((fileEntry.unixMode & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)
494
- : (unixCompatible && ((unixExternalUpper & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0));
500
+ : (unixCompatible && ((unixExternalUpper & FILE_ATTR_UNIX_EXECUTABLE_MASK) != 0)));
495
501
  const modeIsDir = fileEntry.unixMode !== UNDEFINED_VALUE && ((fileEntry.unixMode & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_DIR);
496
502
  const upperIsDir = ((unixExternalUpper & FILE_ATTR_UNIX_TYPE_MASK) == FILE_ATTR_UNIX_TYPE_DIR);
497
503
  Object.assign(fileEntry, {
498
504
  setuid,
499
505
  setgid,
500
506
  sticky,
507
+ symlink,
501
508
  unixExternalUpper,
502
509
  internalFileAttribute: fileEntry.internalFileAttributes,
503
510
  externalFileAttribute: fileEntry.externalFileAttributes,
@@ -563,6 +570,10 @@ class ZipReader {
563
570
  }
564
571
 
565
572
  async close() {
573
+ const { reader } = this;
574
+ if (!reader.readUint8Array && reader.readable && !reader.readable.locked) {
575
+ await reader.readable.cancel();
576
+ }
566
577
  }
567
578
  }
568
579
 
@@ -688,9 +699,12 @@ class ZipEntry {
688
699
  extraFieldLength,
689
700
  filenameLength
690
701
  } = localDirectory;
691
- const checkAmbiguity = getStrictness(getOptionValue(zipEntry, options, OPTION_STRICTNESS), getOptionValue(zipEntry, options, OPTION_CHECK_AMBIGUITY)) == STRICTNESS_STRICT;
702
+ const checkLocalDirectoryOption = getOptionValue(zipEntry, options, OPTION_CHECK_LOCAL_DIRECTORY);
703
+ const entryStrictness = getStrictness(options, zipEntry.options);
704
+ const checkLocalDirectory = getCheckLocalDirectory(checkLocalDirectoryOption, entryStrictness);
705
+ const checkLocalFilename = getCheckLocalFilename(checkLocalDirectoryOption, entryStrictness);
692
706
  let rawLocalFilename = EMPTY_UINT8_ARRAY;
693
- if (checkAmbiguity && (filenameLength || extraFieldLength)) {
707
+ if (checkLocalFilename && (filenameLength || extraFieldLength)) {
694
708
  const trailingDataArray = await readUint8Array(reader, localHeaderOffset + HEADER_SIZE, filenameLength + extraFieldLength);
695
709
  rawLocalFilename = trailingDataArray.subarray(0, filenameLength);
696
710
  localDirectory.rawExtraField = trailingDataArray.subarray(filenameLength);
@@ -699,17 +713,26 @@ class ZipEntry {
699
713
  await readUint8Array(reader, localHeaderOffset + HEADER_SIZE + filenameLength, extraFieldLength) :
700
714
  EMPTY_UINT8_ARRAY;
701
715
  }
716
+ if (checkLocalFilename) {
717
+ localDirectory.rawFilename = rawLocalFilename;
718
+ }
702
719
  readCommonFooter(zipEntry, localDirectory, dataView, 4, true);
703
- if (checkAmbiguity) {
704
- checkLocalDirectory(zipEntry, localDirectory, rawLocalFilename);
720
+ if (checkLocalDirectory) {
721
+ validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename);
705
722
  }
706
- const { lastAccessDate, creationDate } = localDirectory;
723
+ const { lastAccessDate, creationDate, uid, gid } = localDirectory;
707
724
  if (lastAccessDate) {
708
725
  fileEntry.lastAccessDate = lastAccessDate;
709
726
  }
710
727
  if (creationDate) {
711
728
  fileEntry.creationDate = creationDate;
712
729
  }
730
+ if (uid !== UNDEFINED_VALUE && fileEntry.uid === UNDEFINED_VALUE) {
731
+ fileEntry.uid = uid;
732
+ }
733
+ if (gid !== UNDEFINED_VALUE && fileEntry.gid === UNDEFINED_VALUE) {
734
+ fileEntry.gid = gid;
735
+ }
713
736
  const encrypted = zipEntry.encrypted && localDirectory.encrypted && !passThrough;
714
737
  const zipCrypto = encrypted && !extraFieldAES;
715
738
  if (!passThrough) {
@@ -816,7 +839,7 @@ class ZipEntry {
816
839
  throw error;
817
840
  }
818
841
  } finally {
819
- const preventClose = getOptionValue(zipEntry, options, OPTION_PREVENT_CLOSE);
842
+ const preventClose = !ownsWritable(writer) && getOptionValue(zipEntry, options, OPTION_PREVENT_CLOSE);
820
843
  if (!preventClose && writable && !writable.locked) {
821
844
  const writableWriter = writable.getWriter();
822
845
  if (abortError) {
@@ -945,10 +968,12 @@ function readCommonFooter(fileEntry, directory, dataView, offset, localDirectory
945
968
  directory.extraFieldNTFS = extraFieldNTFS;
946
969
  }
947
970
  const extraFieldUnix = extraField.get(EXTRAFIELD_TYPE_UNIX);
971
+ let unixIdsRead;
948
972
  if (extraFieldUnix) {
949
- readExtraFieldUnix(extraFieldUnix, directory, false);
973
+ unixIdsRead = readExtraFieldUnix(extraFieldUnix, directory, false);
950
974
  directory.extraFieldUnix = extraFieldUnix;
951
- } else {
975
+ }
976
+ if (!unixIdsRead) {
952
977
  const extraFieldInfoZip = extraField.get(EXTRAFIELD_TYPE_INFOZIP);
953
978
  if (extraFieldInfoZip) {
954
979
  readExtraFieldUnix(extraFieldInfoZip, directory, true);
@@ -1051,7 +1076,7 @@ function readExtraFieldNTFS(extraFieldNTFS, directory) {
1051
1076
  const creationDate = getDateNTFS(rawCreationDate);
1052
1077
  const extraFieldData = { lastModDate, lastAccessDate, creationDate };
1053
1078
  Object.assign(extraFieldNTFS, extraFieldData);
1054
- Object.assign(directory, extraFieldData);
1079
+ Object.assign(directory, extraFieldData, { rawLastAccessDate, rawCreationDate });
1055
1080
  }
1056
1081
  }
1057
1082
 
@@ -1060,8 +1085,8 @@ function readExtraFieldUnixDates(extraField, directory) {
1060
1085
  return;
1061
1086
  }
1062
1087
  const extraFieldView = getDataView(extraField.data);
1063
- const lastAccessDate = new Date(getUint32(extraFieldView, 0) * 1000);
1064
- const lastModDate = new Date(getUint32(extraFieldView, 4) * 1000);
1088
+ const lastAccessDate = new Date((getUint32(extraFieldView, 0) | 0) * 1000);
1089
+ const lastModDate = new Date((getUint32(extraFieldView, 4) | 0) * 1000);
1065
1090
  const extraFieldData = { lastAccessDate, lastModDate };
1066
1091
  if (extraField.data.length >= 12) {
1067
1092
  extraFieldData.uid = getUint16(extraFieldView, 8);
@@ -1095,6 +1120,7 @@ function readExtraFieldUnix(extraField, directory, isInfoZip) {
1095
1120
  if (gid !== UNDEFINED_VALUE) {
1096
1121
  directory.gid = gid;
1097
1122
  }
1123
+ return uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE;
1098
1124
  } catch {
1099
1125
  // ignored
1100
1126
  }
@@ -1167,26 +1193,25 @@ async function detectOverlappingEntry({
1167
1193
  }
1168
1194
  if (dataDescriptorLength) {
1169
1195
  const dataDescriptorArray = await readUint8Array(reader, dataOffset + compressedSize, dataDescriptorLength + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH);
1170
- const dataDescriptorSignature = dataDescriptorArray.length == dataDescriptorLength + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH &&
1171
- getUint32(getDataView(dataDescriptorArray), 0) == DATA_DESCRIPTOR_RECORD_SIGNATURE;
1172
- if (dataDescriptorSignature) {
1173
- const readCrc32 = getUint32(getDataView(dataDescriptorArray), 4);
1174
- let readCompressedSize;
1175
- let readUncompressedSize;
1176
- if (extraFieldZip64) {
1177
- readCompressedSize = getBigUint64(getDataView(dataDescriptorArray), 8);
1178
- readUncompressedSize = getBigUint64(getDataView(dataDescriptorArray), 16);
1179
- } else {
1180
- readCompressedSize = getUint32(getDataView(dataDescriptorArray), 8);
1181
- readUncompressedSize = getUint32(getDataView(dataDescriptorArray), 12);
1182
- }
1183
- const matchCrc32 = (fileEntry.encrypted && !fileEntry.zipCrypto) || readCrc32 == crc32;
1196
+ const dataDescriptorView = getDataView(dataDescriptorArray);
1197
+ let signature = dataDescriptorArray.length == dataDescriptorLength + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH &&
1198
+ getUint32(dataDescriptorView, 0) == DATA_DESCRIPTOR_RECORD_SIGNATURE;
1199
+ if (signature) {
1200
+ const signedDataDescriptor = readDataDescriptor(dataDescriptorView, DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH, extraFieldZip64);
1201
+ const matchCrc32 = (fileEntry.encrypted && !fileEntry.zipCrypto) || signedDataDescriptor.crc32 == crc32;
1184
1202
  if (matchCrc32 &&
1185
- readCompressedSize == compressedSize &&
1186
- readUncompressedSize == uncompressedSize) {
1203
+ signedDataDescriptor.compressedSize == compressedSize &&
1204
+ signedDataDescriptor.uncompressedSize == uncompressedSize) {
1187
1205
  dataDescriptorLength += DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH;
1206
+ } else {
1207
+ signature = false;
1188
1208
  }
1189
1209
  }
1210
+ if (dataDescriptorArray.length >= dataDescriptorLength) {
1211
+ const localDataDescriptor = readDataDescriptor(dataDescriptorView, signature ? DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH : 0, extraFieldZip64);
1212
+ localDataDescriptor.signature = signature;
1213
+ fileEntry.localDirectory.dataDescriptor = localDataDescriptor;
1214
+ }
1190
1215
  }
1191
1216
  const range = {
1192
1217
  start: offset,
@@ -1203,6 +1228,20 @@ async function detectOverlappingEntry({
1203
1228
  readRanges.set(index, range);
1204
1229
  }
1205
1230
 
1231
+ function readDataDescriptor(dataDescriptorView, offset, extraFieldZip64) {
1232
+ const crc32 = getUint32(dataDescriptorView, offset);
1233
+ let compressedSize;
1234
+ let uncompressedSize;
1235
+ if (extraFieldZip64) {
1236
+ compressedSize = getBigUint64(dataDescriptorView, offset + 4);
1237
+ uncompressedSize = getBigUint64(dataDescriptorView, offset + 12);
1238
+ } else {
1239
+ compressedSize = getUint32(dataDescriptorView, offset + 4);
1240
+ uncompressedSize = getUint32(dataDescriptorView, offset + 8);
1241
+ }
1242
+ return { crc32, compressedSize, uncompressedSize };
1243
+ }
1244
+
1206
1245
  function getDiskOffset(reader, diskNumber) {
1207
1246
  return reader.getDiskOffset ? reader.getDiskOffset(diskNumber) : 0;
1208
1247
  }
@@ -1211,14 +1250,40 @@ function isStrictnessValue(value) {
1211
1250
  return value === STRICTNESS_STRICT || value === STRICTNESS_BALANCED || value === STRICTNESS_TOLERANT;
1212
1251
  }
1213
1252
 
1214
- function getStrictness(strictness, checkAmbiguity) {
1215
- if (strictness === UNDEFINED_VALUE) {
1216
- return checkAmbiguity ? STRICTNESS_STRICT : STRICTNESS_BALANCED;
1253
+ function getStrictness(options, inheritedOptions) {
1254
+ return resolveStrictness(options, resolveStrictness(inheritedOptions, STRICTNESS_BALANCED));
1255
+ }
1256
+
1257
+ function resolveStrictness(options, inheritedStrictness) {
1258
+ const strictness = options[OPTION_STRICTNESS];
1259
+ if (strictness !== UNDEFINED_VALUE) {
1260
+ if (!isStrictnessValue(strictness)) {
1261
+ throw new Error(ERR_INVALID_STRICTNESS);
1262
+ }
1263
+ return strictness;
1264
+ }
1265
+ const checkAmbiguity = options[OPTION_CHECK_AMBIGUITY];
1266
+ if (checkAmbiguity === UNDEFINED_VALUE) {
1267
+ return inheritedStrictness;
1217
1268
  }
1218
- if (!isStrictnessValue(strictness)) {
1219
- throw new Error(ERR_INVALID_STRICTNESS);
1269
+ if (checkAmbiguity) {
1270
+ return STRICTNESS_STRICT;
1271
+ }
1272
+ return inheritedStrictness == STRICTNESS_TOLERANT ? STRICTNESS_TOLERANT : STRICTNESS_BALANCED;
1273
+ }
1274
+
1275
+ function getCheckLocalDirectory(checkLocalDirectory, strictness) {
1276
+ if (checkLocalDirectory === UNDEFINED_VALUE) {
1277
+ return strictness != STRICTNESS_TOLERANT;
1278
+ }
1279
+ return Boolean(checkLocalDirectory);
1280
+ }
1281
+
1282
+ function getCheckLocalFilename(checkLocalFilename, strictness) {
1283
+ if (checkLocalFilename === UNDEFINED_VALUE) {
1284
+ return strictness == STRICTNESS_STRICT;
1220
1285
  }
1221
- return strictness;
1286
+ return Boolean(checkLocalFilename);
1222
1287
  }
1223
1288
 
1224
1289
  function getFilenameValidation(filenameValidation, strictness) {
@@ -1366,10 +1431,11 @@ async function readSignature(reader, view, anchoredOffset, signatureOffset, size
1366
1431
  return UNDEFINED_VALUE;
1367
1432
  }
1368
1433
 
1369
- function checkLocalDirectory(zipEntry, localDirectory, rawLocalFilename) {
1434
+ function validateLocalDirectory(zipEntry, localDirectory, rawLocalFilename, checkLocalFilename) {
1370
1435
  const { rawFilename } = zipEntry;
1371
- if (rawLocalFilename.length != rawFilename.length ||
1372
- rawLocalFilename.some((byteValue, indexByte) => byteValue != rawFilename[indexByte])) {
1436
+ if (checkLocalFilename &&
1437
+ (rawLocalFilename.length != rawFilename.length ||
1438
+ rawLocalFilename.some((byteValue, indexByte) => byteValue != rawFilename[indexByte]))) {
1373
1439
  throwAmbiguousArchive("mismatched local file header (filename)");
1374
1440
  }
1375
1441
  if ((localDirectory.rawBitFlag & BITFLAG_AMBIGUITY_MASK) != (zipEntry.rawBitFlag & BITFLAG_AMBIGUITY_MASK)) {