@zip.js/zip.js 2.8.48 → 2.8.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/deno.json +1 -1
  2. package/dist/zip-core-external.js +351 -205
  3. package/dist/zip-core-external.min.js +1 -1
  4. package/dist/zip-core.js +357 -205
  5. package/dist/zip-core.min.js +1 -1
  6. package/dist/zip-fs-core-external.js +438 -216
  7. package/dist/zip-fs-core-external.min.js +1 -1
  8. package/dist/zip-fs-core.js +213 -435
  9. package/dist/zip-fs-core.min.js +1 -1
  10. package/dist/zip-fs-external.js +438 -216
  11. package/dist/zip-fs-external.min.js +1 -1
  12. package/dist/zip-fs-native.js +447 -219
  13. package/dist/zip-fs-native.min.js +1 -1
  14. package/dist/zip-fs.js +445 -217
  15. package/dist/zip-fs.min.js +1 -1
  16. package/dist/zip-legacy.js +359 -207
  17. package/dist/zip-legacy.min.js +1 -1
  18. package/dist/zip-native.js +360 -208
  19. package/dist/zip-native.min.js +1 -1
  20. package/dist/zip-web-worker-native.js +1 -1
  21. package/dist/zip-web-worker.js +1 -1
  22. package/dist/zip.js +358 -206
  23. package/dist/zip.min.js +1 -1
  24. package/index-native.cjs +446 -218
  25. package/index-native.min.js +1 -1
  26. package/index.cjs +444 -216
  27. package/index.d.cts +124 -2
  28. package/index.d.ts +124 -2
  29. package/index.min.js +1 -1
  30. package/lib/core/codec-pool.js +1 -1
  31. package/lib/core/codec-registry.js +2 -2
  32. package/lib/core/codec-worker-web.js +436 -0
  33. package/lib/core/codec-worker.js +23 -388
  34. package/lib/core/constants.js +5 -1
  35. package/lib/core/io.js +2 -1
  36. package/lib/core/options.js +8 -0
  37. package/lib/core/streams/zlib-js/zlib-streams.min.js +1 -1
  38. package/lib/core/web-worker-base.js +10 -2
  39. package/lib/core/web-worker-inline-native.js +1 -1
  40. package/lib/core/web-worker-inline-wasm.js +1 -1
  41. package/lib/core/zip-fs.js +88 -12
  42. package/lib/core/zip-reader.js +90 -16
  43. package/lib/core/zip-writer.js +36 -13
  44. package/lib/zip-core-reader.js +6 -0
  45. package/lib/zip-core-writer.js +4 -0
  46. package/package.json +1 -1
  47. package/worker-message-property-names.js +1 -1
@@ -26,7 +26,7 @@
26
26
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
27
  */
28
28
 
29
- /* global WritableStream */
29
+ /* global WritableStream, AbortController */
30
30
  // deno-lint-ignore-file no-this-alias
31
31
 
32
32
  import {
@@ -71,6 +71,7 @@ import { toCompatibleReadable, toCompatibleWritable } from "./util/compatible-st
71
71
 
72
72
  const ERR_ENTRY_EXISTS = "Entry filename already exists";
73
73
  const ERR_READABLE_CONSUMED = "Readable stream already consumed";
74
+ const ERR_ABORT_EXPORT = "zipjs-abort-export";
74
75
  const INFOZIP_EXTRA_FIELD_TYPE = "infozip";
75
76
  const INTERPRETED_EXTRA_FIELD_TYPES = new Set([
76
77
  EXTRAFIELD_TYPE_ZIP64,
@@ -179,8 +180,9 @@ class ZipFileEntry extends ZipEntry {
179
180
  const uncompressedSize = zipEntry.data ? zipEntry.data.uncompressedSize : reader.size;
180
181
  await Promise.all([initStream(reader), initStream(writer, uncompressedSize)]);
181
182
  const { readable } = reader;
183
+ const { signal } = options;
182
184
  zipEntry.uncompressedSize = reader.size;
183
- await toCompatibleReadable(readable).pipeTo(toCompatibleWritable(writer.writable));
185
+ await toCompatibleReadable(readable).pipeTo(toCompatibleWritable(writer.writable), { signal });
184
186
  return writer.getData ? writer.getData() : writer.writable;
185
187
  }
186
188
  }
@@ -447,7 +449,7 @@ class ZipDirectoryEntry extends ZipEntry {
447
449
  for (const entry of entries) {
448
450
  let parent = this;
449
451
  try {
450
- const path = entry.filename.split("/");
452
+ const path = entry.filename.split("/").filter(pathPart => pathPart != "" && pathPart != ".");
451
453
  const name = path.pop();
452
454
  path.forEach(pathPart => {
453
455
  const previousParent = parent;
@@ -823,6 +825,39 @@ function detach(entry) {
823
825
  }
824
826
  }
825
827
 
828
+ function forwardAbort(signal, abortController) {
829
+ if (!signal) {
830
+ return () => { };
831
+ }
832
+ if (signal.aborted) {
833
+ abortController.abort(signal.reason);
834
+ return () => { };
835
+ }
836
+ const abort = () => abortController.abort(signal.reason);
837
+ signal.addEventListener("abort", abort, { once: true });
838
+ return () => signal.removeEventListener("abort", abort);
839
+ }
840
+
841
+ function isExportAborted(error) {
842
+ return Boolean(error) && error.message == ERR_ABORT_EXPORT;
843
+ }
844
+
845
+ function aggregateEntryErrors(errors) {
846
+ const [error] = errors;
847
+ const otherErrors = errors
848
+ .slice(1)
849
+ .flatMap(otherError => [otherError, ...(otherError && otherError.entryErrors || [])])
850
+ .filter(otherError => otherError !== error);
851
+ if (otherErrors.length) {
852
+ try {
853
+ error.entryErrors = [...(error.entryErrors || []), ...otherErrors];
854
+ } catch {
855
+ // ignored
856
+ }
857
+ }
858
+ return error;
859
+ }
860
+
826
861
  function getUserExtraField(extraField) {
827
862
  if (extraField) {
828
863
  const userExtraField = new Map();
@@ -943,8 +978,9 @@ async function exportZip(zipWriter, entry, totalSize, options, readers) {
943
978
  function addFileSystemHandle(zipEntry, handle, options) {
944
979
  return addFile(zipEntry, handle, []);
945
980
 
946
- async function addFile(parentEntry, handle, addedEntries) {
981
+ async function addFile(parentEntry, handle, addedEntries, parentName = "") {
947
982
  if (handle) {
983
+ const entryName = parentName ? parentName + "/" + handle.name : handle.name;
948
984
  try {
949
985
  if (handle.isFile || handle.isDirectory) {
950
986
  handle = await transformToFileSystemhandle(handle);
@@ -966,12 +1002,18 @@ function addFileSystemHandle(zipEntry, handle, options) {
966
1002
  const directoryEntry = parentEntry.addDirectory(handle.name);
967
1003
  addedEntries.push(directoryEntry);
968
1004
  for await (const childHandle of handle.values()) {
969
- await addFile(directoryEntry, childHandle, addedEntries);
1005
+ await addFile(directoryEntry, childHandle, addedEntries, entryName);
970
1006
  }
971
1007
  }
972
1008
  } catch (error) {
973
- const message = error.message + (handle ? " (" + handle.name + ")" : "");
974
- throw new Error(message, { cause: error });
1009
+ try {
1010
+ if (error.entryName === UNDEFINED_VALUE) {
1011
+ error.entryName = entryName;
1012
+ }
1013
+ } catch {
1014
+ // ignored
1015
+ }
1016
+ throw error;
975
1017
  }
976
1018
  }
977
1019
  return addedEntries;
@@ -981,15 +1023,32 @@ function addFileSystemHandle(zipEntry, handle, options) {
981
1023
  async function exportFileSystemHandle(zipEntry, directoryHandle, options) {
982
1024
  const totalSize = getTotalSize([zipEntry], "uncompressedSize");
983
1025
  const writtenSizes = new Map();
984
- await exportChildren(zipEntry, directoryHandle);
1026
+ const abortController = new AbortController();
1027
+ const { signal } = abortController;
1028
+ const releaseSignal = forwardAbort(options.signal, abortController);
1029
+ const exportedEntryNames = [];
1030
+ try {
1031
+ await exportChildren(zipEntry, directoryHandle);
1032
+ } catch (error) {
1033
+ try {
1034
+ error.exportedEntryNames = exportedEntryNames;
1035
+ } catch {
1036
+ // ignored
1037
+ }
1038
+ throw error;
1039
+ } finally {
1040
+ releaseSignal();
1041
+ }
985
1042
  return directoryHandle;
986
1043
 
987
1044
  async function exportChildren(entry, parentHandle) {
988
1045
  if (options.concurrent) {
989
1046
  const results = await Promise.allSettled(entry.children.map(child => exportChild(child, parentHandle)));
990
- const failedResult = results.find(result => result.status == "rejected");
991
- if (failedResult) {
992
- throw failedResult.reason;
1047
+ const rejectedResults = results.filter(result => result.status == "rejected");
1048
+ if (rejectedResults.length) {
1049
+ const failedResults = rejectedResults.filter(result => !isExportAborted(result.reason));
1050
+ const reportedResults = failedResults.length ? failedResults : rejectedResults;
1051
+ throw aggregateEntryErrors(reportedResults.map(result => result.reason));
993
1052
  }
994
1053
  } else {
995
1054
  for (const child of entry.children) {
@@ -999,6 +1058,12 @@ async function exportFileSystemHandle(zipEntry, directoryHandle, options) {
999
1058
  }
1000
1059
 
1001
1060
  async function exportChild(child, parentHandle) {
1061
+ if (signal.aborted) {
1062
+ if (isExportAborted(signal.reason)) {
1063
+ return;
1064
+ }
1065
+ throw signal.reason;
1066
+ }
1002
1067
  try {
1003
1068
  if (child.directory) {
1004
1069
  const childDirectoryHandle = await parentHandle.getDirectoryHandle(child.name, { create: true });
@@ -1007,6 +1072,7 @@ async function exportFileSystemHandle(zipEntry, directoryHandle, options) {
1007
1072
  const fileHandle = await parentHandle.getFileHandle(child.name, { create: true });
1008
1073
  const writable = await fileHandle.createWritable();
1009
1074
  await child.getData({ writable }, Object.assign({}, options, {
1075
+ signal,
1010
1076
  onprogress: async progress => {
1011
1077
  if (options.onprogress) {
1012
1078
  writtenSizes.set(child.id, progress);
@@ -1018,9 +1084,19 @@ async function exportFileSystemHandle(zipEntry, directoryHandle, options) {
1018
1084
  }
1019
1085
  }
1020
1086
  }));
1087
+ exportedEntryNames.push(child.getRelativeName(zipEntry));
1021
1088
  }
1022
1089
  } catch (error) {
1023
- throw new Error(error.message + (child ? " (" + child.name + ")" : ""), { cause: error });
1090
+ abortController.abort(new Error(ERR_ABORT_EXPORT));
1091
+ try {
1092
+ if (error.entryName === UNDEFINED_VALUE) {
1093
+ error.entryName = child.getRelativeName(zipEntry);
1094
+ error.entryId = child.id;
1095
+ }
1096
+ } catch {
1097
+ // ignored
1098
+ }
1099
+ throw error;
1024
1100
  }
1025
1101
  }
1026
1102
  }
@@ -88,6 +88,8 @@ import {
88
88
  FILE_ATTR_UNIX_SETUID_MASK,
89
89
  FILE_ATTR_UNIX_SETGID_MASK,
90
90
  FILE_ATTR_UNIX_STICKY_MASK,
91
+ NUMBER_TYPE,
92
+ STRING_TYPE,
91
93
  EMPTY_UINT8_ARRAY
92
94
  } from "./constants.js";
93
95
  import { getConfiguration } from "./configuration.js";
@@ -150,8 +152,12 @@ import {
150
152
  OPTION_CHECK_CRC32,
151
153
  OPTION_CHECK_AUTHENTICATION_CODE,
152
154
  OPTION_STRICTNESS,
155
+ OPTION_FILENAME_VALIDATION,
156
+ OPTION_NORMALIZE_FILENAME,
153
157
  OPTION_MAX_APPENDED_DATA_SIZE,
154
158
  OPTION_DECRYPT_CENTRAL_DIRECTORY,
159
+ TEXT_TYPE_FILENAME,
160
+ TEXT_TYPE_COMMENT,
155
161
  STRICTNESS_STRICT,
156
162
  STRICTNESS_BALANCED,
157
163
  STRICTNESS_TOLERANT,
@@ -174,6 +180,11 @@ const ERR_SPLIT_ZIP_FILE = "Split zip file";
174
180
  const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
175
181
  const ERR_AMBIGUOUS_ARCHIVE = "Ambiguous archive";
176
182
  const ERR_ENCRYPTED_CENTRAL_DIRECTORY = "Encrypted central directory is not supported";
183
+ const ERR_UNSAFE_FILENAME = "Unsafe filename";
184
+ const ERR_INVALID_STRICTNESS = "Invalid strictness (must be 'strict', 'balanced' or 'tolerant')";
185
+ const ERR_INVALID_FILENAME_VALIDATION = "Invalid filenameValidation (must be 'strict', 'balanced' or 'tolerant')";
186
+ const ERR_INVALID_MAX_APPENDED_DATA_SIZE = "Invalid maxAppendedDataSize (must be a number greater than or equal to 0)";
187
+ const DRIVE_LETTER_REGEXP = /^[a-zA-Z]:/;
177
188
  const CHARSET_UTF8 = "utf-8";
178
189
  const PROPERTY_NAME_UTF8_SUFFIX = "UTF8";
179
190
  const CHARSET_CP437 = "cp437";
@@ -195,6 +206,10 @@ const ZIP64_EXTRACTION = {
195
206
  bytes: 8
196
207
  }
197
208
  };
209
+ const MAX_END_OF_CENTRAL_DIR_PROBES = 64;
210
+ const CENTRAL_DIRECTORY_UNREACHABLE = 0;
211
+ const CENTRAL_DIRECTORY_PLAUSIBLE = 1;
212
+ const CENTRAL_DIRECTORY_REACHABLE = 2;
198
213
 
199
214
  class ZipReader {
200
215
 
@@ -223,6 +238,8 @@ class ZipReader {
223
238
  const checkAmbiguity = strictness == STRICTNESS_STRICT;
224
239
  const rejectAmbiguousEndOfDirectory = strictness != STRICTNESS_TOLERANT;
225
240
  const maxAppendedDataSize = getMaxAppendedDataSize(getOptionValue(zipReader, options, OPTION_MAX_APPENDED_DATA_SIZE), strictness);
241
+ const filenameValidation = getFilenameValidation(getOptionValue(zipReader, options, OPTION_FILENAME_VALIDATION), strictness);
242
+ const normalizeFilename = getOptionValue(zipReader, options, OPTION_NORMALIZE_FILENAME);
226
243
  const { endOfDirectoryInfo, endOfDirectoryReachingEndCount } = await findEndOfCentralDirectory(reader, rejectAmbiguousEndOfDirectory, maxAppendedDataSize);
227
244
  if (!endOfDirectoryInfo) {
228
245
  const signatureArray = await readUint8Array(reader, 0, 4);
@@ -416,11 +433,22 @@ class ZipReader {
416
433
  const decode = getOptionValue(zipReader, options, OPTION_DECODE_TEXT) || decodeText;
417
434
  const rawFilenameEncoding = filenameUTF8 ? CHARSET_UTF8 : filenameEncoding || CHARSET_CP437;
418
435
  const rawCommentEncoding = commentUTF8 ? CHARSET_UTF8 : commentEncoding || CHARSET_CP437;
419
- let filename = decode(rawFilename, rawFilenameEncoding);
436
+ let filename = decode(rawFilename, rawFilenameEncoding, TEXT_TYPE_FILENAME);
420
437
  if (filename === UNDEFINED_VALUE) {
421
438
  filename = decodeText(rawFilename, rawFilenameEncoding);
422
439
  }
423
- let comment = decode(rawComment, rawCommentEncoding);
440
+ if (normalizeFilename) {
441
+ const normalizedFilename = normalizeFilename(filename);
442
+ if (normalizedFilename !== UNDEFINED_VALUE) {
443
+ filename = normalizedFilename;
444
+ }
445
+ }
446
+ if (isUnsafeFilename(filename, filenameValidation)) {
447
+ const error = new Error(ERR_UNSAFE_FILENAME);
448
+ error.filename = filename;
449
+ throw error;
450
+ }
451
+ let comment = decode(rawComment, rawCommentEncoding, TEXT_TYPE_COMMENT);
424
452
  if (comment === UNDEFINED_VALUE) {
425
453
  comment = decodeText(rawComment, rawCommentEncoding);
426
454
  }
@@ -481,10 +509,10 @@ class ZipReader {
481
509
  entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader.readRanges, options);
482
510
  entry.arrayBuffer = async options => {
483
511
  const writer = new TransformStream();
484
- const [arrayBuffer] = await Promise.all([
485
- streamToBlob(writer.readable).then(blob => blob.arrayBuffer()),
486
- fileEntry.getData(writer, entry, zipReader.readRanges, options)]);
487
- return arrayBuffer;
512
+ const arrayBufferPromise = streamToBlob(writer.readable).then(blob => blob.arrayBuffer());
513
+ arrayBufferPromise.catch(() => { });
514
+ await fileEntry.getData(writer, entry, zipReader.readRanges, options);
515
+ return arrayBufferPromise;
488
516
  };
489
517
  offset = endOffset;
490
518
  const { onprogress } = options;
@@ -600,6 +628,10 @@ export {
600
628
  ERR_OVERLAPPING_ENTRY,
601
629
  ERR_AMBIGUOUS_ARCHIVE,
602
630
  ERR_ENCRYPTED_CENTRAL_DIRECTORY,
631
+ ERR_UNSAFE_FILENAME,
632
+ ERR_INVALID_STRICTNESS,
633
+ ERR_INVALID_FILENAME_VALIDATION,
634
+ ERR_INVALID_MAX_APPENDED_DATA_SIZE,
603
635
  ERR_WORKER_STARTUP_TIMEOUT
604
636
  };
605
637
 
@@ -760,7 +792,7 @@ class ZipEntry {
760
792
  readRanges
761
793
  });
762
794
  }
763
- let writable;
795
+ let writable, abortError;
764
796
  try {
765
797
  if (!checkOverlappingEntryOnly) {
766
798
  if (checkPasswordOnly) {
@@ -780,12 +812,22 @@ class ZipEntry {
780
812
  writer.size += error.outputSize;
781
813
  }
782
814
  if (!checkPasswordOnly || error.message != ERR_ABORT_CHECK_PASSWORD) {
815
+ abortError = error;
783
816
  throw error;
784
817
  }
785
818
  } finally {
786
819
  const preventClose = getOptionValue(zipEntry, options, OPTION_PREVENT_CLOSE);
787
820
  if (!preventClose && writable && !writable.locked) {
788
- await writable.getWriter().close();
821
+ const writableWriter = writable.getWriter();
822
+ if (abortError) {
823
+ try {
824
+ await writableWriter.abort(abortError);
825
+ } catch {
826
+ // the error being propagated is more relevant; ignored
827
+ }
828
+ } else {
829
+ await writableWriter.close();
830
+ }
789
831
  }
790
832
  }
791
833
  return checkPasswordOnly || checkOverlappingEntryOnly ? UNDEFINED_VALUE : writer.getData ? writer.getData() : writable;
@@ -1165,16 +1207,51 @@ function getDiskOffset(reader, diskNumber) {
1165
1207
  return reader.getDiskOffset ? reader.getDiskOffset(diskNumber) : 0;
1166
1208
  }
1167
1209
 
1210
+ function isStrictnessValue(value) {
1211
+ return value === STRICTNESS_STRICT || value === STRICTNESS_BALANCED || value === STRICTNESS_TOLERANT;
1212
+ }
1213
+
1168
1214
  function getStrictness(strictness, checkAmbiguity) {
1169
1215
  if (strictness === UNDEFINED_VALUE) {
1170
1216
  return checkAmbiguity ? STRICTNESS_STRICT : STRICTNESS_BALANCED;
1171
1217
  }
1218
+ if (!isStrictnessValue(strictness)) {
1219
+ throw new Error(ERR_INVALID_STRICTNESS);
1220
+ }
1172
1221
  return strictness;
1173
1222
  }
1174
1223
 
1224
+ function getFilenameValidation(filenameValidation, strictness) {
1225
+ if (filenameValidation === UNDEFINED_VALUE) {
1226
+ return strictness;
1227
+ }
1228
+ if (!isStrictnessValue(filenameValidation)) {
1229
+ throw new Error(ERR_INVALID_FILENAME_VALIDATION);
1230
+ }
1231
+ return filenameValidation;
1232
+ }
1233
+
1234
+ function isUnsafeFilename(filename, filenameValidation) {
1235
+ if (filenameValidation == STRICTNESS_TOLERANT) {
1236
+ return false;
1237
+ }
1238
+ const pathParts = filename.split("/");
1239
+ if (pathParts.length > 1 && pathParts[pathParts.length - 1] === "") {
1240
+ pathParts.pop();
1241
+ }
1242
+ if (pathParts.includes("..") || filename.startsWith("/") || filename.startsWith("\\\\") || DRIVE_LETTER_REGEXP.test(filename)) {
1243
+ return true;
1244
+ }
1245
+ return filenameValidation == STRICTNESS_STRICT && (pathParts.includes(".") || pathParts.includes(""));
1246
+ }
1247
+
1175
1248
  function getMaxAppendedDataSize(maxAppendedDataSize, strictness) {
1176
1249
  if (maxAppendedDataSize !== UNDEFINED_VALUE) {
1177
- return maxAppendedDataSize;
1250
+ const size = toNumber(maxAppendedDataSize);
1251
+ if (typeof size != NUMBER_TYPE || Number.isNaN(size) || size < 0) {
1252
+ throw new Error(ERR_INVALID_MAX_APPENDED_DATA_SIZE);
1253
+ }
1254
+ return size;
1178
1255
  }
1179
1256
  if (strictness == STRICTNESS_STRICT) {
1180
1257
  return 0;
@@ -1185,12 +1262,6 @@ function getMaxAppendedDataSize(maxAppendedDataSize, strictness) {
1185
1262
  return MAX_16_BITS;
1186
1263
  }
1187
1264
 
1188
- const MAX_END_OF_CENTRAL_DIR_PROBES = 64;
1189
-
1190
- const CENTRAL_DIRECTORY_UNREACHABLE = 0;
1191
- const CENTRAL_DIRECTORY_PLAUSIBLE = 1;
1192
- const CENTRAL_DIRECTORY_REACHABLE = 2;
1193
-
1194
1265
  async function findEndOfCentralDirectory(reader, rejectAmbiguous, maxAppendedDataSize) {
1195
1266
  const { size } = reader;
1196
1267
  const anchoredLength = Math.min(size, END_OF_CENTRAL_DIR_LENGTH + MAX_16_BITS);
@@ -1326,6 +1397,10 @@ function getOptionValue(zipReader, options, name) {
1326
1397
  return options[name] === UNDEFINED_VALUE ? zipReader.options[name] : options[name];
1327
1398
  }
1328
1399
 
1400
+ function toNumber(value) {
1401
+ return typeof value == STRING_TYPE && value.trim() ? Number(value) : value;
1402
+ }
1403
+
1329
1404
  function getDate(timeRaw) {
1330
1405
  const date = (timeRaw & 0xffff0000) >> 16, time = timeRaw & MAX_16_BITS;
1331
1406
  return new Date(1980 + ((date & 0xFE00) >> 9), ((date & 0x01E0) >> 5) - 1, date & 0x001F, (time & 0xF800) >> 11, (time & 0x07E0) >> 5, (time & 0x001F) * 2, 0);
@@ -1350,4 +1425,3 @@ function getUint32(view, offset) {
1350
1425
  function getBigUint64(view, offset) {
1351
1426
  return Number(view.getBigUint64(offset, true));
1352
1427
  }
1353
-
@@ -140,6 +140,7 @@ import {
140
140
  UNDEFINED_VALUE,
141
141
  INFINITY_VALUE,
142
142
  OBJECT_TYPE,
143
+ STRING_TYPE,
143
144
  EMPTY_UINT8_ARRAY
144
145
  } from "./constants.js";
145
146
  import { getConfiguration } from "./configuration.js";
@@ -212,7 +213,9 @@ import {
212
213
  OPTION_USDZ,
213
214
  OPTION_UNIX_EXTRA_FIELD_TYPE,
214
215
  OPTION_LOCAL_EXTRA_FIELD,
215
- OPTION_SIGN_CENTRAL_DIRECTORY
216
+ OPTION_SIGN_CENTRAL_DIRECTORY,
217
+ TEXT_TYPE_FILENAME,
218
+ TEXT_TYPE_COMMENT
216
219
  } from "./options.js";
217
220
 
218
221
  const ERR_DUPLICATED_NAME = "File already exists";
@@ -240,6 +243,8 @@ const ERR_INVALID_UNIX_EXTRA_FIELD_TYPE = "Invalid unixExtraFieldType (must be '
240
243
  const ERR_INVALID_UNIX_ID_SIZE = "uid/gid must be 0..65535 for unixExtraFieldType 'unix' (use 'infozip' for larger ids)";
241
244
  const ERR_INVALID_MSDOS_ATTRIBUTES = "Invalid msdosAttributesRaw (must be integer 0..255)";
242
245
  const ERR_INVALID_MSDOS_DATA = "Invalid msdosAttributes (must be an object with boolean flags)";
246
+ const ERR_INVALID_LEVEL = "Invalid level (must be integer 0..9)";
247
+ const ERR_INVALID_PASSWORD_TYPE = "Invalid password (password must be a string, rawPassword must be a Uint8Array)";
243
248
  const ERR_INVALID_SIGNATURE_DATA = "Signature data exceeds 64KB";
244
249
 
245
250
  const EXTRAFIELD_DATA_AES = new Uint8Array([0x07, 0x00, 0x02, 0x00, 0x41, 0x45, 0x03, 0x00, 0x00]);
@@ -247,6 +252,7 @@ const EXTRAFIELD_OFFSET_AES_VENDOR_VERSION = 4;
247
252
  const VENDOR_VERSION_AE_1 = 1;
248
253
  const INFOZIP_EXTRA_FIELD_TYPE = "infozip";
249
254
  const UNIX_EXTRA_FIELD_TYPE = "unix";
255
+ const MAX_LEVEL = 9;
250
256
 
251
257
  let workers = 0;
252
258
  const pendingEntries = [];
@@ -409,7 +415,8 @@ class ZipWriter {
409
415
 
410
416
  remove(entry) {
411
417
  const { filenames, files } = this;
412
- if (typeof entry == "string") {
418
+ // deno-lint-ignore valid-typeof
419
+ if (typeof entry == STRING_TYPE) {
413
420
  entry = files.get(entry);
414
421
  }
415
422
  if (entry && entry.filename !== UNDEFINED_VALUE) {
@@ -528,6 +535,8 @@ export {
528
535
  ERR_INVALID_UNIX_ID_SIZE,
529
536
  ERR_INVALID_MSDOS_ATTRIBUTES,
530
537
  ERR_INVALID_MSDOS_DATA,
538
+ ERR_INVALID_LEVEL,
539
+ ERR_INVALID_PASSWORD_TYPE,
531
540
  ERR_UNSUPPORTED_CRYPTO_API
532
541
  };
533
542
 
@@ -574,20 +583,20 @@ function resolveAttributes(zipWriter, name, options) {
574
583
  let msDosCompatible = getOptionValue(zipWriter, options, PROPERTY_NAME_MS_DOS_COMPATIBLE);
575
584
  let versionMadeBy = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION_MADE_BY, msDosCompatible ? 20 : 768);
576
585
  const executable = getOptionValue(zipWriter, options, PROPERTY_NAME_EXECUTABLE);
577
- const uid = getOptionValue(zipWriter, options, PROPERTY_NAME_UID);
578
- const gid = getOptionValue(zipWriter, options, PROPERTY_NAME_GID);
579
- let unixMode = getOptionValue(zipWriter, options, PROPERTY_NAME_UNIX_MODE);
586
+ const uid = getNumberOptionValue(zipWriter, options, PROPERTY_NAME_UID);
587
+ const gid = getNumberOptionValue(zipWriter, options, PROPERTY_NAME_GID);
588
+ let unixMode = getNumberOptionValue(zipWriter, options, PROPERTY_NAME_UNIX_MODE);
580
589
  let unixExtraFieldType = getOptionValue(zipWriter, options, OPTION_UNIX_EXTRA_FIELD_TYPE);
581
590
  let setuid = getOptionValue(zipWriter, options, PROPERTY_NAME_SETUID);
582
591
  let setgid = getOptionValue(zipWriter, options, PROPERTY_NAME_SETGID);
583
592
  let sticky = getOptionValue(zipWriter, options, PROPERTY_NAME_STICKY);
584
- if (uid !== UNDEFINED_VALUE && (uid < 0 || uid > MAX_32_BITS)) {
593
+ if (uid !== UNDEFINED_VALUE && (!Number.isInteger(uid) || uid < 0 || uid > MAX_32_BITS)) {
585
594
  throw new Error(ERR_INVALID_UID);
586
595
  }
587
- if (gid !== UNDEFINED_VALUE && (gid < 0 || gid > MAX_32_BITS)) {
596
+ if (gid !== UNDEFINED_VALUE && (!Number.isInteger(gid) || gid < 0 || gid > MAX_32_BITS)) {
588
597
  throw new Error(ERR_INVALID_GID);
589
598
  }
590
- if (unixMode !== UNDEFINED_VALUE && (unixMode < 0 || unixMode > MAX_16_BITS)) {
599
+ if (unixMode !== UNDEFINED_VALUE && (!Number.isInteger(unixMode) || unixMode < 0 || unixMode > MAX_16_BITS)) {
591
600
  throw new Error(ERR_INVALID_UNIX_MODE);
592
601
  }
593
602
  if (unixExtraFieldType !== UNDEFINED_VALUE && unixExtraFieldType !== INFOZIP_EXTRA_FIELD_TYPE && unixExtraFieldType !== UNIX_EXTRA_FIELD_TYPE) {
@@ -699,7 +708,7 @@ function resolveAttributes(zipWriter, name, options) {
699
708
 
700
709
  function resolveMetadata(zipWriter, name, options) {
701
710
  const encode = getOptionValue(zipWriter, options, OPTION_ENCODE_TEXT, encodeText);
702
- let rawFilename = encode(name);
711
+ let rawFilename = encode(name, TEXT_TYPE_FILENAME);
703
712
  if (rawFilename === UNDEFINED_VALUE) {
704
713
  rawFilename = encodeText(name);
705
714
  }
@@ -707,7 +716,7 @@ function resolveMetadata(zipWriter, name, options) {
707
716
  throw new Error(ERR_INVALID_ENTRY_NAME);
708
717
  }
709
718
  const comment = options[PROPERTY_NAME_COMMENT] || "";
710
- let rawComment = encode(comment);
719
+ let rawComment = encode(comment, TEXT_TYPE_COMMENT);
711
720
  if (rawComment === UNDEFINED_VALUE) {
712
721
  rawComment = encodeText(comment);
713
722
  }
@@ -727,8 +736,11 @@ function resolveMetadata(zipWriter, name, options) {
727
736
  if (!passThrough) {
728
737
  password = getOptionValue(zipWriter, options, OPTION_PASSWORD);
729
738
  rawPassword = getOptionValue(zipWriter, options, OPTION_RAW_PASSWORD);
739
+ if ((password && typeof password != STRING_TYPE) || (rawPassword && !(rawPassword instanceof Uint8Array))) {
740
+ throw new Error(ERR_INVALID_PASSWORD_TYPE);
741
+ }
730
742
  }
731
- const encryptionStrength = getOptionValue(zipWriter, options, OPTION_ENCRYPTION_STRENGTH, 3);
743
+ const encryptionStrength = getNumberOptionValue(zipWriter, options, OPTION_ENCRYPTION_STRENGTH, 3);
732
744
  const zipCrypto = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIPCRYPTO);
733
745
  const extendedTimestamp = getOptionValue(zipWriter, options, OPTION_EXTENDED_TIMESTAMP, true);
734
746
  const ntfsTimestamp = getOptionValue(zipWriter, options, OPTION_NTFS_TIMESTAMP);
@@ -746,7 +758,10 @@ function resolveMetadata(zipWriter, name, options) {
746
758
  compressionMethod !== COMPRESSION_METHOD_STORE && compressionMethod !== COMPRESSION_METHOD_DEFLATE && !registeredCodec) {
747
759
  throw new Error(ERR_UNSUPPORTED_COMPRESSION);
748
760
  }
749
- let level = getOptionValue(zipWriter, options, OPTION_LEVEL);
761
+ let level = getNumberOptionValue(zipWriter, options, OPTION_LEVEL);
762
+ if (level !== UNDEFINED_VALUE && (!Number.isInteger(level) || level < 0 || level > MAX_LEVEL)) {
763
+ throw new Error(ERR_INVALID_LEVEL);
764
+ }
750
765
  if (zipWriter.options[OPTION_USDZ]) {
751
766
  if (password !== UNDEFINED_VALUE || rawPassword !== UNDEFINED_VALUE) {
752
767
  throw new Error(ERR_UNSUPPORTED_ENCRYPTION_USDZ);
@@ -767,7 +782,7 @@ function resolveMetadata(zipWriter, name, options) {
767
782
  useCompressionStream = false;
768
783
  }
769
784
  const zip64 = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIP64);
770
- if (!zipCrypto && (password !== UNDEFINED_VALUE || rawPassword !== UNDEFINED_VALUE) && !(encryptionStrength >= 1 && encryptionStrength <= 3)) {
785
+ if (!zipCrypto && (password !== UNDEFINED_VALUE || rawPassword !== UNDEFINED_VALUE) && !(Number.isInteger(encryptionStrength) && encryptionStrength >= 1 && encryptionStrength <= 3)) {
771
786
  throw new Error(ERR_INVALID_ENCRYPTION_STRENGTH);
772
787
  }
773
788
  const rawExtraField = serializeExtraField(options[PROPERTY_NAME_EXTRA_FIELD]);
@@ -1961,6 +1976,14 @@ function getOptionValue(zipWriter, options, name, defaultValue) {
1961
1976
  return result === UNDEFINED_VALUE ? defaultValue : result;
1962
1977
  }
1963
1978
 
1979
+ function getNumberOptionValue(zipWriter, options, name, defaultValue) {
1980
+ return toNumber(getOptionValue(zipWriter, options, name, defaultValue));
1981
+ }
1982
+
1983
+ function toNumber(value) {
1984
+ return typeof value == STRING_TYPE && value.trim() ? Number(value) : value;
1985
+ }
1986
+
1964
1987
  function getMaximumCompressedSize(uncompressedSize) {
1965
1988
  return uncompressedSize + (5 * (Math.floor(uncompressedSize / 16383) + 1));
1966
1989
  }
@@ -26,6 +26,8 @@
26
26
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
27
  */
28
28
 
29
+ import "./core/codec-worker-web.js";
30
+
29
31
  export {
30
32
  ZipReader,
31
33
  ZipReaderStream,
@@ -48,5 +50,9 @@ export {
48
50
  ERR_OVERLAPPING_ENTRY,
49
51
  ERR_AMBIGUOUS_ARCHIVE,
50
52
  ERR_ENCRYPTED_CENTRAL_DIRECTORY,
53
+ ERR_UNSAFE_FILENAME,
54
+ ERR_INVALID_STRICTNESS,
55
+ ERR_INVALID_FILENAME_VALIDATION,
56
+ ERR_INVALID_MAX_APPENDED_DATA_SIZE,
51
57
  ERR_WORKER_STARTUP_TIMEOUT
52
58
  } from "./core/zip-reader.js";
@@ -26,6 +26,8 @@
26
26
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
27
  */
28
28
 
29
+ import "./core/codec-worker-web.js";
30
+
29
31
  export {
30
32
  ZipWriter,
31
33
  ZipWriterStream,
@@ -50,5 +52,7 @@ export {
50
52
  ERR_INVALID_UNIX_ID_SIZE,
51
53
  ERR_INVALID_MSDOS_ATTRIBUTES,
52
54
  ERR_INVALID_MSDOS_DATA,
55
+ ERR_INVALID_LEVEL,
56
+ ERR_INVALID_PASSWORD_TYPE,
53
57
  ERR_UNSUPPORTED_CRYPTO_API
54
58
  } from "./core/zip-writer.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.48",
6
+ "version": "2.8.50",
7
7
  "type": "module",
8
8
  "keywords": [
9
9
  "zip",
@@ -1,3 +1,3 @@
1
- const WORKER_MESSAGE_PROPERTY_NAMES = ["baseURI", "cause", "checkAuthenticationCode", "checkCrc32", "checkPasswordOnly", "chunkSize", "code", "codecType", "codecURI", "compressed", "compressionMethod", "computeCrc32", "config", "crc32", "deflate64", "done", "encrypted", "encryptionStrength", "error", "format", "inputSize", "level", "message", "messageId", "name", "options", "outputSize", "password", "passwordVerification", "rawBitFlag", "rawPassword", "readable", "result", "stack", "transferStreams", "type", "useCompressionStream", "useWebWorkers", "value", "wasmURI", "workerStartupTimeout", "writable", "zipCrypto"];
1
+ const WORKER_MESSAGE_PROPERTY_NAMES = ["baseURI", "cause", "checkAuthenticationCode", "checkCrc32", "checkPasswordOnly", "chunkSize", "code", "codecImportFailed", "codecType", "codecURI", "compressed", "compressionMethod", "computeCrc32", "config", "crc32", "deflate64", "done", "encrypted", "encryptionStrength", "error", "format", "inputSize", "level", "message", "messageId", "name", "options", "outputSize", "password", "passwordVerification", "rawBitFlag", "rawPassword", "readable", "result", "stack", "transferStreams", "type", "useCompressionStream", "useWebWorkers", "value", "wasmURI", "workerStartupTimeout", "writable", "zipCrypto"];
2
2
 
3
3
  export { WORKER_MESSAGE_PROPERTY_NAMES };