@zip.js/zip.js 2.8.49 → 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.
- package/deno.json +1 -1
- package/dist/zip-core-external.js +333 -203
- package/dist/zip-core-external.min.js +1 -1
- package/dist/zip-core.js +339 -203
- package/dist/zip-core.min.js +1 -1
- package/dist/zip-fs-core-external.js +420 -214
- package/dist/zip-fs-core-external.min.js +1 -1
- package/dist/zip-fs-core.js +212 -450
- package/dist/zip-fs-core.min.js +1 -1
- package/dist/zip-fs-external.js +420 -214
- package/dist/zip-fs-external.min.js +1 -1
- package/dist/zip-fs-native.js +426 -214
- package/dist/zip-fs-native.min.js +1 -1
- package/dist/zip-fs.js +426 -214
- package/dist/zip-fs.min.js +1 -1
- package/dist/zip-legacy.js +339 -203
- package/dist/zip-legacy.min.js +1 -1
- package/dist/zip-native.js +339 -203
- package/dist/zip-native.min.js +1 -1
- package/dist/zip.js +339 -203
- package/dist/zip.min.js +1 -1
- package/index-native.cjs +425 -213
- package/index-native.min.js +1 -1
- package/index.cjs +425 -213
- package/index.d.cts +124 -2
- package/index.d.ts +124 -2
- package/index.min.js +1 -1
- package/lib/core/codec-registry.js +2 -2
- package/lib/core/codec-worker-web.js +436 -0
- package/lib/core/codec-worker.js +23 -404
- package/lib/core/constants.js +5 -1
- package/lib/core/io.js +2 -1
- package/lib/core/options.js +8 -0
- package/lib/core/zip-fs.js +88 -12
- package/lib/core/zip-reader.js +90 -16
- package/lib/core/zip-writer.js +36 -13
- package/lib/zip-core-reader.js +6 -0
- package/lib/zip-core-writer.js +4 -0
- package/package.json +1 -1
package/lib/core/zip-fs.js
CHANGED
|
@@ -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
|
-
|
|
974
|
-
|
|
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
|
-
|
|
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
|
|
991
|
-
if (
|
|
992
|
-
|
|
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
|
-
|
|
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
|
}
|
package/lib/core/zip-reader.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
package/lib/core/zip-writer.js
CHANGED
|
@@ -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
|
-
|
|
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 =
|
|
578
|
-
const gid =
|
|
579
|
-
let unixMode =
|
|
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 =
|
|
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 =
|
|
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
|
}
|
package/lib/zip-core-reader.js
CHANGED
|
@@ -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";
|
package/lib/zip-core-writer.js
CHANGED
|
@@ -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