@zip.js/zip.js 2.7.70 → 2.7.72

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,10 @@ const ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH = 20;
45
45
  const ZIP64_END_OF_CENTRAL_DIR_LENGTH = 56;
46
46
  const ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH = END_OF_CENTRAL_DIR_LENGTH + ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH + ZIP64_END_OF_CENTRAL_DIR_LENGTH;
47
47
 
48
+ const DATA_DESCRIPTOR_RECORD_LENGTH = 12;
49
+ const DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH = 20;
50
+ const DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH = 4;
51
+
48
52
  const EXTRAFIELD_TYPE_ZIP64 = 0x0001;
49
53
  const EXTRAFIELD_TYPE_AES = 0x9901;
50
54
  const EXTRAFIELD_TYPE_NTFS = 0x000a;
@@ -73,7 +77,7 @@ const VERSION_AES = 0x33;
73
77
 
74
78
  const DIRECTORY_SIGNATURE = "/";
75
79
 
76
- const HEADER_SIZE = 26;
80
+ const HEADER_SIZE = 30;
77
81
  const HEADER_OFFSET_SIGNATURE = 10;
78
82
  const HEADER_OFFSET_COMPRESSED_SIZE = 14;
79
83
  const HEADER_OFFSET_UNCOMPRESSED_SIZE = 18;
@@ -99,6 +103,9 @@ export {
99
103
  END_OF_CENTRAL_DIR_SIGNATURE,
100
104
  ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIGNATURE,
101
105
  ZIP64_END_OF_CENTRAL_DIR_SIGNATURE,
106
+ DATA_DESCRIPTOR_RECORD_LENGTH,
107
+ DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH,
108
+ DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH,
102
109
  EXTRAFIELD_TYPE_ZIP64,
103
110
  EXTRAFIELD_TYPE_AES,
104
111
  EXTRAFIELD_TYPE_NTFS,
@@ -0,0 +1,89 @@
1
+ /*
2
+ Copyright (c) 2022 Gildas Lormeau. All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice,
8
+ this list of conditions and the following disclaimer.
9
+
10
+ 2. Redistributions in binary form must reproduce the above copyright
11
+ notice, this list of conditions and the following disclaimer in
12
+ the documentation and/or other materials provided with the distribution.
13
+
14
+ 3. The names of the authors may not be used to endorse or promote products
15
+ derived from this software without specific prior written permission.
16
+
17
+ THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
18
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
19
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
20
+ INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
21
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
23
+ OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
26
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+ */
28
+
29
+ const OPTION_FILENAME_ENCODING = "filenameEncoding";
30
+ const OPTION_COMMENT_ENCODING = "commentEncoding";
31
+ const OPTION_DECODE_TEXT = "decodeText";
32
+ const OPTION_EXTRACT_PREPENDED_DATA = "extractPrependedData";
33
+ const OPTION_EXTRACT_APPENDED_DATA = "extractAppendedData";
34
+ const OPTION_PASSWORD = "password";
35
+ const OPTION_RAW_PASSWORD = "rawPassword";
36
+ const OPTION_PASS_THROUGH = "passThrough";
37
+ const OPTION_SIGNAL = "signal";
38
+ const OPTION_CHECK_PASSWORD_ONLY = "checkPasswordOnly";
39
+ const OPTION_CHECK_OVERLAPPING_ENTRY_ONLY = "checkOverlappingEntryOnly";
40
+ const OPTION_CHECK_OVERLAPPING_ENTRY = "checkOverlappingEntry";
41
+ const OPTION_CHECK_SIGNATURE = "checkSignature";
42
+ const OPTION_USE_WEB_WORKERS = "useWebWorkers";
43
+ const OPTION_USE_COMPRESSION_STREAM = "useCompressionStream";
44
+ const OPTION_TRANSFER_STREAMS = "transferStreams";
45
+ const OPTION_PREVENT_CLOSE = "preventClose";
46
+ const OPTION_ENCRYPTION_STRENGTH = "encryptionStrength";
47
+ const OPTION_EXTENDED_TIMESTAMP = "extendedTimestamp";
48
+ const OPTION_KEEP_ORDER = "keepOrder";
49
+ const OPTION_LEVEL = "level";
50
+ const OPTION_BUFFERED_WRITE = "bufferedWrite";
51
+ const OPTION_DATA_DESCRIPTOR_SIGNATURE = "dataDescriptorSignature";
52
+ const OPTION_USE_UNICODE_FILE_NAMES = "useUnicodeFileNames";
53
+ const OPTION_DATA_DESCRIPTOR = "dataDescriptor";
54
+ const OPTION_SUPPORT_ZIP64_SPLIT_FILE = "supportZip64SplitFile";
55
+ const OPTION_ENCODE_TEXT = "encodeText";
56
+ const OPTION_OFFSET = "offset";
57
+ const OPTION_USDZ = "usdz";
58
+
59
+ export {
60
+ OPTION_FILENAME_ENCODING,
61
+ OPTION_COMMENT_ENCODING,
62
+ OPTION_DECODE_TEXT,
63
+ OPTION_EXTRACT_PREPENDED_DATA,
64
+ OPTION_EXTRACT_APPENDED_DATA,
65
+ OPTION_PASSWORD,
66
+ OPTION_RAW_PASSWORD,
67
+ OPTION_PASS_THROUGH,
68
+ OPTION_SIGNAL,
69
+ OPTION_CHECK_PASSWORD_ONLY,
70
+ OPTION_CHECK_OVERLAPPING_ENTRY_ONLY,
71
+ OPTION_CHECK_OVERLAPPING_ENTRY,
72
+ OPTION_CHECK_SIGNATURE,
73
+ OPTION_USE_WEB_WORKERS,
74
+ OPTION_USE_COMPRESSION_STREAM,
75
+ OPTION_TRANSFER_STREAMS,
76
+ OPTION_PREVENT_CLOSE,
77
+ OPTION_ENCRYPTION_STRENGTH,
78
+ OPTION_EXTENDED_TIMESTAMP,
79
+ OPTION_KEEP_ORDER,
80
+ OPTION_LEVEL,
81
+ OPTION_BUFFERED_WRITE,
82
+ OPTION_DATA_DESCRIPTOR_SIGNATURE,
83
+ OPTION_USE_UNICODE_FILE_NAMES,
84
+ OPTION_DATA_DESCRIPTOR,
85
+ OPTION_SUPPORT_ZIP64_SPLIT_FILE,
86
+ OPTION_ENCODE_TEXT,
87
+ OPTION_OFFSET,
88
+ OPTION_USDZ
89
+ };
@@ -131,7 +131,7 @@ function createKeys(target, password) {
131
131
  Object.assign(target, {
132
132
  keys,
133
133
  crcKey0: new Crc32(keys[0]),
134
- crcKey2: new Crc32(keys[2]),
134
+ crcKey2: new Crc32(keys[2])
135
135
  });
136
136
  for (let index = 0; index < password.length; index++) {
137
137
  updateKeys(target, password.charCodeAt(index));
@@ -54,7 +54,7 @@ class DeflateStream extends TransformStream {
54
54
  const { compressed, encrypted, useCompressionStream, zipCrypto, signed, level } = options;
55
55
  const stream = this;
56
56
  let crc32Stream, encryptionStream;
57
- let readable = filterEmptyChunks(super.readable);
57
+ let readable = super.readable;
58
58
  if ((!encrypted || zipCrypto) && signed) {
59
59
  crc32Stream = new Crc32Stream();
60
60
  readable = pipeThrough(readable, crc32Stream);
@@ -89,7 +89,7 @@ class InflateStream extends TransformStream {
89
89
  super({});
90
90
  const { zipCrypto, encrypted, signed, signature, compressed, useCompressionStream } = options;
91
91
  let crc32Stream, decryptionStream;
92
- let readable = filterEmptyChunks(super.readable);
92
+ let readable = super.readable;
93
93
  if (encrypted) {
94
94
  if (zipCrypto) {
95
95
  readable = pipeThrough(readable, new ZipCryptoDecryptionStream(options));
@@ -125,16 +125,6 @@ export {
125
125
  ERR_ABORT_CHECK_PASSWORD
126
126
  };
127
127
 
128
- function filterEmptyChunks(readable) {
129
- return pipeThrough(readable, new TransformStream({
130
- transform(chunk, controller) {
131
- if (chunk && chunk.length) {
132
- controller.enqueue(chunk);
133
- }
134
- }
135
- }));
136
- }
137
-
138
128
  function setReadable(stream, readable, flush) {
139
129
  readable = pipeThrough(readable, new TransformStream({ flush }));
140
130
  Object.defineProperty(stream, "readable", {
@@ -149,16 +139,11 @@ function pipeThroughCommpressionStream(readable, useCompressionStream, options,
149
139
  const CompressionStream = useCompressionStream && CodecStreamNative ? CodecStreamNative : CodecStream;
150
140
  readable = pipeThrough(readable, new CompressionStream(COMPRESSION_FORMAT, options));
151
141
  // eslint-disable-next-line no-unused-vars
152
- } catch (_) {
142
+ } catch (error) {
153
143
  if (useCompressionStream) {
154
- try {
155
- readable = pipeThrough(readable, new CodecStream(COMPRESSION_FORMAT, options));
156
- // eslint-disable-next-line no-unused-vars
157
- } catch (_) {
158
- return readable;
159
- }
144
+ readable = pipeThrough(readable, new CodecStream(COMPRESSION_FORMAT, options));
160
145
  } else {
161
- return readable;
146
+ throw error;
162
147
  }
163
148
  }
164
149
  return readable;
@@ -30,8 +30,8 @@ const PROPERTY_NAME_FILENAME = "filename";
30
30
  const PROPERTY_NAME_RAW_FILENAME = "rawFilename";
31
31
  const PROPERTY_NAME_COMMENT = "comment";
32
32
  const PROPERTY_NAME_RAW_COMMENT = "rawComment";
33
- const PROPERTY_NAME_UNCOMPPRESSED_SIZE = "uncompressedSize";
34
- const PROPERTY_NAME_COMPPRESSED_SIZE = "compressedSize";
33
+ const PROPERTY_NAME_UNCOMPRESSED_SIZE = "uncompressedSize";
34
+ const PROPERTY_NAME_COMPRESSED_SIZE = "compressedSize";
35
35
  const PROPERTY_NAME_OFFSET = "offset";
36
36
  const PROPERTY_NAME_DISK_NUMBER_START = "diskNumberStart";
37
37
  const PROPERTY_NAME_LAST_MODIFICATION_DATE = "lastModDate";
@@ -52,12 +52,20 @@ const PROPERTY_NAME_VERSION_MADE_BY = "versionMadeBy";
52
52
  const PROPERTY_NAME_ZIPCRYPTO = "zipCrypto";
53
53
  const PROPERTY_NAME_DIRECTORY = "directory";
54
54
  const PROPERTY_NAME_EXECUTABLE = "executable";
55
+ const PROPERTY_NAME_COMPRESSION_METHOD = "compressionMethod";
56
+ const PROPERTY_NAME_SIGNATURE = "signature";
57
+ const PROPERTY_NAME_EXTRA_FIELD = "extraField";
55
58
 
56
59
  const PROPERTY_NAMES = [
57
- PROPERTY_NAME_FILENAME, PROPERTY_NAME_RAW_FILENAME, PROPERTY_NAME_COMPPRESSED_SIZE, PROPERTY_NAME_UNCOMPPRESSED_SIZE,
60
+ PROPERTY_NAME_FILENAME, PROPERTY_NAME_RAW_FILENAME, PROPERTY_NAME_COMPRESSED_SIZE, PROPERTY_NAME_UNCOMPRESSED_SIZE,
58
61
  PROPERTY_NAME_LAST_MODIFICATION_DATE, PROPERTY_NAME_RAW_LAST_MODIFICATION_DATE, PROPERTY_NAME_COMMENT, PROPERTY_NAME_RAW_COMMENT,
59
62
  PROPERTY_NAME_LAST_ACCESS_DATE, PROPERTY_NAME_CREATION_DATE, PROPERTY_NAME_OFFSET, PROPERTY_NAME_DISK_NUMBER_START,
60
- PROPERTY_NAME_DISK_NUMBER_START, PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTE, PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTES, PROPERTY_NAME_EXTERNAL_FILE_ATTRIBUTE, PROPERTY_NAME_EXTERNAL_FILE_ATTRIBUTES, PROPERTY_NAME_MS_DOS_COMPATIBLE, PROPERTY_NAME_ZIP64, PROPERTY_NAME_ENCRYPTED, PROPERTY_NAME_VERSION, PROPERTY_NAME_VERSION_MADE_BY, PROPERTY_NAME_ZIPCRYPTO, PROPERTY_NAME_DIRECTORY, PROPERTY_NAME_EXECUTABLE, "bitFlag", "signature", "filenameUTF8", "commentUTF8", "compressionMethod", "extraField", "rawExtraField", "extraFieldZip64", "extraFieldUnicodePath", "extraFieldUnicodeComment", "extraFieldAES", "extraFieldNTFS", "extraFieldExtendedTimestamp"];
63
+ PROPERTY_NAME_DISK_NUMBER_START, PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTE, PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTES,
64
+ PROPERTY_NAME_EXTERNAL_FILE_ATTRIBUTE, PROPERTY_NAME_EXTERNAL_FILE_ATTRIBUTES, PROPERTY_NAME_MS_DOS_COMPATIBLE, PROPERTY_NAME_ZIP64,
65
+ PROPERTY_NAME_ENCRYPTED, PROPERTY_NAME_VERSION, PROPERTY_NAME_VERSION_MADE_BY, PROPERTY_NAME_ZIPCRYPTO, PROPERTY_NAME_DIRECTORY,
66
+ PROPERTY_NAME_EXECUTABLE, PROPERTY_NAME_COMPRESSION_METHOD, PROPERTY_NAME_SIGNATURE, PROPERTY_NAME_EXTRA_FIELD,
67
+ "bitFlag", "filenameUTF8", "commentUTF8", "rawExtraField", "extraFieldZip64", "extraFieldUnicodePath", "extraFieldUnicodeComment",
68
+ "extraFieldAES", "extraFieldNTFS", "extraFieldExtendedTimestamp"];
61
69
 
62
70
  class Entry {
63
71
 
@@ -72,8 +80,8 @@ export {
72
80
  PROPERTY_NAME_RAW_FILENAME,
73
81
  PROPERTY_NAME_COMMENT,
74
82
  PROPERTY_NAME_RAW_COMMENT,
75
- PROPERTY_NAME_UNCOMPPRESSED_SIZE,
76
- PROPERTY_NAME_COMPPRESSED_SIZE,
83
+ PROPERTY_NAME_UNCOMPRESSED_SIZE,
84
+ PROPERTY_NAME_COMPRESSED_SIZE,
77
85
  PROPERTY_NAME_OFFSET,
78
86
  PROPERTY_NAME_DISK_NUMBER_START,
79
87
  PROPERTY_NAME_LAST_MODIFICATION_DATE,
@@ -94,5 +102,8 @@ export {
94
102
  PROPERTY_NAME_ZIPCRYPTO,
95
103
  PROPERTY_NAME_DIRECTORY,
96
104
  PROPERTY_NAME_EXECUTABLE,
105
+ PROPERTY_NAME_COMPRESSION_METHOD,
106
+ PROPERTY_NAME_SIGNATURE,
107
+ PROPERTY_NAME_EXTRA_FIELD,
97
108
  Entry
98
109
  };
@@ -874,7 +874,7 @@ async function transformToFileSystemhandles(entry) {
874
874
  next() {
875
875
  const result = {
876
876
  value: entries[entryIndex],
877
- done: entryIndex === entries.length
877
+ done: entryIndex == entries.length
878
878
  };
879
879
  entryIndex++;
880
880
  return result;
@@ -36,6 +36,10 @@ import {
36
36
  COMPRESSION_METHOD_STORE,
37
37
  COMPRESSION_METHOD_AES,
38
38
  SPLIT_ZIP_FILE_SIGNATURE,
39
+ DATA_DESCRIPTOR_RECORD_SIGNATURE,
40
+ DATA_DESCRIPTOR_RECORD_LENGTH,
41
+ DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH,
42
+ DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH,
39
43
  LOCAL_FILE_HEADER_SIGNATURE,
40
44
  CENTRAL_FILE_HEADER_SIGNATURE,
41
45
  END_OF_CENTRAL_DIR_SIGNATURE,
@@ -93,8 +97,8 @@ import {
93
97
  PROPERTY_NAME_FILENAME,
94
98
  PROPERTY_NAME_RAW_COMMENT,
95
99
  PROPERTY_NAME_COMMENT,
96
- PROPERTY_NAME_UNCOMPPRESSED_SIZE,
97
- PROPERTY_NAME_COMPPRESSED_SIZE,
100
+ PROPERTY_NAME_UNCOMPRESSED_SIZE,
101
+ PROPERTY_NAME_COMPRESSED_SIZE,
98
102
  PROPERTY_NAME_OFFSET,
99
103
  PROPERTY_NAME_DISK_NUMBER_START,
100
104
  PROPERTY_NAME_LAST_MODIFICATION_DATE,
@@ -105,6 +109,25 @@ import {
105
109
  PROPERTY_NAME_RAW_CREATION_DATE,
106
110
  Entry
107
111
  } from "./zip-entry.js";
112
+ import {
113
+ OPTION_FILENAME_ENCODING,
114
+ OPTION_COMMENT_ENCODING,
115
+ OPTION_DECODE_TEXT,
116
+ OPTION_EXTRACT_PREPENDED_DATA,
117
+ OPTION_EXTRACT_APPENDED_DATA,
118
+ OPTION_PASSWORD,
119
+ OPTION_RAW_PASSWORD,
120
+ OPTION_PASS_THROUGH,
121
+ OPTION_SIGNAL,
122
+ OPTION_CHECK_PASSWORD_ONLY,
123
+ OPTION_CHECK_OVERLAPPING_ENTRY_ONLY,
124
+ OPTION_CHECK_OVERLAPPING_ENTRY,
125
+ OPTION_CHECK_SIGNATURE,
126
+ OPTION_USE_WEB_WORKERS,
127
+ OPTION_USE_COMPRESSION_STREAM,
128
+ OPTION_TRANSFER_STREAMS,
129
+ OPTION_PREVENT_CLOSE
130
+ } from "./options.js";
108
131
 
109
132
  const ERR_BAD_FORMAT = "File format is not recognized";
110
133
  const ERR_EOCDR_NOT_FOUND = "End of central directory not found";
@@ -116,11 +139,12 @@ const ERR_ENCRYPTED = "File contains encrypted entry";
116
139
  const ERR_UNSUPPORTED_ENCRYPTION = "Encryption method not supported";
117
140
  const ERR_UNSUPPORTED_COMPRESSION = "Compression method not supported";
118
141
  const ERR_SPLIT_ZIP_FILE = "Split zip file";
142
+ const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
119
143
  const CHARSET_UTF8 = "utf-8";
120
144
  const CHARSET_CP437 = "cp437";
121
145
  const ZIP64_PROPERTIES = [
122
- [PROPERTY_NAME_UNCOMPPRESSED_SIZE, MAX_32_BITS],
123
- [PROPERTY_NAME_COMPPRESSED_SIZE, MAX_32_BITS],
146
+ [PROPERTY_NAME_UNCOMPRESSED_SIZE, MAX_32_BITS],
147
+ [PROPERTY_NAME_COMPRESSED_SIZE, MAX_32_BITS],
124
148
  [PROPERTY_NAME_OFFSET, MAX_32_BITS],
125
149
  [PROPERTY_NAME_DISK_NUMBER_START, MAX_16_BITS]
126
150
  ];
@@ -141,7 +165,8 @@ class ZipReader {
141
165
  Object.assign(this, {
142
166
  reader: new GenericReader(reader),
143
167
  options,
144
- config: getConfiguration()
168
+ config: getConfiguration(),
169
+ readRanges: []
145
170
  });
146
171
  }
147
172
 
@@ -191,7 +216,9 @@ class ZipReader {
191
216
  if (getUint32(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE && directoryDataOffset != expectedDirectoryDataOffset) {
192
217
  const originalDirectoryDataOffset = directoryDataOffset;
193
218
  directoryDataOffset = expectedDirectoryDataOffset;
194
- prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
219
+ if (directoryDataOffset > originalDirectoryDataOffset) {
220
+ prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
221
+ }
195
222
  endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH, -1);
196
223
  endOfDirectoryView = getDataView(endOfDirectoryArray);
197
224
  }
@@ -231,7 +258,9 @@ class ZipReader {
231
258
  if (getUint32(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE && directoryDataOffset != expectedDirectoryDataOffset) {
232
259
  const originalDirectoryDataOffset = directoryDataOffset;
233
260
  directoryDataOffset = expectedDirectoryDataOffset;
234
- prependedDataLength += directoryDataOffset - originalDirectoryDataOffset;
261
+ if (directoryDataOffset > originalDirectoryDataOffset) {
262
+ prependedDataLength += directoryDataOffset - originalDirectoryDataOffset;
263
+ }
235
264
  directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength, diskNumber);
236
265
  directoryView = getDataView(directoryArray);
237
266
  }
@@ -245,8 +274,8 @@ class ZipReader {
245
274
  if (directoryDataOffset < 0 || directoryDataOffset >= reader.size) {
246
275
  throw new Error(ERR_BAD_FORMAT);
247
276
  }
248
- const filenameEncoding = getOptionValue(zipReader, options, "filenameEncoding");
249
- const commentEncoding = getOptionValue(zipReader, options, "commentEncoding");
277
+ const filenameEncoding = getOptionValue(zipReader, options, OPTION_FILENAME_ENCODING);
278
+ const commentEncoding = getOptionValue(zipReader, options, OPTION_COMMENT_ENCODING);
250
279
  for (let indexFile = 0; indexFile < filesLength; indexFile++) {
251
280
  const fileEntry = new ZipEntry(reader, config, zipReader.options);
252
281
  if (getUint32(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE) {
@@ -292,7 +321,7 @@ class ZipReader {
292
321
  });
293
322
  fileEntry.internalFileAttribute = fileEntry.internalFileAttributes;
294
323
  fileEntry.externalFileAttribute = fileEntry.externalFileAttributes;
295
- const decode = getOptionValue(zipReader, options, "decodeText") || decodeText;
324
+ const decode = getOptionValue(zipReader, options, OPTION_DECODE_TEXT) || decodeText;
296
325
  const rawFilenameEncoding = filenameUTF8 ? CHARSET_UTF8 : filenameEncoding || CHARSET_CP437;
297
326
  const rawCommentEncoding = commentUTF8 ? CHARSET_UTF8 : commentEncoding || CHARSET_CP437;
298
327
  let filename = decode(rawFilename, rawFilenameEncoding);
@@ -313,12 +342,12 @@ class ZipReader {
313
342
  readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6);
314
343
  fileEntry.zipCrypto = fileEntry.encrypted && !fileEntry.extraFieldAES;
315
344
  const entry = new Entry(fileEntry);
316
- entry.getData = (writer, options) => fileEntry.getData(writer, entry, options);
345
+ entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader.readRanges, options);
317
346
  entry.arrayBuffer = async options => {
318
347
  const writer = new TransformStream();
319
348
  const [arrayBuffer] = await Promise.all([
320
349
  new Response(writer.readable).arrayBuffer(),
321
- fileEntry.getData(writer, entry, options)]);
350
+ fileEntry.getData(writer, entry, zipReader.readRanges, options)]);
322
351
  return arrayBuffer;
323
352
  };
324
353
  offset = endOffset;
@@ -333,8 +362,8 @@ class ZipReader {
333
362
  }
334
363
  yield entry;
335
364
  }
336
- const extractPrependedData = getOptionValue(zipReader, options, "extractPrependedData");
337
- const extractAppendedData = getOptionValue(zipReader, options, "extractAppendedData");
365
+ const extractPrependedData = getOptionValue(zipReader, options, OPTION_EXTRACT_PREPENDED_DATA);
366
+ const extractAppendedData = getOptionValue(zipReader, options, OPTION_EXTRACT_APPENDED_DATA);
338
367
  if (extractPrependedData) {
339
368
  zipReader.prependedData = startOffset > 0 ? await readUint8Array(reader, 0, startOffset) : new Uint8Array();
340
369
  }
@@ -400,7 +429,8 @@ export {
400
429
  ERR_INVALID_SIGNATURE,
401
430
  ERR_INVALID_PASSWORD,
402
431
  ERR_INVALID_UNCOMPRESSED_SIZE,
403
- ERR_SPLIT_ZIP_FILE
432
+ ERR_SPLIT_ZIP_FILE,
433
+ ERR_OVERLAPPING_ENTRY
404
434
  };
405
435
 
406
436
  class ZipEntry {
@@ -413,13 +443,14 @@ class ZipEntry {
413
443
  });
414
444
  }
415
445
 
416
- async getData(writer, fileEntry, options = {}) {
446
+ async getData(writer, fileEntry, readRanges, options = {}) {
417
447
  const zipEntry = this;
418
448
  const {
419
449
  reader,
420
450
  offset,
421
451
  diskNumberStart,
422
452
  extraFieldAES,
453
+ extraFieldZip64,
423
454
  compressionMethod,
424
455
  config,
425
456
  bitFlag,
@@ -428,13 +459,15 @@ class ZipEntry {
428
459
  uncompressedSize,
429
460
  compressedSize
430
461
  } = zipEntry;
462
+ const {
463
+ dataDescriptor
464
+ } = bitFlag;
431
465
  const localDirectory = fileEntry.localDirectory = {};
432
- const dataArraySize = HEADER_SIZE + 4;
433
- const dataArray = await readUint8Array(reader, offset, dataArraySize + 4, diskNumberStart);
466
+ const dataArray = await readUint8Array(reader, offset, HEADER_SIZE, diskNumberStart);
434
467
  const dataView = getDataView(dataArray);
435
- let password = getOptionValue(zipEntry, options, "password");
436
- let rawPassword = getOptionValue(zipEntry, options, "rawPassword");
437
- const passThrough = getOptionValue(zipEntry, options, "passThrough");
468
+ let password = getOptionValue(zipEntry, options, OPTION_PASSWORD);
469
+ let rawPassword = getOptionValue(zipEntry, options, OPTION_RAW_PASSWORD);
470
+ const passThrough = getOptionValue(zipEntry, options, OPTION_PASS_THROUGH);
438
471
  password = password && password.length && password;
439
472
  rawPassword = rawPassword && rawPassword.length && rawPassword;
440
473
  if (extraFieldAES) {
@@ -449,14 +482,17 @@ class ZipEntry {
449
482
  throw new Error(ERR_LOCAL_FILE_HEADER_NOT_FOUND);
450
483
  }
451
484
  readCommonHeader(localDirectory, dataView, 4);
452
- localDirectory.rawExtraField = localDirectory.extraFieldLength ?
453
- await readUint8Array(reader, offset + dataArraySize + localDirectory.filenameLength, localDirectory.extraFieldLength, diskNumberStart) :
485
+ const {
486
+ extraFieldLength,
487
+ filenameLength,
488
+ lastAccessDate,
489
+ creationDate
490
+ } = localDirectory;
491
+ localDirectory.rawExtraField = extraFieldLength ?
492
+ await readUint8Array(reader, offset + HEADER_SIZE + filenameLength, extraFieldLength, diskNumberStart) :
454
493
  new Uint8Array();
455
494
  readCommonFooter(zipEntry, localDirectory, dataView, 4, true);
456
- Object.assign(fileEntry, {
457
- lastAccessDate: localDirectory.lastAccessDate,
458
- creationDate: localDirectory.creationDate
459
- });
495
+ Object.assign(fileEntry, { lastAccessDate, creationDate });
460
496
  const encrypted = zipEntry.encrypted && localDirectory.encrypted && !passThrough;
461
497
  const zipCrypto = encrypted && !extraFieldAES;
462
498
  if (!passThrough) {
@@ -469,7 +505,7 @@ class ZipEntry {
469
505
  throw new Error(ERR_ENCRYPTED);
470
506
  }
471
507
  }
472
- const dataOffset = offset + dataArraySize + localDirectory.filenameLength + localDirectory.extraFieldLength;
508
+ const dataOffset = offset + HEADER_SIZE + filenameLength + extraFieldLength;
473
509
  const size = compressedSize;
474
510
  const readable = reader.readable;
475
511
  Object.assign(readable, {
@@ -477,14 +513,13 @@ class ZipEntry {
477
513
  offset: dataOffset,
478
514
  size
479
515
  });
480
- const signal = getOptionValue(zipEntry, options, "signal");
481
- const checkPasswordOnly = getOptionValue(zipEntry, options, "checkPasswordOnly");
482
- if (checkPasswordOnly) {
483
- writer = new WritableStream();
484
- }
485
- writer = new GenericWriter(writer);
486
- await initStream(writer, passThrough ? compressedSize : uncompressedSize);
487
- const { writable } = writer;
516
+ const signal = getOptionValue(zipEntry, options, OPTION_SIGNAL);
517
+ const checkPasswordOnly = getOptionValue(zipEntry, options, OPTION_CHECK_PASSWORD_ONLY);
518
+ let checkOverlappingEntry = getOptionValue(zipEntry, options, OPTION_CHECK_OVERLAPPING_ENTRY);
519
+ const checkOverlappingEntryOnly = getOptionValue(zipEntry, options, OPTION_CHECK_OVERLAPPING_ENTRY_ONLY);
520
+ if (checkOverlappingEntryOnly) {
521
+ checkOverlappingEntry = true;
522
+ }
488
523
  const { onstart, onprogress, onend } = options;
489
524
  const workerOptions = {
490
525
  options: {
@@ -493,23 +528,50 @@ class ZipEntry {
493
528
  rawPassword,
494
529
  zipCrypto,
495
530
  encryptionStrength: extraFieldAES && extraFieldAES.strength,
496
- signed: getOptionValue(zipEntry, options, "checkSignature") && !passThrough,
497
- passwordVerification: zipCrypto && (bitFlag.dataDescriptor ? ((rawLastModDate >>> 8) & 0xFF) : ((signature >>> 24) & 0xFF)),
531
+ signed: getOptionValue(zipEntry, options, OPTION_CHECK_SIGNATURE) && !passThrough,
532
+ passwordVerification: zipCrypto && (dataDescriptor ? ((rawLastModDate >>> 8) & 0xFF) : ((signature >>> 24) & 0xFF)),
498
533
  outputSize: uncompressedSize,
499
534
  signature,
500
535
  compressed: compressionMethod != 0 && !passThrough,
501
536
  encrypted: zipEntry.encrypted && !passThrough,
502
- useWebWorkers: getOptionValue(zipEntry, options, "useWebWorkers"),
503
- useCompressionStream: getOptionValue(zipEntry, options, "useCompressionStream"),
504
- transferStreams: getOptionValue(zipEntry, options, "transferStreams"),
537
+ useWebWorkers: getOptionValue(zipEntry, options, OPTION_USE_WEB_WORKERS),
538
+ useCompressionStream: getOptionValue(zipEntry, options, OPTION_USE_COMPRESSION_STREAM),
539
+ transferStreams: getOptionValue(zipEntry, options, OPTION_TRANSFER_STREAMS),
505
540
  checkPasswordOnly
506
541
  },
507
542
  config,
508
543
  streamOptions: { signal, size, onstart, onprogress, onend }
509
544
  };
545
+ if (checkOverlappingEntry) {
546
+ await detectOverlappingEntry({
547
+ reader,
548
+ fileEntry,
549
+ offset,
550
+ diskNumberStart,
551
+ signature,
552
+ compressedSize,
553
+ uncompressedSize,
554
+ dataOffset,
555
+ dataDescriptor: dataDescriptor || localDirectory.bitFlag.dataDescriptor,
556
+ extraFieldZip64: extraFieldZip64 || localDirectory.extraFieldZip64,
557
+ readRanges
558
+ });
559
+ }
560
+ let writable;
510
561
  try {
511
- const { outputSize } = await runWorker({ readable, writable }, workerOptions);
512
- writer.size += outputSize;
562
+ if (!checkOverlappingEntryOnly) {
563
+ if (checkPasswordOnly) {
564
+ writer = new WritableStream();
565
+ }
566
+ writer = new GenericWriter(writer);
567
+ await initStream(writer, passThrough ? compressedSize : uncompressedSize);
568
+ ({ writable } = writer);
569
+ const { outputSize } = await runWorker({ readable, writable }, workerOptions);
570
+ writer.size += outputSize;
571
+ if (outputSize != (passThrough ? compressedSize : uncompressedSize)) {
572
+ throw new Error(ERR_INVALID_UNCOMPRESSED_SIZE);
573
+ }
574
+ }
513
575
  } catch (error) {
514
576
  if (error.outputSize !== UNDEFINED_VALUE) {
515
577
  writer.size += error.outputSize;
@@ -518,12 +580,12 @@ class ZipEntry {
518
580
  throw error;
519
581
  }
520
582
  } finally {
521
- const preventClose = getOptionValue(zipEntry, options, "preventClose");
522
- if (!preventClose && !writable.locked) {
583
+ const preventClose = getOptionValue(zipEntry, options, OPTION_PREVENT_CLOSE);
584
+ if (!preventClose && writable && !writable.locked) {
523
585
  await writable.getWriter().close();
524
586
  }
525
587
  }
526
- return checkPasswordOnly ? UNDEFINED_VALUE : writer.getData ? writer.getData() : writable;
588
+ return checkPasswordOnly || checkOverlappingEntryOnly ? UNDEFINED_VALUE : writer.getData ? writer.getData() : writable;
527
589
  }
528
590
  }
529
591
 
@@ -731,6 +793,71 @@ function readExtraFieldExtendedTimestamp(extraFieldExtendedTimestamp, directory,
731
793
  });
732
794
  }
733
795
 
796
+ async function detectOverlappingEntry({
797
+ reader,
798
+ fileEntry,
799
+ offset,
800
+ diskNumberStart,
801
+ signature,
802
+ compressedSize,
803
+ uncompressedSize,
804
+ dataOffset,
805
+ dataDescriptor,
806
+ extraFieldZip64,
807
+ readRanges
808
+ }) {
809
+ let diskOffset = 0;
810
+ if (diskNumberStart) {
811
+ for (let indexReader = 0; indexReader < diskNumberStart; indexReader++) {
812
+ const diskReader = reader.readers[indexReader];
813
+ diskOffset += diskReader.size;
814
+ }
815
+ }
816
+ let dataDescriptorLength = 0;
817
+ if (dataDescriptor) {
818
+ if (extraFieldZip64) {
819
+ dataDescriptorLength = DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH;
820
+ } else {
821
+ dataDescriptorLength = DATA_DESCRIPTOR_RECORD_LENGTH;
822
+ }
823
+ }
824
+ if (dataDescriptorLength) {
825
+ const dataDescriptorArray = await readUint8Array(reader, dataOffset + compressedSize, dataDescriptorLength + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH, diskNumberStart);
826
+ const dataDescriptorSignature = getUint32(getDataView(dataDescriptorArray), 0) == DATA_DESCRIPTOR_RECORD_SIGNATURE;
827
+ if (dataDescriptorSignature) {
828
+ const readSignature = getUint32(getDataView(dataDescriptorArray), 4);
829
+ let readCompressedSize;
830
+ let readUncompressedSize;
831
+ if (extraFieldZip64) {
832
+ readCompressedSize = getBigUint64(getDataView(dataDescriptorArray), 8);
833
+ readUncompressedSize = getBigUint64(getDataView(dataDescriptorArray), 16);
834
+ } else {
835
+ readCompressedSize = getUint32(getDataView(dataDescriptorArray), 8);
836
+ readUncompressedSize = getUint32(getDataView(dataDescriptorArray), 12);
837
+ }
838
+ const matchSignature = (fileEntry.encrypted && !fileEntry.zipCrypto) || readSignature == signature;
839
+ if (matchSignature &&
840
+ readCompressedSize == compressedSize &&
841
+ readUncompressedSize == uncompressedSize) {
842
+ dataDescriptorLength += DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH;
843
+ }
844
+ }
845
+ }
846
+ const range = {
847
+ start: diskOffset + offset,
848
+ end: diskOffset + dataOffset + compressedSize + dataDescriptorLength,
849
+ fileEntry
850
+ };
851
+ for (const otherRange of readRanges) {
852
+ if (otherRange.fileEntry != fileEntry && range.start >= otherRange.start && range.start < otherRange.end) {
853
+ const error = new Error(ERR_OVERLAPPING_ENTRY);
854
+ error.overlappingEntry = otherRange.fileEntry;
855
+ throw error;
856
+ }
857
+ }
858
+ readRanges.push(range);
859
+ }
860
+
734
861
  async function seekSignature(reader, signature, startOffset, minimumBytes, maximumLength) {
735
862
  const signatureArray = new Uint8Array(4);
736
863
  const signatureView = getDataView(signatureArray);