@zip.js/zip.js 2.8.37 → 2.8.39

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.
@@ -45,6 +45,7 @@
45
45
  const LOCAL_FILE_HEADER_SIGNATURE = 0x04034b50;
46
46
  const SPLIT_ZIP_FILE_SIGNATURE = 0x08074b50;
47
47
  const DATA_DESCRIPTOR_RECORD_SIGNATURE = SPLIT_ZIP_FILE_SIGNATURE;
48
+ const ARCHIVE_EXTRA_DATA_SIGNATURE = 0x08064b50;
48
49
  const CENTRAL_FILE_HEADER_SIGNATURE = 0x02014b50;
49
50
  const END_OF_CENTRAL_DIR_SIGNATURE = 0x06054b50;
50
51
  const ZIP64_END_OF_CENTRAL_DIR_SIGNATURE = 0x06064b50;
@@ -76,6 +77,7 @@
76
77
  const BITFLAG_LEVEL_FAST_MASK = 0b100;
77
78
  const BITFLAG_LEVEL_SUPER_FAST_MASK = 0b110;
78
79
  const BITFLAG_DATA_DESCRIPTOR = 0b1000;
80
+ const BITFLAG_STRONG_ENCRYPTION = 0b1000000;
79
81
  const BITFLAG_LANG_ENCODING_FLAG = 0b100000000000;
80
82
  const FILE_ATTR_MSDOS_DIR_MASK = 0b10000;
81
83
  const FILE_ATTR_MSDOS_READONLY_MASK = 0x01;
@@ -159,6 +161,7 @@
159
161
  maxWorkers,
160
162
  terminateWorkerTimeout: 5000,
161
163
  workerStarvationTimeout: 5000,
164
+ workerStartupTimeout: 5000,
162
165
  useWebWorkers: true,
163
166
  useCompressionStream: true,
164
167
  transferStreams: true,
@@ -987,7 +990,7 @@
987
990
 
988
991
  getDiskOffset(diskNumber) {
989
992
  const { diskOffsets, size } = this;
990
- const diskOffset = diskOffsets.at(diskNumber);
993
+ const diskOffset = diskOffsets[diskNumber];
991
994
  return diskOffset === UNDEFINED_VALUE ? size : diskOffset;
992
995
  }
993
996
 
@@ -2575,7 +2578,7 @@
2575
2578
  if (zipCrypto.password || zipCrypto.rawPassword) {
2576
2579
  const decryptedHeader = decrypt(zipCrypto, chunk.subarray(0, HEADER_LENGTH));
2577
2580
  zipCrypto.password = zipCrypto.rawPassword = null;
2578
- if ((decryptedHeader.at(-1) ^ zipCrypto.passwordVerification) != 0) {
2581
+ if ((decryptedHeader[HEADER_LENGTH - 1] ^ zipCrypto.passwordVerification) != 0) {
2579
2582
  throw new Error(ERR_INVALID_PASSWORD);
2580
2583
  }
2581
2584
  chunk = chunk.subarray(HEADER_LENGTH);
@@ -2748,7 +2751,7 @@
2748
2751
  readable = pipeThroughBackpressured(readable, new CompressionStream(FORMAT_GZIP));
2749
2752
  readable = pipeThrough(readable, gzipCrc32Stream);
2750
2753
  } else {
2751
- readable = pipeThroughCommpressionStream(readable, useCompressionStream, { level, chunkSize }, CompressionStream, CompressionStreamZlib, CompressionStream);
2754
+ readable = pipeThroughCompressionStream(readable, useCompressionStream, { level, chunkSize }, CompressionStream, CompressionStreamZlib);
2752
2755
  }
2753
2756
  }
2754
2757
  if (encrypted) {
@@ -2833,7 +2836,7 @@
2833
2836
  if (codecStreams) {
2834
2837
  readable = pipeThroughBackpressured(readable, createCodecStream(codecStreams.DecompressionStream, format, { chunkSize }));
2835
2838
  } else {
2836
- readable = pipeThroughCommpressionStream(readable, useCompressionStream, { chunkSize, deflate64 }, DecompressionStream, DecompressionStreamZlib, DecompressionStream);
2839
+ readable = pipeThroughCompressionStream(readable, useCompressionStream, { chunkSize, deflate64 }, DecompressionStream, DecompressionStreamZlib);
2837
2840
  }
2838
2841
  readable = mapInflateStreamError(readable);
2839
2842
  }
@@ -2852,6 +2855,25 @@
2852
2855
  }
2853
2856
  }
2854
2857
 
2858
+ const deflateRawSupportByStream = new Map();
2859
+
2860
+ function supportsDeflateRaw(StreamClass) {
2861
+ if (!StreamClass) {
2862
+ return false;
2863
+ }
2864
+ let supported = deflateRawSupportByStream.get(StreamClass);
2865
+ if (supported === UNDEFINED_VALUE) {
2866
+ try {
2867
+ new StreamClass(FORMAT_DEFLATE_RAW);
2868
+ supported = true;
2869
+ } catch {
2870
+ supported = false;
2871
+ }
2872
+ deflateRawSupportByStream.set(StreamClass, supported);
2873
+ }
2874
+ return supported;
2875
+ }
2876
+
2855
2877
  function setReadable(stream, readable, flush) {
2856
2878
  readable = pipeThrough(readable, new TransformStream({ flush }));
2857
2879
  Object.defineProperty(stream, "readable", {
@@ -2868,21 +2890,17 @@
2868
2890
  return new CodecStreamClass(format, options);
2869
2891
  }
2870
2892
 
2871
- function pipeThroughCommpressionStream(readable, useCompressionStream, options, CompressionStreamNative, CompressionStreamZlib, CompressionStream) {
2872
- const Stream = useCompressionStream && CompressionStreamNative ? CompressionStreamNative : CompressionStreamZlib || CompressionStream;
2893
+ function pipeThroughCompressionStream(readable, useCompressionStream, options, CompressionStreamNative, CompressionStreamZlib) {
2894
+ const Stream = useCompressionStream && CompressionStreamNative ?
2895
+ CompressionStreamNative :
2896
+ CompressionStreamZlib || CompressionStreamNative;
2873
2897
  const format = options.deflate64 ? FORMAT_DEFLATE64_RAW : FORMAT_DEFLATE_RAW;
2874
2898
  let codecStream;
2875
2899
  try {
2876
2900
  codecStream = new Stream(format, options);
2877
2901
  } catch (error) {
2878
- if (useCompressionStream) {
2879
- if (CompressionStreamZlib) {
2880
- codecStream = new CompressionStreamZlib(format, options);
2881
- } else if (CompressionStream) {
2882
- codecStream = new CompressionStream(format, options);
2883
- } else {
2884
- throw error;
2885
- }
2902
+ if (useCompressionStream && CompressionStreamZlib && Stream != CompressionStreamZlib) {
2903
+ codecStream = new CompressionStreamZlib(format, options);
2886
2904
  } else {
2887
2905
  throw error;
2888
2906
  }
@@ -3143,6 +3161,7 @@
3143
3161
  const MODULE_WORKER_OPTIONS = { type: "module" };
3144
3162
  const ERROR_EVENT_TYPE = "error";
3145
3163
  const MESSAGE_ERROR_EVENT_TYPE = "messageerror";
3164
+ const ERR_WORKER_STARTUP_TIMEOUT = "Worker startup timeout";
3146
3165
 
3147
3166
  let webWorkerSupported, webWorkerSource, webWorkerURI, webWorkerOptions;
3148
3167
  let transferStreamsSupported = true;
@@ -3246,7 +3265,7 @@
3246
3265
  }
3247
3266
 
3248
3267
  function createWebWorkerInterface(workerData, config) {
3249
- const { baseURI, chunkSize } = config;
3268
+ const { baseURI, chunkSize, workerStartupTimeout } = config;
3250
3269
  let { wasmURI } = config;
3251
3270
 
3252
3271
  if (!workerData.interface) {
@@ -3263,9 +3282,26 @@
3263
3282
  }
3264
3283
  Object.assign(workerData, {
3265
3284
  worker,
3285
+ workerAlive: false,
3266
3286
  terminated: false,
3267
3287
  interface: {
3268
- run: () => runWebWorker(workerData, { chunkSize, wasmURI, baseURI })
3288
+ run: async () => {
3289
+ try {
3290
+ return await runWebWorker(workerData, { chunkSize, wasmURI, baseURI, workerStartupTimeout });
3291
+ } catch (error) {
3292
+ if (error && error.workerStartupFailed) {
3293
+ webWorkerSupported = false;
3294
+ const { reader } = workerData;
3295
+ if (reader) {
3296
+ reader.releaseLock();
3297
+ }
3298
+ workerData.reader = null;
3299
+ workerData.writer = null;
3300
+ return runWorker$1(workerData, config);
3301
+ }
3302
+ throw error;
3303
+ }
3304
+ }
3269
3305
  }
3270
3306
  });
3271
3307
  }
@@ -3275,15 +3311,23 @@
3275
3311
  async function runWorker$1({ options, readable, writable, onTaskFinished }, config) {
3276
3312
  let codecStream;
3277
3313
  try {
3278
- if (!options.useCompressionStream) {
3279
- try {
3280
- await initModule(config);
3281
- } catch {
3282
- const ZlibStream = options.codecType.startsWith(CODEC_DEFLATE) ?
3283
- config.CompressionStreamZlib :
3284
- config.DecompressionStreamZlib;
3285
- if (!ZlibStream || ZlibStream.requiresModule) {
3286
- options.useCompressionStream = true;
3314
+ if (options.compressed && !options.format) {
3315
+ const deflate = options.codecType.startsWith(CODEC_DEFLATE);
3316
+ const ZlibStream = deflate ? config.CompressionStreamZlib : config.DecompressionStreamZlib;
3317
+ const NativeStream = deflate ? config.CompressionStream : config.DecompressionStream;
3318
+ if (!options.useCompressionStream) {
3319
+ try {
3320
+ await initModule(config);
3321
+ } catch {
3322
+ if (!ZlibStream || ZlibStream.requiresModule) {
3323
+ options.useCompressionStream = true;
3324
+ }
3325
+ }
3326
+ } else if (ZlibStream && ZlibStream.requiresModule && !supportsDeflateRaw(NativeStream)) {
3327
+ try {
3328
+ await initModule(config);
3329
+ } catch {
3330
+ // ignored
3287
3331
  }
3288
3332
  }
3289
3333
  }
@@ -3352,6 +3396,10 @@
3352
3396
  writer: writable.getWriter()
3353
3397
  });
3354
3398
  }
3399
+ const { workerStartupTimeout } = config;
3400
+ if (!workerData.workerAlive && Number.isFinite(workerStartupTimeout) && workerStartupTimeout >= 0) {
3401
+ workerData.startupTimeout = setTimeout(() => onStartupTimeout(workerData), workerStartupTimeout);
3402
+ }
3355
3403
  try {
3356
3404
  const resultValue = await result;
3357
3405
  await closeWritable();
@@ -3464,28 +3512,68 @@
3464
3512
  } else {
3465
3513
  worker = new Worker(webWorkerURI, webWorkerOptions);
3466
3514
  }
3467
- worker.addEventListener(MESSAGE_EVENT_TYPE, event => onMessage(event, workerData));
3515
+ worker.addEventListener(MESSAGE_EVENT_TYPE, event => {
3516
+ workerData.workerAlive = true;
3517
+ clearStartupTimeout(workerData);
3518
+ onMessage(event, workerData);
3519
+ });
3468
3520
  worker.addEventListener(ERROR_EVENT_TYPE, event => onWorkerError(event, workerData));
3469
3521
  worker.addEventListener(MESSAGE_ERROR_EVENT_TYPE, event => onWorkerError(event, workerData));
3470
3522
  return worker;
3471
3523
  }
3472
3524
 
3525
+ function onStartupTimeout(workerData) {
3526
+ workerData.startupTimeout = null;
3527
+ if (workerData.workerAlive) {
3528
+ return;
3529
+ }
3530
+ const { rejectResult, writer } = workerData;
3531
+ terminateWorker$1(workerData);
3532
+ workerData.worker = null;
3533
+ if (rejectResult) {
3534
+ const error = new Error(ERR_WORKER_STARTUP_TIMEOUT);
3535
+ error.workerStartupFailed = true;
3536
+ rejectResult(error);
3537
+ if (writer) {
3538
+ writer.releaseLock();
3539
+ }
3540
+ }
3541
+ }
3542
+
3543
+ function clearStartupTimeout(workerData) {
3544
+ const { startupTimeout } = workerData;
3545
+ if (startupTimeout) {
3546
+ clearTimeout(startupTimeout);
3547
+ workerData.startupTimeout = null;
3548
+ }
3549
+ }
3550
+
3473
3551
  function onWorkerError(event, workerData) {
3474
3552
  if (event.preventDefault) {
3475
3553
  event.preventDefault();
3476
3554
  }
3477
- const { rejectResult, writer, onTaskFinished } = workerData;
3555
+ clearStartupTimeout(workerData);
3556
+ const { workerAlive, rejectResult, writer, onTaskFinished } = workerData;
3478
3557
  terminateWorker$1(workerData);
3558
+ if (!workerAlive) {
3559
+ workerData.worker = null;
3560
+ }
3479
3561
  if (rejectResult) {
3480
- rejectResult(event.error || new Error(event.message || ERROR_EVENT_TYPE));
3562
+ let error = event.error || new Error(event.message || ERROR_EVENT_TYPE);
3563
+ if (!workerAlive) {
3564
+ error = Object.assign(new Error(error.message || ERROR_EVENT_TYPE), { workerStartupFailed: true });
3565
+ }
3566
+ rejectResult(error);
3481
3567
  if (writer) {
3482
3568
  writer.releaseLock();
3483
3569
  }
3484
- onTaskFinished();
3570
+ if (workerAlive) {
3571
+ onTaskFinished();
3572
+ }
3485
3573
  }
3486
3574
  }
3487
3575
 
3488
- function sendMessage(message, { worker, writer, transferStreams }) {
3576
+ function sendMessage(message, { worker, writer, transferStreams, workerAlive }) {
3489
3577
  try {
3490
3578
  const { value, readable, writable } = message;
3491
3579
  const transferables = [];
@@ -3493,7 +3581,7 @@
3493
3581
  message.value = toExactUint8Array(value);
3494
3582
  transferables.push(message.value.buffer);
3495
3583
  }
3496
- if (transferStreams && transferStreamsSupported) {
3584
+ if (transferStreams && transferStreamsSupported && workerAlive) {
3497
3585
  if (readable) {
3498
3586
  transferables.push(readable);
3499
3587
  }
@@ -4049,6 +4137,7 @@
4049
4137
  const ERR_SPLIT_ZIP_FILE = "Split zip file";
4050
4138
  const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
4051
4139
  const ERR_AMBIGUOUS_ARCHIVE = "Ambiguous archive";
4140
+ const ERR_ENCRYPTED_CENTRAL_DIRECTORY = "Encrypted central directory is not supported";
4052
4141
  const CHARSET_UTF8 = "utf-8";
4053
4142
  const PROPERTY_NAME_UTF8_SUFFIX = "UTF8";
4054
4143
  const CHARSET_CP437 = "cp437";
@@ -4126,6 +4215,7 @@
4126
4215
  let prependedDataLength = 0;
4127
4216
  let startOffset;
4128
4217
  let zip64EndOfDirectory;
4218
+ let zip64EndOfDirectoryVersion2;
4129
4219
  const requiresZip64 = directoryDataOffset == MAX_32_BITS || directoryDataLength == MAX_32_BITS || filesLength == MAX_16_BITS || diskNumber == MAX_16_BITS;
4130
4220
  if (directoryDataOffset != MAX_32_BITS && diskNumber != MAX_16_BITS) {
4131
4221
  directoryDataOffset += getDiskOffset$1(reader, diskNumber);
@@ -4155,6 +4245,7 @@
4155
4245
  throw new Error(ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND);
4156
4246
  }
4157
4247
  zip64EndOfDirectory = true;
4248
+ zip64EndOfDirectoryVersion2 = getBigUint64(endOfDirectoryView, 4) > ZIP64_END_OF_CENTRAL_DIR_LENGTH - 12;
4158
4249
  if (lastDiskNumber == MAX_16_BITS) {
4159
4250
  lastDiskNumber = getUint32(endOfDirectoryView, 16);
4160
4251
  } else if (checkAmbiguity && lastDiskNumber != getUint32(endOfDirectoryView, 16)) {
@@ -4234,6 +4325,9 @@
4234
4325
  for (let indexFile = 0; indexFile < filesLength; indexFile++) {
4235
4326
  const fileEntry = new ZipEntry$1(reader, config, zipReader.options);
4236
4327
  if (offset + CENTRAL_FILE_HEADER_LENGTH > directoryArray.length || getUint32(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE) {
4328
+ if (indexFile == 0 && (zip64EndOfDirectoryVersion2 || detectEncryptedCentralDirectory(directoryView))) {
4329
+ throw new Error(ERR_ENCRYPTED_CENTRAL_DIRECTORY);
4330
+ }
4237
4331
  throw new Error(ERR_CENTRAL_DIRECTORY_NOT_FOUND);
4238
4332
  }
4239
4333
  readCommonHeader(fileEntry, directoryView, offset + 6);
@@ -4461,6 +4555,9 @@
4461
4555
  fileEntry.zipCrypto = zipCrypto;
4462
4556
  }
4463
4557
  if (encrypted) {
4558
+ if ((localDirectory.rawBitFlag & BITFLAG_STRONG_ENCRYPTION) == BITFLAG_STRONG_ENCRYPTION) {
4559
+ throw new Error(ERR_UNSUPPORTED_ENCRYPTION);
4560
+ }
4464
4561
  if (!zipCrypto && (extraFieldAES.strength < 1 || extraFieldAES.strength > 3)) {
4465
4562
  throw new Error(ERR_UNSUPPORTED_ENCRYPTION);
4466
4563
  } else if (!password && !rawPassword) {
@@ -4554,6 +4651,16 @@
4554
4651
  }
4555
4652
  };
4556
4653
 
4654
+ function detectEncryptedCentralDirectory(directoryView) {
4655
+ const maxOffset = Math.min(directoryView.byteLength, 1024) - 3;
4656
+ for (let offset = 0; offset < maxOffset; offset++) {
4657
+ if (getUint32(directoryView, offset) == ARCHIVE_EXTRA_DATA_SIGNATURE) {
4658
+ return true;
4659
+ }
4660
+ }
4661
+ return false;
4662
+ }
4663
+
4557
4664
  function readCommonHeader(directory, dataView, offset) {
4558
4665
  const rawBitFlag = directory.rawBitFlag = getUint16(dataView, offset + 2);
4559
4666
  const encrypted = (rawBitFlag & BITFLAG_ENCRYPTED) == BITFLAG_ENCRYPTED;
@@ -5068,6 +5175,7 @@
5068
5175
  ERR_BAD_FORMAT: ERR_BAD_FORMAT,
5069
5176
  ERR_CENTRAL_DIRECTORY_NOT_FOUND: ERR_CENTRAL_DIRECTORY_NOT_FOUND,
5070
5177
  ERR_ENCRYPTED: ERR_ENCRYPTED,
5178
+ ERR_ENCRYPTED_CENTRAL_DIRECTORY: ERR_ENCRYPTED_CENTRAL_DIRECTORY,
5071
5179
  ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND: ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND,
5072
5180
  ERR_EOCDR_NOT_FOUND: ERR_EOCDR_NOT_FOUND,
5073
5181
  ERR_EXTRAFIELD_ZIP64_NOT_FOUND: ERR_EXTRAFIELD_ZIP64_NOT_FOUND,
@@ -5417,7 +5525,11 @@
5417
5525
  if (versionMadeBy > MAX_16_BITS) {
5418
5526
  throw new Error(ERR_INVALID_VERSION);
5419
5527
  }
5420
- let externalFileAttributes = getOptionValue(zipWriter, options, PROPERTY_NAME_EXTERNAL_FILE_ATTRIBUTES, 0);
5528
+ let externalFileAttributes = getOptionValue(zipWriter, options, PROPERTY_NAME_EXTERNAL_FILE_ATTRIBUTES);
5529
+ const externalFileAttributesProvided = externalFileAttributes !== UNDEFINED_VALUE;
5530
+ if (!externalFileAttributesProvided) {
5531
+ externalFileAttributes = 0;
5532
+ }
5421
5533
  if (!options[PROPERTY_NAME_DIRECTORY] && name.endsWith(DIRECTORY_SIGNATURE)) {
5422
5534
  options[PROPERTY_NAME_DIRECTORY] = true;
5423
5535
  }
@@ -5426,13 +5538,13 @@
5426
5538
  if (!name.endsWith(DIRECTORY_SIGNATURE)) {
5427
5539
  name += DIRECTORY_SIGNATURE;
5428
5540
  }
5429
- if (externalFileAttributes === 0) {
5541
+ if (!externalFileAttributesProvided) {
5430
5542
  externalFileAttributes = FILE_ATTR_MSDOS_DIR_MASK;
5431
5543
  if (!msDosCompatible) {
5432
5544
  externalFileAttributes |= (FILE_ATTR_UNIX_TYPE_DIR | FILE_ATTR_UNIX_EXECUTABLE_MASK | FILE_ATTR_UNIX_DEFAULT_MASK) << 16;
5433
5545
  }
5434
5546
  }
5435
- } else if (!msDosCompatible && externalFileAttributes === 0) {
5547
+ } else if (!msDosCompatible && !externalFileAttributesProvided) {
5436
5548
  if (executable) {
5437
5549
  externalFileAttributes = (FILE_ATTR_UNIX_EXECUTABLE_MASK | FILE_ATTR_UNIX_DEFAULT_MASK) << 16;
5438
5550
  } else {
@@ -5441,6 +5553,7 @@
5441
5553
  }
5442
5554
  let unixExternalUpper;
5443
5555
  if (!msDosCompatible) {
5556
+ const unixModeProvided = unixMode !== UNDEFINED_VALUE || Boolean(setuid || setgid || sticky);
5444
5557
  unixExternalUpper = (externalFileAttributes >> 16) & MAX_16_BITS;
5445
5558
  unixMode = unixMode === UNDEFINED_VALUE ? unixExternalUpper : (unixMode & MAX_16_BITS);
5446
5559
  if (setuid) {
@@ -5458,10 +5571,12 @@
5458
5571
  } else {
5459
5572
  sticky = Boolean(unixMode & FILE_ATTR_UNIX_STICKY_MASK);
5460
5573
  }
5461
- if (directory) {
5462
- unixMode |= FILE_ATTR_UNIX_TYPE_DIR;
5574
+ if (!externalFileAttributesProvided || unixModeProvided) {
5575
+ if (directory) {
5576
+ unixMode |= FILE_ATTR_UNIX_TYPE_DIR;
5577
+ }
5578
+ externalFileAttributes = ((unixMode & MAX_16_BITS) << 16) | (externalFileAttributes & MAX_16_BITS);
5463
5579
  }
5464
- externalFileAttributes = ((unixMode & MAX_16_BITS) << 16) | (externalFileAttributes & MAX_8_BITS);
5465
5580
  }
5466
5581
  ({ msdosAttributesRaw, msdosAttributes } = normalizeMsdosAttributes(msdosAttributesRaw, msdosAttributes));
5467
5582
  if (hasMsDosProvided) {
@@ -6184,6 +6299,7 @@
6184
6299
  if (extraFieldLength > MAX_16_BITS) {
6185
6300
  throw new Error(ERR_INVALID_EXTRAFIELD_DATA);
6186
6301
  }
6302
+ const dosLastModDate = new Date(Math.ceil(lastModDate.getTime() / 2000) * 2000);
6187
6303
  const {
6188
6304
  headerArray,
6189
6305
  headerView,
@@ -6193,7 +6309,7 @@
6193
6309
  bitFlag: getBitFlag(level, useUnicodeFileNames, dataDescriptor, encrypted, compressionMethod),
6194
6310
  compressionMethod,
6195
6311
  uncompressedSize,
6196
- lastModDate: lastModDate < MIN_DATE ? MIN_DATE : lastModDate > MAX_DATE ? MAX_DATE : lastModDate,
6312
+ lastModDate: dosLastModDate < MIN_DATE ? MIN_DATE : dosLastModDate > MAX_DATE ? MAX_DATE : dosLastModDate,
6197
6313
  rawFilename,
6198
6314
  zip64CompressedSize,
6199
6315
  zip64UncompressedSize,