@poe-platform/safe-bash 0.1.656 → 0.1.657
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/dist/safe-bash/chunks/{xz-U4QDTPZL.js → xz-4V5FXSPG.js} +555 -514
- package/dist/safe-bash/chunks/xz-4V5FXSPG.js.map +7 -0
- package/dist/safe-bash/commands/archive/index.d.ts +1 -1
- package/dist/safe-bash/commands/archive/index.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/internal.d.ts +32 -0
- package/dist/safe-bash/commands/archive/internal.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/internal.js.map +1 -1
- package/dist/safe-bash/commands/archive/unzip/arguments.d.ts +3 -0
- package/dist/safe-bash/commands/archive/unzip/arguments.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/unzip/arguments.js +31 -5
- package/dist/safe-bash/commands/archive/unzip/arguments.js.map +1 -1
- package/dist/safe-bash/commands/archive/unzip/safety.d.ts +4 -0
- package/dist/safe-bash/commands/archive/unzip/safety.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/unzip/safety.js +3 -0
- package/dist/safe-bash/commands/archive/unzip/safety.js.map +1 -1
- package/dist/safe-bash/commands/archive/unzip.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/unzip.js +159 -112
- package/dist/safe-bash/commands/archive/unzip.js.map +1 -1
- package/dist/safe-bash/commands/archive/zip/aes-primitives-unavailable.d.ts +2 -0
- package/dist/safe-bash/commands/archive/zip/aes-primitives-unavailable.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/aes-primitives-unavailable.js +3 -0
- package/dist/safe-bash/commands/archive/zip/aes-primitives-unavailable.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/aes-primitives.d.ts +2 -0
- package/dist/safe-bash/commands/archive/zip/aes-primitives.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/aes-primitives.js +3 -0
- package/dist/safe-bash/commands/archive/zip/aes-primitives.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/aes.d.ts +18 -0
- package/dist/safe-bash/commands/archive/zip/aes.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/aes.js +157 -0
- package/dist/safe-bash/commands/archive/zip/aes.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/crypto.d.ts +16 -0
- package/dist/safe-bash/commands/archive/zip/crypto.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/zip/crypto.js +85 -0
- package/dist/safe-bash/commands/archive/zip/crypto.js.map +1 -1
- package/dist/safe-bash/commands/archive/zip/grow.d.ts +4 -0
- package/dist/safe-bash/commands/archive/zip/grow.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/grow.js +3 -0
- package/dist/safe-bash/commands/archive/zip/grow.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/help.d.ts +4 -2
- package/dist/safe-bash/commands/archive/zip/help.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/zip/help.js +98 -3
- package/dist/safe-bash/commands/archive/zip/help.js.map +1 -1
- package/dist/safe-bash/commands/archive/zip/line-endings.d.ts +1 -0
- package/dist/safe-bash/commands/archive/zip/line-endings.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/zip/line-endings.js +158 -0
- package/dist/safe-bash/commands/archive/zip/line-endings.js.map +1 -1
- package/dist/safe-bash/commands/archive/zip/log.d.ts +23 -0
- package/dist/safe-bash/commands/archive/zip/log.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/log.js +159 -0
- package/dist/safe-bash/commands/archive/zip/log.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/lzma.d.ts +10 -0
- package/dist/safe-bash/commands/archive/zip/lzma.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/lzma.js +41 -0
- package/dist/safe-bash/commands/archive/zip/lzma.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/names.d.ts +3 -0
- package/dist/safe-bash/commands/archive/zip/names.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/names.js +34 -0
- package/dist/safe-bash/commands/archive/zip/names.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/options.d.ts +7 -0
- package/dist/safe-bash/commands/archive/zip/options.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/zip/options.js +88 -17
- package/dist/safe-bash/commands/archive/zip/options.js.map +1 -1
- package/dist/safe-bash/commands/archive/zip/repair.d.ts +11 -0
- package/dist/safe-bash/commands/archive/zip/repair.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/repair.js +392 -0
- package/dist/safe-bash/commands/archive/zip/repair.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/safety.d.ts +23 -4
- package/dist/safe-bash/commands/archive/zip/safety.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/zip/safety.js +73 -23
- package/dist/safe-bash/commands/archive/zip/safety.js.map +1 -1
- package/dist/safe-bash/commands/archive/zip/sfx.d.ts +5 -0
- package/dist/safe-bash/commands/archive/zip/sfx.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/sfx.js +56 -0
- package/dist/safe-bash/commands/archive/zip/sfx.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/test-command.d.ts +6 -0
- package/dist/safe-bash/commands/archive/zip/test-command.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/test-command.js +101 -0
- package/dist/safe-bash/commands/archive/zip/test-command.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/volumes.d.ts +22 -0
- package/dist/safe-bash/commands/archive/zip/volumes.d.ts.map +1 -0
- package/dist/safe-bash/commands/archive/zip/volumes.js +265 -0
- package/dist/safe-bash/commands/archive/zip/volumes.js.map +1 -0
- package/dist/safe-bash/commands/archive/zip/zip64.d.ts +20 -1
- package/dist/safe-bash/commands/archive/zip/zip64.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/zip/zip64.js +127 -17
- package/dist/safe-bash/commands/archive/zip/zip64.js.map +1 -1
- package/dist/safe-bash/commands/archive/zip-format.d.ts +30 -5
- package/dist/safe-bash/commands/archive/zip-format.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/zip-format.js +489 -91
- package/dist/safe-bash/commands/archive/zip-format.js.map +1 -1
- package/dist/safe-bash/commands/archive/zip.d.ts.map +1 -1
- package/dist/safe-bash/commands/archive/zip.js +729 -128
- package/dist/safe-bash/commands/archive/zip.js.map +1 -1
- package/dist/safe-bash/commands/bytes/compression/bounded-codec.d.ts +7 -0
- package/dist/safe-bash/commands/bytes/compression/bounded-codec.d.ts.map +1 -1
- package/dist/safe-bash/commands/bytes/compression/bounded-codec.js.map +1 -1
- package/dist/safe-bash/commands/bytes/compression/codec-loader.d.ts.map +1 -1
- package/dist/safe-bash/commands/bytes/compression/codec-loader.js +8 -1
- package/dist/safe-bash/commands/bytes/compression/codec-loader.js.map +1 -1
- package/dist/safe-bash/commands/bytes/compression/native/generated/xz.mjs +1 -1
- package/dist/safe-bash/commands/bytes/compression/native/sources.json +2 -2
- package/dist/safe-bash/commands/bytes/compression/native/types.d.ts +1 -0
- package/dist/safe-bash/commands/bytes/compression/native/types.d.ts.map +1 -1
- package/dist/safe-bash/commands/text.js +1 -1
- package/dist/safe-bash/commands/text.js.map +1 -1
- package/dist/safe-bash/core.browser.js +3106 -717
- package/dist/safe-bash/core.browser.js.map +4 -4
- package/package.json +11 -3
- package/dist/safe-bash/chunks/xz-U4QDTPZL.js.map +0 -7
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { zipGrowRecords } from "./zip/grow.js";
|
|
2
|
+
import { zip64Directory, zip64Fields, zipDiskOffset, zipDiskRecord, stripZip64, zip64Extra, zip64Member, zipEnd, zipDescriptor } from "./zip/zip64.js";
|
|
2
3
|
import { crcTable } from "./zip/crc.js";
|
|
3
|
-
import {
|
|
4
|
+
import { encryptZipPayload, decryptZipPayload } from "./zip/crypto.js";
|
|
5
|
+
import { aesParameters, aesExtra, encryptAesPayload, decryptAesPayload } from "./zip/aes.js";
|
|
6
|
+
import { collectBytes, readBytes } from "../../contracts/index.js";
|
|
4
7
|
import { yieldTurn } from "../../contracts/yield.js";
|
|
5
8
|
import { codec, CodecReader } from "../bytes/compression/codec.js";
|
|
6
9
|
import { boundedCodec } from "../bytes/compression/bounded-codec.js";
|
|
7
10
|
import { fail, text } from "./internal.js";
|
|
11
|
+
import { zipLzma } from "./zip/lzma.js";
|
|
8
12
|
const encoder = new TextEncoder();
|
|
9
13
|
const cp437 = "ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒáíóúñѪº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■ ";
|
|
10
14
|
export function crc32(bytes, previous = 0) {
|
|
@@ -19,7 +23,7 @@ function number(value, maximum, label) {
|
|
|
19
23
|
}
|
|
20
24
|
function admit(limits, signal) {
|
|
21
25
|
signal.throwIfAborted();
|
|
22
|
-
for (const key of ["maxArchiveBytes", "maxEntryBytes", "maxTotalBytes", "maxMembers", "maxPathBytes", "maxDepth", "maxPaxBytes", "maxTextBytes", "chunkSize"]) {
|
|
26
|
+
for (const key of ["maxArchiveBytes", "maxEntryBytes", "maxTotalBytes", "maxMembers", "maxPathBytes", "maxDepth", "maxPaxBytes", "maxTextBytes", "maxPatternSteps", "chunkSize"]) {
|
|
23
27
|
number(limits[key], Number.MAX_SAFE_INTEGER, key);
|
|
24
28
|
}
|
|
25
29
|
if (limits.chunkSize < 512 || limits.chunkSize > 1024 * 1024)
|
|
@@ -29,6 +33,7 @@ function admit(limits, signal) {
|
|
|
29
33
|
function pathBytes(name, limits) {
|
|
30
34
|
if (name.length > Math.min(limits.maxPathBytes, 65535))
|
|
31
35
|
fail("ZIP path byte limit exceeded");
|
|
36
|
+
number(Buffer.byteLength(name), Math.min(limits.maxPathBytes, limits.maxArchiveBytes, 65535), "path byte");
|
|
32
37
|
const bytes = encoder.encode(name);
|
|
33
38
|
if (!name || text(bytes) !== name || name.includes("\0"))
|
|
34
39
|
fail("ZIP invalid or unsafe path");
|
|
@@ -72,7 +77,16 @@ function extras(bytes, rawName, comment, central, limits) {
|
|
|
72
77
|
fail("ZIP64 invalid extra field length");
|
|
73
78
|
result.zip64 = bytes.subarray(offset, offset + length);
|
|
74
79
|
}
|
|
75
|
-
if (identifier === 0x9901
|
|
80
|
+
if (identifier === 0x9901) {
|
|
81
|
+
if (length !== 7 || bytes[offset + 2] !== 65 || bytes[offset + 3] !== 69)
|
|
82
|
+
fail("ZIP invalid AES extra field");
|
|
83
|
+
const aes = { version: view.getUint16(offset, true), strength: (bytes[offset + 4] + 1) * 64, method: view.getUint16(offset + 5, true) };
|
|
84
|
+
aesParameters(aes);
|
|
85
|
+
if (aes.method !== 0 && aes.method !== 8 && aes.method !== 12)
|
|
86
|
+
fail("ZIP unsupported AES compression method");
|
|
87
|
+
result.aes = aes;
|
|
88
|
+
}
|
|
89
|
+
if (identifier === 0x0017 || identifier === 0x0018 || identifier === 0x0019)
|
|
76
90
|
fail("ZIP encryption is unsupported");
|
|
77
91
|
if (identifier === 0x7075 || identifier === 0x6375) {
|
|
78
92
|
if (length < 5 || bytes[offset] !== 1)
|
|
@@ -114,13 +128,24 @@ function nameFrom(rawName, flags, metadata, limits) {
|
|
|
114
128
|
}
|
|
115
129
|
function format(method, flags, version) {
|
|
116
130
|
number(flags, 65535, "general purpose flags");
|
|
117
|
-
if (flags & (
|
|
131
|
+
if (flags & (64 | 0x2000))
|
|
118
132
|
fail("ZIP encryption is unsupported");
|
|
119
|
-
if (
|
|
133
|
+
if (method === 99) {
|
|
134
|
+
if (!(flags & 1) || flags & ~0x80f || version !== 51)
|
|
135
|
+
fail("ZIP invalid AES flags or extraction version");
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (method === 14 ? flags & ~0x80b : flags & ~0x80f || method !== 8 && flags & 6)
|
|
120
139
|
fail("ZIP unsupported general purpose flags");
|
|
140
|
+
if (method === 14) {
|
|
141
|
+
if (version !== 63)
|
|
142
|
+
fail("ZIP unsupported LZMA extraction version");
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
121
145
|
if (method !== 0 && method !== 8 && method !== 12)
|
|
122
146
|
fail("ZIP unsupported compression method");
|
|
123
|
-
|
|
147
|
+
// Info-ZIP uses version 10 for encrypted STORE, including descriptors.
|
|
148
|
+
if (version < (method === 12 ? 46 : method === 8 || flags & 8 && !(flags & 1) ? 20 : 10) || version > 20 && version !== 45 && version !== 46)
|
|
124
149
|
fail("ZIP unsupported extraction version (including ZIP64)");
|
|
125
150
|
}
|
|
126
151
|
function dosModified(date, time) {
|
|
@@ -137,13 +162,30 @@ function dosModified(date, time) {
|
|
|
137
162
|
fail("ZIP invalid DOS timestamp");
|
|
138
163
|
return new Date(year, month, day, hours, minutes, seconds);
|
|
139
164
|
}
|
|
165
|
+
function attributeMode(versionMadeBy, externalAttributes, directory) {
|
|
166
|
+
const host = versionMadeBy >>> 8;
|
|
167
|
+
const unixMode = host === 3 || host === 19 ? externalAttributes >>> 16 : 0;
|
|
168
|
+
if (unixMode)
|
|
169
|
+
return unixMode;
|
|
170
|
+
const mode = directory ? 0o040755 : 0o100644;
|
|
171
|
+
return externalAttributes & 1 ? mode & ~0o222 : mode;
|
|
172
|
+
}
|
|
140
173
|
function entryBounds(entry, limits) {
|
|
141
174
|
pathBytes(entry.name, limits);
|
|
142
|
-
number(entry.size, Math.min(limits.maxEntryBytes, limits.maxTotalBytes
|
|
143
|
-
number(entry.data.length,
|
|
175
|
+
number(entry.size, Math.min(limits.maxEntryBytes, limits.maxTotalBytes), "entry byte");
|
|
176
|
+
number(entry.data.length, limits.maxArchiveBytes, "compressed byte");
|
|
144
177
|
number(entry.crc32, 0xffffffff, "CRC32");
|
|
145
178
|
number(entry.mode, 0xffff, "mode");
|
|
146
|
-
format(entry.method, entry.flags ?? 0x800, entry.method === 12 ? 46 : entry.method === 8 || (entry.flags ?? 0) &
|
|
179
|
+
format(entry.method, entry.flags ?? 0x800, entry.method === 14 ? 63 : entry.method === 99 ? 51 : entry.method === 12 ? 46 : entry.method === 8 || (entry.flags ?? 0) & 9 ? 20 : 10);
|
|
180
|
+
if (entry.method === 99 && !entry.aes || entry.method !== 99 && entry.aes)
|
|
181
|
+
fail("ZIP inconsistent AES metadata");
|
|
182
|
+
if (entry.aes) {
|
|
183
|
+
aesParameters(entry.aes);
|
|
184
|
+
if (entry.aes.method !== 0 && entry.aes.method !== 8 && entry.aes.method !== 12)
|
|
185
|
+
fail("ZIP unsupported AES compression method");
|
|
186
|
+
if (entry.aes.version === 2 && entry.crc32 !== 0)
|
|
187
|
+
fail("ZIP AE-2 CRC must be zero");
|
|
188
|
+
}
|
|
147
189
|
if (entry.directory !== entry.name.endsWith("/") || entry.directory && (entry.symlink || entry.size !== 0))
|
|
148
190
|
fail("ZIP inconsistent directory metadata");
|
|
149
191
|
const type = entry.mode & 0o170000;
|
|
@@ -161,11 +203,12 @@ async function copyBytes(source, destination, start, chunkSize, signal) {
|
|
|
161
203
|
await yieldTurn(signal);
|
|
162
204
|
}
|
|
163
205
|
}
|
|
164
|
-
export async function readZipArchive(bytes, limits, signal) {
|
|
206
|
+
export async function readZipArchive(bytes, limits, signal, profile = {}) {
|
|
165
207
|
const chunkSize = admit(limits, signal);
|
|
166
|
-
number(bytes.length,
|
|
208
|
+
number(bytes.length, limits.maxArchiveBytes, "archive byte");
|
|
167
209
|
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
168
210
|
let end = -1;
|
|
211
|
+
let displacement = 0;
|
|
169
212
|
const lower = Math.max(0, bytes.length - 22 - 65535);
|
|
170
213
|
for (let offset = bytes.length - 22; offset >= lower; offset--) {
|
|
171
214
|
if ((bytes.length - offset) % 4096 === 0)
|
|
@@ -174,23 +217,58 @@ export async function readZipArchive(bytes, limits, signal) {
|
|
|
174
217
|
const size = view.getUint32(offset + 12, true);
|
|
175
218
|
const start = view.getUint32(offset + 16, true);
|
|
176
219
|
const zip64 = size === 0xffffffff || start === 0xffffffff || offset >= 20 && view.getUint32(offset - 20, true) === 0x07064b50;
|
|
177
|
-
|
|
178
|
-
|
|
220
|
+
let candidateDisplacement = 0;
|
|
221
|
+
if (!zip64 && !profile.disks && start + size !== offset) {
|
|
222
|
+
if (!profile.prefix || start + size > offset || offset - size < 0 || size && view.getUint32(offset - size, true) !== 0x02014b50)
|
|
223
|
+
continue;
|
|
224
|
+
candidateDisplacement = offset - size - start;
|
|
225
|
+
}
|
|
226
|
+
if (zip64 && profile.prefix && !profile.disks && offset >= 76 && view.getUint32(offset - 20, true) === 0x07064b50) {
|
|
227
|
+
const declared = view.getBigUint64(offset - 12, true);
|
|
228
|
+
if (declared > BigInt(Number.MAX_SAFE_INTEGER))
|
|
229
|
+
fail("ZIP64 unsafe numeric field");
|
|
230
|
+
if (Number(declared) + 12 > offset - 20 || view.getUint32(Number(declared), true) !== 0x06064b50) {
|
|
231
|
+
let found = -1;
|
|
232
|
+
const low = Math.max(0, offset - 76 - limits.maxPaxBytes);
|
|
233
|
+
for (let wide = offset - 76; wide >= low; wide--) {
|
|
234
|
+
if (offset - 76 - wide > limits.maxPatternSteps)
|
|
235
|
+
fail("ZIP SFX scan work limit exceeded");
|
|
236
|
+
if ((offset - wide) % 4096 === 0)
|
|
237
|
+
await yieldTurn(signal);
|
|
238
|
+
if (view.getUint32(wide, true) !== 0x06064b50 || view.getBigUint64(wide + 4, true) !== BigInt(offset - 32 - wide))
|
|
239
|
+
continue;
|
|
240
|
+
if (found !== -1)
|
|
241
|
+
fail("ZIP64 ambiguous SFX end records");
|
|
242
|
+
found = wide;
|
|
243
|
+
}
|
|
244
|
+
if (found < Number(declared))
|
|
245
|
+
continue;
|
|
246
|
+
candidateDisplacement = found - Number(declared);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
179
249
|
if (end !== -1)
|
|
180
250
|
fail("ZIP ambiguous end records");
|
|
181
251
|
end = offset;
|
|
252
|
+
displacement = candidateDisplacement;
|
|
182
253
|
}
|
|
183
254
|
}
|
|
184
255
|
if (end === -1)
|
|
185
256
|
fail("ZIP truncated or missing end of central directory");
|
|
186
|
-
const { members, centralStart, centralEnd } = zip64Directory(view, end, limits);
|
|
257
|
+
const { members, centralStart, centralEnd, diskMembers, disk: finalDisk, zip64Disk, zip64DiskMembers } = zip64Directory(view, end, limits, profile.disks, displacement);
|
|
187
258
|
number(bytes.length - end - 22, limits.maxTextBytes, "archive comment");
|
|
188
259
|
const entries = [];
|
|
189
260
|
const spans = [];
|
|
261
|
+
const growSpans = new Map();
|
|
190
262
|
let offset = centralStart;
|
|
191
263
|
let total = 0;
|
|
264
|
+
let finalMembers = 0;
|
|
265
|
+
let wideDiskMembers = 0;
|
|
192
266
|
for (let index = 0; index < members; index++) {
|
|
193
267
|
await yieldTurn(signal);
|
|
268
|
+
if (!profile.disks || offset >= profile.disks.starts[finalDisk])
|
|
269
|
+
finalMembers++;
|
|
270
|
+
if (zip64Disk >= 0 && (!profile.disks || offset >= profile.disks.starts[zip64Disk] && offset < profile.disks.starts[zip64Disk] + profile.disks.lengths[zip64Disk]))
|
|
271
|
+
wideDiskMembers++;
|
|
194
272
|
if (offset + 46 > centralEnd || view.getUint32(offset, true) !== 0x02014b50)
|
|
195
273
|
fail("ZIP truncated or invalid central header");
|
|
196
274
|
const versionMadeBy = view.getUint16(offset + 4, true);
|
|
@@ -215,6 +293,7 @@ export async function readZipArchive(bytes, limits, signal) {
|
|
|
215
293
|
const next = offset + 46 + nameLength + extraLength + commentLength;
|
|
216
294
|
if (next > centralEnd)
|
|
217
295
|
fail("ZIP truncated central metadata");
|
|
296
|
+
zipDiskRecord(profile.disks, offset, next);
|
|
218
297
|
number(commentLength, limits.maxTextBytes, "entry comment");
|
|
219
298
|
const rawName = bytes.subarray(offset + 46, offset + 46 + nameLength);
|
|
220
299
|
const centralExtra = bytes.subarray(offset + 46 + nameLength, next - commentLength);
|
|
@@ -222,13 +301,26 @@ export async function readZipArchive(bytes, limits, signal) {
|
|
|
222
301
|
if (flags & 0x800)
|
|
223
302
|
text(comment);
|
|
224
303
|
const centralMetadata = extras(centralExtra, rawName, comment, true, limits);
|
|
304
|
+
if (Boolean(centralMetadata.aes) !== (method === 99))
|
|
305
|
+
fail("ZIP inconsistent AES method and extra field");
|
|
306
|
+
const aes = centralMetadata.aes;
|
|
307
|
+
if (aes && aes.method !== 8 && flags & 6)
|
|
308
|
+
fail("ZIP unsupported AES compression flags");
|
|
309
|
+
if (aes?.version === 2 && checksum !== 0)
|
|
310
|
+
fail("ZIP AE-2 CRC must be zero");
|
|
311
|
+
if (version < 45 && (size === 0xffffffff || compressedSize === 0xffffffff || local === 0xffffffff || disk === 65535))
|
|
312
|
+
fail("ZIP64 unsupported member extraction version");
|
|
225
313
|
const resolved = zip64Fields(centralMetadata.zip64, [size, compressedSize, local, disk === 65535 ? 0xffffffff : disk]);
|
|
226
314
|
[size, compressedSize, local] = resolved;
|
|
227
|
-
|
|
228
|
-
|
|
315
|
+
local = zipDiskOffset(profile.disks, resolved[3], local);
|
|
316
|
+
local += displacement;
|
|
229
317
|
number(size, limits.maxEntryBytes, "entry byte");
|
|
230
318
|
number(compressedSize, limits.maxArchiveBytes, "compressed entry byte");
|
|
231
|
-
if (
|
|
319
|
+
if (flags & 1 && compressedSize < (aes ? aesParameters(aes).saltBytes + 12 : 12))
|
|
320
|
+
fail("ZIP truncated encryption header");
|
|
321
|
+
if (aes?.method === 0 && compressedSize !== size + aesParameters(aes).saltBytes + 12)
|
|
322
|
+
fail("ZIP AES stored size mismatch");
|
|
323
|
+
if (method === 0 && compressedSize !== size + (flags & 1 ? 12 : 0))
|
|
232
324
|
fail("ZIP stored size mismatch");
|
|
233
325
|
total += size;
|
|
234
326
|
number(total, limits.maxTotalBytes, "total byte");
|
|
@@ -240,6 +332,7 @@ export async function readZipArchive(bytes, limits, signal) {
|
|
|
240
332
|
const localNameLength = view.getUint16(local + 26, true);
|
|
241
333
|
const localExtraLength = view.getUint16(local + 28, true);
|
|
242
334
|
const payloadStart = local + 30 + localNameLength + localExtraLength;
|
|
335
|
+
zipDiskRecord(profile.disks, local, payloadStart);
|
|
243
336
|
let payloadEnd = payloadStart + compressedSize;
|
|
244
337
|
if (payloadEnd > centralStart)
|
|
245
338
|
fail("ZIP truncated or overlapping local payload");
|
|
@@ -248,6 +341,8 @@ export async function readZipArchive(bytes, limits, signal) {
|
|
|
248
341
|
fail("ZIP central/local filename mismatch");
|
|
249
342
|
const localExtra = bytes.subarray(local + 30 + localNameLength, payloadStart);
|
|
250
343
|
const localMetadata = extras(localExtra, localName, comment, false, limits);
|
|
344
|
+
if (aes ? !localMetadata.aes || aes.version !== localMetadata.aes.version || aes.strength !== localMetadata.aes.strength || aes.method !== localMetadata.aes.method : localMetadata.aes !== undefined)
|
|
345
|
+
fail("ZIP central/local AES metadata mismatch");
|
|
251
346
|
const localEffective = nameFrom(localName, flags, localMetadata, limits);
|
|
252
347
|
if (localMetadata.name !== undefined && localEffective !== name)
|
|
253
348
|
fail("ZIP central/local Unicode name mismatch");
|
|
@@ -255,6 +350,8 @@ export async function readZipArchive(bytes, limits, signal) {
|
|
|
255
350
|
fail("ZIP central/local timestamp mismatch");
|
|
256
351
|
const rawLocalSize = view.getUint32(local + 22, true);
|
|
257
352
|
const rawLocalCompressed = view.getUint32(local + 18, true);
|
|
353
|
+
if (version < 45 && (rawLocalSize === 0xffffffff || rawLocalCompressed === 0xffffffff))
|
|
354
|
+
fail("ZIP64 unsupported member extraction version");
|
|
258
355
|
const [localSize, localCompressed] = zip64Fields(localMetadata.zip64, [rawLocalSize, rawLocalCompressed]);
|
|
259
356
|
for (const [actual, expected] of [[view.getUint32(local + 14, true), checksum], [localCompressed, compressedSize], [localSize, size]]) {
|
|
260
357
|
if (actual !== expected && (!(flags & 8) || actual !== 0))
|
|
@@ -275,29 +372,41 @@ export async function readZipArchive(bytes, limits, signal) {
|
|
|
275
372
|
}
|
|
276
373
|
if (matches.length !== 1)
|
|
277
374
|
fail("ZIP truncated, ambiguous or mismatched data descriptor");
|
|
375
|
+
zipDiskRecord(profile.disks, payloadEnd, matches[0]);
|
|
278
376
|
payloadEnd = matches[0];
|
|
279
377
|
}
|
|
280
|
-
const host = versionMadeBy >>> 8;
|
|
281
|
-
const unixMode = host === 3 || host === 19 ? externalAttributes >>> 16 : 0;
|
|
282
378
|
const directory = name.endsWith("/");
|
|
283
379
|
if (Boolean(externalAttributes & 16) && !directory)
|
|
284
380
|
fail("ZIP conflicting directory attributes");
|
|
285
|
-
const mode =
|
|
381
|
+
const mode = attributeMode(versionMadeBy, externalAttributes, directory);
|
|
286
382
|
const symlink = (mode & 0o170000) === 0o120000;
|
|
287
383
|
const modified = dosModified(dosDate, dosTime);
|
|
288
384
|
const timestamp = centralMetadata.modified ?? localMetadata.modified;
|
|
289
385
|
if (timestamp !== undefined)
|
|
290
386
|
modified.setTime(timestamp);
|
|
291
387
|
const entry = { name, data: bytes.subarray(payloadStart, payloadStart + compressedSize), size, method, crc32: checksum, modified, mode, directory, symlink, rawName, localName, comment, localExtra, centralExtra, flags, versionMadeBy, internalAttributes, externalAttributes, dosTime, dosDate };
|
|
388
|
+
if (aes)
|
|
389
|
+
entry.aes = aes;
|
|
292
390
|
entryBounds(entry, limits);
|
|
391
|
+
if (profile.grow) {
|
|
392
|
+
if (centralMetadata.zip64 || localMetadata.zip64)
|
|
393
|
+
entry.zip64 = true;
|
|
394
|
+
growSpans.set(entry, { start: local, end: payloadEnd });
|
|
395
|
+
}
|
|
293
396
|
entries.push(entry);
|
|
294
397
|
spans.push({ start: local, end: payloadEnd });
|
|
295
398
|
offset = next;
|
|
296
399
|
}
|
|
297
400
|
if (offset !== centralEnd)
|
|
298
401
|
fail("ZIP central directory size or member count mismatch");
|
|
402
|
+
if (diskMembers !== 65535 && diskMembers !== finalMembers)
|
|
403
|
+
fail("ZIP inconsistent per-disk member count");
|
|
404
|
+
if (zip64DiskMembers >= 0 && zip64DiskMembers !== wideDiskMembers)
|
|
405
|
+
fail("ZIP64 inconsistent per-disk member count");
|
|
299
406
|
spans.sort((first, second) => first.start - second.start);
|
|
300
|
-
|
|
407
|
+
if (profile.disks && view.getUint32(0, true) !== 0x08074b50)
|
|
408
|
+
fail("ZIP missing split signature");
|
|
409
|
+
let covered = profile.disks ? 4 : profile.prefix ? spans[0]?.start ?? centralStart : 0;
|
|
301
410
|
for (const span of spans) {
|
|
302
411
|
if (span.start !== covered)
|
|
303
412
|
fail("ZIP overlapping spans, gaps or self-extracting prefix are unsupported");
|
|
@@ -306,6 +415,12 @@ export async function readZipArchive(bytes, limits, signal) {
|
|
|
306
415
|
if (covered !== centralStart)
|
|
307
416
|
fail("ZIP unreferenced local data is unsupported");
|
|
308
417
|
for (const entry of entries) {
|
|
418
|
+
const span = growSpans.get(entry);
|
|
419
|
+
if (span) {
|
|
420
|
+
const record = new Uint8Array(span.end - span.start);
|
|
421
|
+
await copyBytes(bytes.subarray(span.start, span.end), record, 0, chunkSize, signal);
|
|
422
|
+
zipGrowRecords.set(entry, record);
|
|
423
|
+
}
|
|
309
424
|
const data = new Uint8Array(entry.data.length);
|
|
310
425
|
await copyBytes(entry.data, data, 0, chunkSize, signal);
|
|
311
426
|
entry.data = data;
|
|
@@ -316,17 +431,50 @@ export async function readZipArchive(bytes, limits, signal) {
|
|
|
316
431
|
entry.centralExtra = new Uint8Array(entry.centralExtra);
|
|
317
432
|
await yieldTurn(signal);
|
|
318
433
|
}
|
|
319
|
-
return { entries, comment: new Uint8Array(bytes.subarray(end + 22)) };
|
|
434
|
+
return { entries, comment: new Uint8Array(bytes.subarray(end + 22)), ...(profile.prefix ? { prefix: new Uint8Array(bytes.subarray(0, spans[0]?.start ?? centralStart)) } : {}) };
|
|
435
|
+
}
|
|
436
|
+
export async function* decodeZipEntry(entry, limits, signal, password) {
|
|
437
|
+
if (!entry.aes) {
|
|
438
|
+
yield* decodeZipContent(entry, limits, signal, password);
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
admit(limits, signal);
|
|
442
|
+
entryBounds(entry, limits);
|
|
443
|
+
if (password === undefined)
|
|
444
|
+
fail("ZIP password required");
|
|
445
|
+
// Budget ciphertext's authentication copy, compressed plaintext and verified
|
|
446
|
+
// decompressed output together. The archive input is separately bounded.
|
|
447
|
+
const available = limits.maxBufferedFileBytes - entry.data.length * 2;
|
|
448
|
+
if (available < entry.size * 2)
|
|
449
|
+
fail("ZIP AES authenticated staging limit exceeded");
|
|
450
|
+
const plaintext = await decryptAesPayload(entry.data, password, entry.aes, available, signal);
|
|
451
|
+
let verified;
|
|
452
|
+
try {
|
|
453
|
+
verified = await collectBytes(decodeZipContent(entry, limits, signal, password, plaintext), { maxBytes: Math.floor((available - plaintext.length) / 2), signal });
|
|
454
|
+
yield* wireChunks(verified, Math.min(limits.chunkSize, 65536), signal);
|
|
455
|
+
}
|
|
456
|
+
finally {
|
|
457
|
+
plaintext.fill(0);
|
|
458
|
+
verified?.fill(0);
|
|
459
|
+
}
|
|
320
460
|
}
|
|
321
|
-
|
|
461
|
+
async function* decodeZipContent(entry, limits, signal, password, authenticated) {
|
|
322
462
|
const chunkSize = admit(limits, signal);
|
|
323
463
|
entryBounds(entry, limits);
|
|
324
|
-
|
|
464
|
+
if (entry.flags & 1 && password === undefined)
|
|
465
|
+
fail("ZIP password required");
|
|
466
|
+
const input = (async function* () { yield authenticated ?? entry.data; })();
|
|
467
|
+
const decrypted = authenticated === undefined && entry.flags & 1 ? decryptZipPayload(input, password, entry.flags & 8 ? (entry.dosTime ?? 0) >>> 8 : entry.crc32 >>> 24, signal) : input;
|
|
468
|
+
const reader = new CodecReader(decrypted, signal);
|
|
325
469
|
let length = 0;
|
|
326
470
|
let checksum = 0;
|
|
327
|
-
const
|
|
328
|
-
|
|
329
|
-
|
|
471
|
+
const method = entry.aes?.method ?? entry.method;
|
|
472
|
+
const source = method === 14 ? zipLzma(reader, signal, { decode: true, level: 1, eos: Boolean(entry.flags & 2), size: entry.size }) : method === 12 ? boundedCodec(reader, { format: "bzip2", decompress: true, level: 9, singleMember: true }, signal) : method === 8 ? codec(reader, { mode: "inflate-raw", chunkSize }, signal) : (async function* () {
|
|
473
|
+
for (;;) {
|
|
474
|
+
const chunk = await reader.chunk();
|
|
475
|
+
if (chunk === undefined)
|
|
476
|
+
break;
|
|
477
|
+
yield new Uint8Array(chunk);
|
|
330
478
|
await yieldTurn(signal);
|
|
331
479
|
}
|
|
332
480
|
})();
|
|
@@ -334,15 +482,15 @@ export async function* decodeZipEntry(entry, limits, signal) {
|
|
|
334
482
|
for await (const chunk of source) {
|
|
335
483
|
signal.throwIfAborted();
|
|
336
484
|
length += chunk.length;
|
|
337
|
-
number(length, Math.min(limits.maxEntryBytes, limits.maxTotalBytes), "decoded byte");
|
|
485
|
+
number(length, Math.min(limits.maxEntryBytes, limits.maxTotalBytes, method === 14 ? entry.size : Number.MAX_SAFE_INTEGER), "decoded byte");
|
|
338
486
|
checksum = crc32(chunk, checksum);
|
|
339
487
|
yield chunk;
|
|
340
488
|
}
|
|
341
|
-
if (
|
|
489
|
+
if (method !== 0 && await reader.chunk() !== undefined)
|
|
342
490
|
fail("ZIP trailing compressed data");
|
|
343
491
|
if (length !== entry.size)
|
|
344
492
|
fail("ZIP uncompressed size mismatch");
|
|
345
|
-
if (checksum !== entry.crc32)
|
|
493
|
+
if (entry.aes?.version !== 2 && checksum !== entry.crc32)
|
|
346
494
|
fail("ZIP CRC32 mismatch");
|
|
347
495
|
}
|
|
348
496
|
finally {
|
|
@@ -375,7 +523,7 @@ export async function makeZipEntry(name, bytes, attributes, limits, signal, leve
|
|
|
375
523
|
if (level !== 0 && (bytes.length || forceCompression) && !entry.directory && !entry.symlink) {
|
|
376
524
|
const chunks = [];
|
|
377
525
|
let length = 0;
|
|
378
|
-
const source = method === "bzip2" ? boundedCodec(reader, { format: "bzip2", decompress: false, level }, signal) : codec(reader, { mode: "deflate-raw", chunkSize, level }, signal);
|
|
526
|
+
const source = method === "lzma" ? zipLzma(reader, signal, { decode: false, level, eos: true, size: bytes.length }) : method === "bzip2" ? boundedCodec(reader, { format: "bzip2", decompress: false, level }, signal) : codec(reader, { mode: "deflate-raw", chunkSize, level }, signal);
|
|
379
527
|
for await (const chunk of source) {
|
|
380
528
|
length += chunk.length;
|
|
381
529
|
if (length > limits.maxArchiveBytes)
|
|
@@ -392,7 +540,9 @@ export async function makeZipEntry(name, bytes, attributes, limits, signal, leve
|
|
|
392
540
|
offset += chunk.length;
|
|
393
541
|
await yieldTurn(signal);
|
|
394
542
|
}
|
|
395
|
-
entry.method = method === "bzip2" ? 12 : 8;
|
|
543
|
+
entry.method = method === "lzma" ? 14 : method === "bzip2" ? 12 : 8;
|
|
544
|
+
if (entry.method === 14)
|
|
545
|
+
entry.flags = 0x802;
|
|
396
546
|
return entry;
|
|
397
547
|
}
|
|
398
548
|
}
|
|
@@ -429,7 +579,7 @@ export function updateZipExtras(entry, previous, mode, limits) {
|
|
|
429
579
|
const identifier = view.getUint16(offset, true);
|
|
430
580
|
const next = offset + 4 + view.getUint16(offset + 2, true);
|
|
431
581
|
// Sizes, timestamps and Unicode names/comments are rebuilt for the new entry.
|
|
432
|
-
if (identifier !== 1 && identifier !== 0x5455 && identifier !== 0x7075 && identifier !== 0x6375) {
|
|
582
|
+
if (identifier !== 1 && identifier !== 0x9901 && identifier !== 0x5455 && identifier !== 0x7075 && identifier !== 0x6375) {
|
|
433
583
|
fields.push(original.subarray(offset, next));
|
|
434
584
|
length += next - offset;
|
|
435
585
|
}
|
|
@@ -492,38 +642,166 @@ export function setZipEntryComment(entry, comment, limits) {
|
|
|
492
642
|
}
|
|
493
643
|
return { ...entry, comment: new Uint8Array(comment), flags, localExtra: metadata[0], centralExtra: metadata[1] };
|
|
494
644
|
}
|
|
495
|
-
export async function* streamZipArchive(archive, limits, signal, descriptors = false, forceZip64 = false) {
|
|
645
|
+
export async function* streamZipArchive(archive, limits, signal, descriptors = false, forceZip64 = false, allowZip64 = true) {
|
|
496
646
|
const chunkSize = admit(limits, signal);
|
|
647
|
+
if (archive.entries.some(entry => entry.encryption?.aes && !entry.directory)) {
|
|
648
|
+
number(archive.entries.length, limits.maxMembers, "member");
|
|
649
|
+
if (archive.entries.length > limits.maxPatternSteps)
|
|
650
|
+
fail("ZIP archive work limit exceeded");
|
|
651
|
+
const entries = [];
|
|
652
|
+
let total = 0;
|
|
653
|
+
let stagedBytes = 0;
|
|
654
|
+
let retained = archive.entries.reduce((sum, entry) => sum + entry.data.length, 0);
|
|
655
|
+
number(retained, limits.maxArchiveBytes, "compressed byte");
|
|
656
|
+
for (const original of archive.entries) {
|
|
657
|
+
await yieldTurn(signal);
|
|
658
|
+
total += original.source ? 0 : original.size;
|
|
659
|
+
number(total, limits.maxTotalBytes, "total byte");
|
|
660
|
+
if (!original.encryption?.aes || original.directory) {
|
|
661
|
+
entries.push(original);
|
|
662
|
+
continue;
|
|
663
|
+
}
|
|
664
|
+
entryBounds(original, limits);
|
|
665
|
+
const aes = { ...original.encryption.aes };
|
|
666
|
+
aesParameters(aes);
|
|
667
|
+
// Conservative allowance for overlapping owned input, codec result,
|
|
668
|
+
// encryption input/output and wire assembly. Codec workspace is separate.
|
|
669
|
+
const maximum = Math.floor((limits.maxBufferedFileBytes - 28) / 6);
|
|
670
|
+
if (maximum < 0)
|
|
671
|
+
fail("ZIP AES authenticated staging limit exceeded");
|
|
672
|
+
let entry = original;
|
|
673
|
+
if (original.source) {
|
|
674
|
+
const plaintext = await collectBytes(original.source, { maxBytes: Math.min(maximum, limits.maxEntryBytes, limits.maxTotalBytes - total), signal });
|
|
675
|
+
total += plaintext.length;
|
|
676
|
+
try {
|
|
677
|
+
if (original.expectedSize !== undefined && plaintext.length !== original.expectedSize)
|
|
678
|
+
fail("ZIP live input size mismatch");
|
|
679
|
+
const made = await makeZipEntry(original.name, plaintext, { modified: original.modified, mode: original.mode, directory: original.directory, symlink: original.symlink }, limits, signal, original.method === 0 ? 0 : original.level ?? 6, original.method !== 0, original.method === 14 ? "lzma" : original.method === 12 ? "bzip2" : "deflate");
|
|
680
|
+
entry = { ...original, ...made };
|
|
681
|
+
delete entry.source;
|
|
682
|
+
delete entry.expectedSize;
|
|
683
|
+
}
|
|
684
|
+
finally {
|
|
685
|
+
plaintext.fill(0);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
if (entry.data.length > maximum || entry.size > maximum)
|
|
689
|
+
fail("ZIP AES authenticated staging limit exceeded");
|
|
690
|
+
const overhead = aesParameters(aes).saltBytes + 12;
|
|
691
|
+
retained += entry.data.length + overhead - original.data.length;
|
|
692
|
+
number(retained, limits.maxArchiveBytes, "encrypted byte");
|
|
693
|
+
const data = await encryptAesPayload((async function* () { yield entry.data; })(), { ...original.encryption, aes }, maximum, signal);
|
|
694
|
+
stagedBytes += data.length;
|
|
695
|
+
if (original.source) {
|
|
696
|
+
original.size = entry.size;
|
|
697
|
+
original.compressedSize = data.length;
|
|
698
|
+
original.crc32 = entry.crc32;
|
|
699
|
+
if (entry.internalAttributes !== undefined)
|
|
700
|
+
original.internalAttributes = entry.internalAttributes;
|
|
701
|
+
}
|
|
702
|
+
const extra = aesExtra(aes, entry.method);
|
|
703
|
+
const append = (bytes) => {
|
|
704
|
+
const joined = new Uint8Array(bytes.length + extra.length);
|
|
705
|
+
joined.set(bytes);
|
|
706
|
+
joined.set(extra, bytes.length);
|
|
707
|
+
return joined;
|
|
708
|
+
};
|
|
709
|
+
const encrypted = { ...entry, data, aes: { ...aes, method: entry.method }, method: 99, crc32: aes.version === 2 ? 0 : entry.crc32,
|
|
710
|
+
flags: (((entry.flags ?? 0x800) | 1) & ~6) | (descriptors || original.descriptors ? 8 : 0),
|
|
711
|
+
localExtra: append(entry.localExtra ?? timestampExtra(entry.modified)), centralExtra: append(entry.centralExtra ?? timestampExtra(entry.modified)) };
|
|
712
|
+
delete encrypted.encryption;
|
|
713
|
+
entries.push(encrypted);
|
|
714
|
+
}
|
|
715
|
+
yield* streamZipArchive({ ...archive, entries, ...(archive.onRetention ? { onRetention: (counters) => archive.onRetention({ ...counters, payloadBytes: counters.payloadBytes + stagedBytes }) } : {}) }, limits, signal, descriptors, forceZip64, allowZip64);
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
if (archive.entries.some(entry => entry.encryption && !entry.source && !entry.directory)) {
|
|
719
|
+
const entries = [];
|
|
720
|
+
for (const original of archive.entries) {
|
|
721
|
+
if (!original.encryption || original.source || original.directory) {
|
|
722
|
+
entries.push(original);
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
725
|
+
entryBounds(original, limits);
|
|
726
|
+
number(original.data.length + 12, limits.maxArchiveBytes, "encrypted byte");
|
|
727
|
+
const rounded = new Date(Math.ceil(Math.floor(original.modified.getTime() / 1000) / 2) * 2000);
|
|
728
|
+
const time = original.dosTime ?? ((rounded.getHours() << 11) | (rounded.getMinutes() << 5) | (rounded.getSeconds() >>> 1));
|
|
729
|
+
const descriptor = descriptors || original.descriptors === true;
|
|
730
|
+
const data = await collectBytes(encryptZipPayload((async function* () { yield original.data; })(), original.encryption, descriptor ? time >>> 8 : original.crc32 >>> 24, signal), { maxBytes: limits.maxArchiveBytes, signal });
|
|
731
|
+
const entry = { ...original, data, dosTime: time, flags: (original.flags ?? 0x800) | 1 | (descriptor ? 8 : 0) };
|
|
732
|
+
delete entry.encryption;
|
|
733
|
+
entries.push(entry);
|
|
734
|
+
}
|
|
735
|
+
yield* streamZipArchive({ ...archive, entries }, limits, signal, descriptors, forceZip64, allowZip64);
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
497
738
|
number(archive.entries.length, limits.maxMembers, "member");
|
|
498
739
|
number(archive.comment.length, Math.min(limits.maxTextBytes, 65535), "archive comment");
|
|
499
|
-
const
|
|
740
|
+
const wideProfile = (entry) => {
|
|
741
|
+
const required = Math.max(entry.size, entry.data.length, entry.expectedSize ?? 0) >= 0xffffffff;
|
|
742
|
+
const unknown = entry.source !== undefined && entry.zip64 !== false && allowZip64 &&
|
|
743
|
+
(entry.expectedSize === undefined && limits.maxEntryBytes >= 0xffffffff || entry.method !== 0 && limits.maxArchiveBytes >= 0xffffffff);
|
|
744
|
+
return forceZip64 || entry.zip64 === true || required || unknown;
|
|
745
|
+
};
|
|
746
|
+
let wideArchive = archive.entries.length >= 65535 || forceZip64 || archive.entries.some(wideProfile);
|
|
747
|
+
if (wideArchive && !allowZip64)
|
|
748
|
+
fail("ZIP64 required but disabled");
|
|
500
749
|
let length = 22 + archive.comment.length + (wideArchive ? 76 : 0);
|
|
750
|
+
number(length, limits.maxArchiveBytes, "archive byte");
|
|
501
751
|
let localLength = 0;
|
|
502
752
|
let total = 0;
|
|
753
|
+
let metadataBytes = 0;
|
|
754
|
+
let work = 0;
|
|
503
755
|
const encoded = [];
|
|
504
756
|
for (const entry of archive.entries) {
|
|
757
|
+
if (++work > limits.maxPatternSteps)
|
|
758
|
+
fail("ZIP archive work limit exceeded");
|
|
505
759
|
await yieldTurn(signal);
|
|
506
760
|
entryBounds(entry, limits);
|
|
507
|
-
if (entry.method === 0 && entry.data.length !== entry.size)
|
|
761
|
+
if (!entry.source && entry.method === 0 && entry.data.length !== entry.size + (entry.flags & 1 ? 12 : 0))
|
|
508
762
|
fail("ZIP stored size mismatch");
|
|
509
|
-
|
|
763
|
+
if (entry.source && (entry.data.length || entry.directory || entry.symlink))
|
|
764
|
+
fail("ZIP invalid live source profile");
|
|
765
|
+
if (entry.expectedSize !== undefined)
|
|
766
|
+
number(entry.expectedSize, Math.min(limits.maxEntryBytes, limits.maxTotalBytes), "expected input byte");
|
|
767
|
+
if (entry.source)
|
|
768
|
+
number(entry.level ?? 6, 9, "compression level");
|
|
769
|
+
total += entry.source ? 0 : entry.size;
|
|
510
770
|
number(total, limits.maxTotalBytes, "total byte");
|
|
511
771
|
const rawName = entry.rawName ?? pathBytes(entry.name, limits);
|
|
512
|
-
|
|
513
|
-
const
|
|
772
|
+
// Retained ciphertext binds its verifier to the original descriptor flag.
|
|
773
|
+
const descriptor = entry.flags & 1 || zipGrowRecords.has(entry) ? Boolean(entry.flags & 8) : (entry.source !== undefined || descriptors || entry.descriptors === true) && !entry.directory;
|
|
774
|
+
const flags = ((entry.flags ?? (entry.method === 14 ? 0x802 : 0x800)) & ~8) | (descriptor ? 8 : 0) | (entry.encryption && !entry.directory ? 1 : 0) | (entry.source && entry.method === 14 ? 2 : 0);
|
|
514
775
|
const comment = entry.comment ?? new Uint8Array();
|
|
515
776
|
number(comment.length, Math.min(limits.maxTextBytes, 65535), "entry comment");
|
|
516
777
|
if (flags & 0x800)
|
|
517
778
|
text(comment);
|
|
518
779
|
const originalLocalExtra = entry.localExtra ?? timestampExtra(entry.modified);
|
|
519
|
-
extras(originalLocalExtra, rawName, comment, false, limits);
|
|
520
|
-
const wide =
|
|
521
|
-
|
|
780
|
+
const originalLocalMetadata = extras(originalLocalExtra, rawName, comment, false, limits);
|
|
781
|
+
const wide = wideProfile(entry);
|
|
782
|
+
if (wide && (!allowZip64 || !forceZip64 && entry.zip64 === false))
|
|
783
|
+
fail("ZIP64 required but disabled");
|
|
784
|
+
const member = zip64Member(entry.size, entry.data.length, localLength, forceZip64 || entry.zip64 === true, allowZip64 && (forceZip64 || entry.zip64 !== false), wide && descriptor);
|
|
522
785
|
const originalCentralExtra = entry.centralExtra ?? timestampExtra(entry.modified);
|
|
523
|
-
extras(originalCentralExtra, rawName, comment, true, limits);
|
|
524
|
-
const
|
|
786
|
+
const originalCentralMetadata = extras(originalCentralExtra, rawName, comment, true, limits);
|
|
787
|
+
const record = forceZip64 ? undefined : zipGrowRecords.get(entry);
|
|
788
|
+
const localValues = descriptor && wide
|
|
789
|
+
? (entry.method === 12 || entry.method === 14) && !entry.source ? [entry.size, entry.data.length] : [0, 0]
|
|
790
|
+
: [entry.size, entry.data.length].filter(value => forceZip64 || entry.zip64 === true || value >= 0xffffffff);
|
|
791
|
+
const localExtraSize = record ? originalLocalExtra.length : originalLocalExtra.length - (originalLocalMetadata.zip64 ? originalLocalMetadata.zip64.length + 4 : 0) + (localValues.length ? 4 + localValues.length * 8 : 0);
|
|
792
|
+
const centralExtraSize = originalCentralExtra.length - (originalCentralMetadata.zip64 ? originalCentralMetadata.zip64.length + 4 : 0) + (member.values.length ? 4 + member.values.length * 8 : 0);
|
|
793
|
+
number(localExtraSize, Math.min(limits.maxPaxBytes, 65535), "extra field");
|
|
794
|
+
number(centralExtraSize, Math.min(limits.maxPaxBytes, 65535), "extra field");
|
|
795
|
+
const retainedSize = record
|
|
796
|
+
? record.length + 46 + rawName.length + centralExtraSize + comment.length
|
|
797
|
+
: 76 + 2 * rawName.length + localExtraSize + centralExtraSize + comment.length + entry.data.length + (descriptor ? wide ? 24 : 16 : 0);
|
|
798
|
+
number(length + retainedSize, limits.maxArchiveBytes, "archive byte");
|
|
799
|
+
const localExtra = record ? originalLocalExtra : localValues.length ? zip64Extra(localValues, stripZip64(originalLocalExtra)) : stripZip64(originalLocalExtra);
|
|
800
|
+
const centralExtra = member.values.length ? zip64Extra(member.values, stripZip64(originalCentralExtra)) : stripZip64(originalCentralExtra);
|
|
525
801
|
const localMetadata = extras(localExtra, rawName, comment, false, limits);
|
|
526
802
|
const centralMetadata = extras(centralExtra, rawName, comment, true, limits);
|
|
803
|
+
if (entry.aes ? !localMetadata.aes || !centralMetadata.aes || [localMetadata.aes, centralMetadata.aes].some(aes => aes.version !== entry.aes.version || aes.strength !== entry.aes.strength || aes.method !== entry.aes.method) : localMetadata.aes || centralMetadata.aes)
|
|
804
|
+
fail("ZIP retained AES metadata mismatch");
|
|
527
805
|
if (nameFrom(rawName, flags, centralMetadata, limits) !== entry.name || localMetadata.name !== undefined && nameFrom(rawName, flags, localMetadata, limits) !== entry.name || entry.localName && !equal(rawName, entry.localName))
|
|
528
806
|
fail("ZIP retained filename metadata mismatch");
|
|
529
807
|
if (localMetadata.modified !== undefined && centralMetadata.modified !== undefined && localMetadata.modified !== centralMetadata.modified)
|
|
@@ -534,71 +812,198 @@ export async function* streamZipArchive(archive, limits, signal, descriptors = f
|
|
|
534
812
|
const time = entry.dosTime ?? ((rounded.getHours() << 11) | (rounded.getMinutes() << 5) | (rounded.getSeconds() >>> 1));
|
|
535
813
|
number(date, 65535, "DOS date");
|
|
536
814
|
number(time, 65535, "DOS time");
|
|
537
|
-
const dosTimestamp = dosModified(date, time)
|
|
815
|
+
const dosTimestamp = dosModified(date, time);
|
|
538
816
|
const extendedTimestamp = centralMetadata.modified ?? localMetadata.modified;
|
|
539
|
-
|
|
817
|
+
// DOS stores local calendar fields, so it cannot distinguish the two
|
|
818
|
+
// occurrences of a repeated DST hour. UT still binds the absolute instant.
|
|
819
|
+
const timestampMatches = extendedTimestamp === undefined
|
|
820
|
+
? ["getFullYear", "getMonth", "getDate", "getHours", "getMinutes", "getSeconds"]
|
|
821
|
+
.every(field => dosTimestamp[field]() === rounded[field]())
|
|
822
|
+
: Math.floor(extendedTimestamp / 1000) === Math.floor(entry.modified.getTime() / 1000);
|
|
823
|
+
if (!timestampMatches)
|
|
540
824
|
fail("ZIP retained or unrepresentable timestamp metadata mismatch");
|
|
541
825
|
for (const [value, maximum, label] of [[entry.versionMadeBy ?? 0x31e, 65535, "creator version"], [entry.internalAttributes ?? 0, 65535, "internal attributes"], [entry.externalAttributes ?? 0, 0xffffffff, "external attributes"]])
|
|
542
826
|
number(value, maximum, label);
|
|
543
827
|
if ((entry.internalAttributes ?? 0) & ~1)
|
|
544
828
|
fail("ZIP unsupported internal attributes");
|
|
545
829
|
if (entry.externalAttributes !== undefined) {
|
|
546
|
-
const
|
|
547
|
-
const unixMode = host === 3 || host === 19 ? entry.externalAttributes >>> 16 : 0;
|
|
548
|
-
const mode = unixMode || (entry.directory ? 0o040755 : 0o100644);
|
|
830
|
+
const mode = attributeMode(entry.versionMadeBy ?? 0x31e, entry.externalAttributes, entry.directory);
|
|
549
831
|
if (mode !== entry.mode || Boolean(entry.externalAttributes & 16) && !entry.directory)
|
|
550
832
|
fail("ZIP retained file attributes mismatch");
|
|
551
833
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
834
|
+
metadataBytes += 76 + 2 * rawName.length + localExtra.length + centralExtra.length + comment.length;
|
|
835
|
+
number(metadataBytes, limits.maxArchiveBytes, "metadata byte");
|
|
836
|
+
const localSize = record?.length ?? 30 + rawName.length + localExtra.length + entry.data.length + (descriptor ? wide ? 24 : 16 : 0);
|
|
837
|
+
length += localSize + 46 + rawName.length + centralExtra.length + comment.length;
|
|
838
|
+
number(length, limits.maxArchiveBytes, "archive byte");
|
|
839
|
+
const metadata = { ...entry, data: new Uint8Array() };
|
|
840
|
+
delete metadata.source;
|
|
841
|
+
delete metadata.encryption;
|
|
842
|
+
const item = { entry: metadata, rawName, wide, flags, localExtra, centralExtra, comment, date, time, offset: localLength, compressedSize: entry.data.length };
|
|
843
|
+
encoded.push(item);
|
|
844
|
+
archive.onRetention?.({ metadataBytes, payloadBytes: 0 });
|
|
845
|
+
localLength += localSize;
|
|
846
|
+
}
|
|
847
|
+
const plannedCentralSize = length - (22 + archive.comment.length + (wideArchive ? 76 : 0)) - localLength;
|
|
848
|
+
if (!wideArchive && (localLength >= 0xffffffff || plannedCentralSize >= 0xffffffff)) {
|
|
849
|
+
if (!allowZip64)
|
|
850
|
+
fail("ZIP64 required but disabled");
|
|
851
|
+
number(length + 76, limits.maxArchiveBytes, "archive byte");
|
|
852
|
+
length += 76;
|
|
853
|
+
wideArchive = true;
|
|
556
854
|
}
|
|
557
|
-
|
|
558
|
-
|
|
855
|
+
const refreshCentral = (item, size, compressed, offset) => {
|
|
856
|
+
const member = zip64Member(size, compressed, offset, forceZip64 || item.entry.zip64 === true, allowZip64 && (forceZip64 || item.entry.zip64 !== false), item.wide && Boolean(item.flags & 8));
|
|
857
|
+
const retained = stripZip64(item.centralExtra);
|
|
858
|
+
const extraSize = retained.length + (member.values.length ? 4 + member.values.length * 8 : 0);
|
|
859
|
+
number(extraSize, Math.min(limits.maxPaxBytes, 65535), "extra field");
|
|
860
|
+
const delta = extraSize - item.centralExtra.length;
|
|
861
|
+
const overhead = !wideArchive && member.wide ? 76 : 0;
|
|
862
|
+
number(length + delta + overhead, limits.maxArchiveBytes, "archive byte");
|
|
863
|
+
length += delta + overhead;
|
|
864
|
+
metadataBytes += delta;
|
|
865
|
+
wideArchive ||= member.wide;
|
|
866
|
+
item.centralExtra = member.values.length ? zip64Extra(member.values, retained) : retained;
|
|
867
|
+
};
|
|
868
|
+
localLength = 0;
|
|
869
|
+
for (const [index, item] of encoded.entries()) {
|
|
870
|
+
const entry = archive.entries[index];
|
|
871
|
+
const { rawName, wide, flags, localExtra, date, time } = item;
|
|
872
|
+
const descriptor = Boolean(flags & 8);
|
|
873
|
+
item.offset = localLength;
|
|
874
|
+
refreshCentral(item, entry.size, entry.data.length, localLength);
|
|
559
875
|
await yieldTurn(signal);
|
|
560
|
-
const
|
|
876
|
+
const record = forceZip64 ? undefined : zipGrowRecords.get(entry);
|
|
877
|
+
if (record) {
|
|
878
|
+
yield* wireChunks(record, chunkSize, signal);
|
|
879
|
+
localLength += record.length;
|
|
880
|
+
continue;
|
|
881
|
+
}
|
|
561
882
|
const offset = 0;
|
|
562
883
|
const bytes = new Uint8Array(30 + rawName.length + localExtra.length);
|
|
563
884
|
const view = new DataView(bytes.buffer);
|
|
564
|
-
const version = entry.method === 12 ? 46 : wide ? 45 : entry.method === 8 || flags &
|
|
885
|
+
const version = entry.method === 14 ? 63 : entry.method === 99 ? 51 : entry.method === 12 ? 46 : wide || item.offset >= 0xffffffff ? 45 : entry.method === 8 || flags & 9 ? 20 : 10;
|
|
565
886
|
view.setUint32(offset, 0x04034b50, true);
|
|
566
887
|
view.setUint16(offset + 4, version, true);
|
|
567
888
|
view.setUint16(offset + 6, flags, true);
|
|
568
889
|
view.setUint16(offset + 8, entry.method, true);
|
|
569
890
|
view.setUint16(offset + 10, time, true);
|
|
570
891
|
view.setUint16(offset + 12, date, true);
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
892
|
+
// Buffered BZIP2/LZMA have known compressed spans. Retain them with a
|
|
893
|
+
// descriptor so streaming native readers can locate the member boundary.
|
|
894
|
+
const knownCompressedSpan = (entry.method === 12 || entry.method === 14) && !entry.source;
|
|
895
|
+
view.setUint32(offset + 14, flags & 8 && !knownCompressedSpan ? 0 : entry.crc32, true);
|
|
896
|
+
view.setUint32(offset + 18, wide && (descriptor || forceZip64 || entry.zip64 === true || entry.data.length >= 0xffffffff) ? 0xffffffff : flags & 8 && !knownCompressedSpan ? 0 : entry.data.length, true);
|
|
897
|
+
view.setUint32(offset + 22, wide && (descriptor || forceZip64 || entry.zip64 === true || entry.size >= 0xffffffff) ? 0xffffffff : flags & 8 && !knownCompressedSpan ? 0 : entry.size, true);
|
|
574
898
|
view.setUint16(offset + 26, rawName.length, true);
|
|
575
899
|
view.setUint16(offset + 28, localExtra.length, true);
|
|
576
900
|
bytes.set(rawName, offset + 30);
|
|
577
901
|
bytes.set(localExtra, offset + 30 + rawName.length);
|
|
578
902
|
yield* wireChunks(bytes, chunkSize, signal);
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
903
|
+
if (entry.source) {
|
|
904
|
+
let size = 0;
|
|
905
|
+
let checksum = 0;
|
|
906
|
+
let textual = false;
|
|
907
|
+
let binary = false;
|
|
908
|
+
let previousSlab = 0;
|
|
909
|
+
const measured = (async function* () {
|
|
910
|
+
for await (const chunk of readBytes(entry.source, signal)) {
|
|
911
|
+
if (chunk.length === 0) {
|
|
912
|
+
if (++work > limits.maxPatternSteps)
|
|
913
|
+
fail("ZIP archive work limit exceeded");
|
|
914
|
+
await yieldTurn(signal);
|
|
915
|
+
continue;
|
|
916
|
+
}
|
|
917
|
+
number(size + chunk.length, Math.min(limits.maxEntryBytes, limits.maxTotalBytes - total, entry.expectedSize ?? Number.MAX_SAFE_INTEGER), "input byte");
|
|
918
|
+
if (!wide)
|
|
919
|
+
number(size + chunk.length, 0xfffffffe, "classic input byte");
|
|
920
|
+
// STORE output has the same size as its input. Admit it before
|
|
921
|
+
// copying any producer bytes into an owned slab.
|
|
922
|
+
if (entry.method === 0) {
|
|
923
|
+
const overhead = !wideArchive && localLength + bytes.length + item.compressedSize + chunk.length + (descriptor ? wide ? 24 : 16 : 0) >= 0xffffffff ? 76 : 0;
|
|
924
|
+
number(length + chunk.length + overhead, limits.maxArchiveBytes, "archive byte");
|
|
925
|
+
}
|
|
926
|
+
for (let offset = 0; offset < chunk.length; offset += chunkSize) {
|
|
927
|
+
if (++work > limits.maxPatternSteps)
|
|
928
|
+
fail("ZIP archive work limit exceeded");
|
|
929
|
+
const view = chunk.subarray(offset, Math.min(chunk.length, offset + chunkSize));
|
|
930
|
+
size += view.length;
|
|
931
|
+
checksum = crc32(view, checksum);
|
|
932
|
+
if (entry.method !== 0)
|
|
933
|
+
for (const byte of view) {
|
|
934
|
+
if (byte <= 6 || byte >= 14 && byte <= 25 || byte >= 28 && byte <= 31)
|
|
935
|
+
binary = true;
|
|
936
|
+
else if (byte === 9 || byte === 10 || byte === 13 || byte >= 32)
|
|
937
|
+
textual = true;
|
|
938
|
+
}
|
|
939
|
+
// A consumer can still reference the consumed slab while next()
|
|
940
|
+
// admits its replacement. Account for both before allocating.
|
|
941
|
+
archive.onRetention?.({ metadataBytes, payloadBytes: previousSlab + view.length });
|
|
942
|
+
previousSlab = view.length;
|
|
943
|
+
yield new Uint8Array(view);
|
|
944
|
+
await yieldTurn(signal);
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
if (entry.expectedSize !== undefined && size !== entry.expectedSize)
|
|
948
|
+
fail("ZIP source changed while reading");
|
|
949
|
+
})();
|
|
950
|
+
const reader = new CodecReader(measured, signal);
|
|
951
|
+
const payload = entry.method === 14 ? zipLzma(reader, signal, { decode: false, level: entry.level ?? 6, eos: true, size: entry.expectedSize ?? 0 }) : entry.method === 12 ? boundedCodec(reader, { format: "bzip2", decompress: false, level: entry.level ?? 6 }, signal)
|
|
952
|
+
: entry.method === 8 ? codec(reader, { mode: "deflate-raw", level: entry.level ?? 6, chunkSize }, signal) : measured;
|
|
953
|
+
item.compressedSize = 0;
|
|
954
|
+
try {
|
|
955
|
+
const wire = entry.encryption ? encryptZipPayload(payload, entry.encryption, time >>> 8, signal) : payload;
|
|
956
|
+
for await (const chunk of wire) {
|
|
957
|
+
if (++work > limits.maxPatternSteps)
|
|
958
|
+
fail("ZIP archive work limit exceeded");
|
|
959
|
+
if (!wideArchive && localLength + bytes.length + item.compressedSize + chunk.length + (descriptor ? wide ? 24 : 16 : 0) >= 0xffffffff) {
|
|
960
|
+
if (!allowZip64)
|
|
961
|
+
fail("ZIP64 required but disabled");
|
|
962
|
+
number(length + chunk.length + 76, limits.maxArchiveBytes, "archive byte");
|
|
963
|
+
length += 76;
|
|
964
|
+
wideArchive = true;
|
|
965
|
+
}
|
|
966
|
+
number(length + chunk.length, limits.maxArchiveBytes, "archive byte");
|
|
967
|
+
if (!wide)
|
|
968
|
+
number(item.compressedSize + chunk.length, 0xfffffffe, "classic compressed byte");
|
|
969
|
+
length += chunk.length;
|
|
970
|
+
item.compressedSize += chunk.length;
|
|
971
|
+
yield chunk;
|
|
972
|
+
signal.throwIfAborted();
|
|
973
|
+
}
|
|
589
974
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
view.setUint32(descriptor + 12, entry.size, true);
|
|
975
|
+
finally {
|
|
976
|
+
await reader.close();
|
|
593
977
|
}
|
|
978
|
+
total += size;
|
|
979
|
+
item.entry.size = size;
|
|
980
|
+
item.entry.crc32 = checksum;
|
|
981
|
+
if (entry.method !== 0)
|
|
982
|
+
item.entry.internalAttributes = !binary && textual ? 1 : 0;
|
|
983
|
+
refreshCentral(item, size, item.compressedSize, item.offset);
|
|
984
|
+
archive.onRetention?.({ metadataBytes, payloadBytes: 0 });
|
|
985
|
+
entry.size = size;
|
|
986
|
+
entry.crc32 = checksum;
|
|
987
|
+
entry.compressedSize = item.compressedSize;
|
|
988
|
+
if (item.entry.internalAttributes !== undefined)
|
|
989
|
+
entry.internalAttributes = item.entry.internalAttributes;
|
|
990
|
+
}
|
|
991
|
+
else
|
|
992
|
+
yield* wireChunks(entry.data, chunkSize, signal);
|
|
993
|
+
localLength += bytes.length + item.compressedSize + (descriptor ? wide ? 24 : 16 : 0);
|
|
994
|
+
if (flags & 8) {
|
|
995
|
+
const bytes = zipDescriptor(item.entry.crc32, item.compressedSize, item.entry.size, wide);
|
|
594
996
|
yield* wireChunks(bytes, chunkSize, signal);
|
|
595
997
|
}
|
|
596
998
|
}
|
|
597
999
|
let centralSize = 0;
|
|
598
|
-
for (const item of encoded) {
|
|
1000
|
+
for (const [index, item] of encoded.entries()) {
|
|
599
1001
|
await yieldTurn(signal);
|
|
600
1002
|
const { entry, rawName, wide, flags, centralExtra, comment, date, time, offset } = item;
|
|
601
|
-
const
|
|
1003
|
+
const member = zip64Member(entry.size, item.compressedSize, offset, forceZip64 || entry.zip64 === true, allowZip64 && (forceZip64 || entry.zip64 !== false), wide && Boolean(flags & 8));
|
|
1004
|
+
const record = forceZip64 ? undefined : zipGrowRecords.get(archive.entries[index]);
|
|
1005
|
+
const version = record ? new DataView(record.buffer, record.byteOffset, record.byteLength).getUint16(4, true)
|
|
1006
|
+
: entry.method === 14 ? 63 : entry.method === 99 ? 51 : entry.method === 12 ? 46 : wide || member.wide ? 45 : entry.method === 8 || flags & 9 ? 20 : 10;
|
|
602
1007
|
const central = 0;
|
|
603
1008
|
const bytes = new Uint8Array(46 + rawName.length + centralExtra.length + comment.length);
|
|
604
1009
|
const view = new DataView(bytes.buffer);
|
|
@@ -610,40 +1015,33 @@ export async function* streamZipArchive(archive, limits, signal, descriptors = f
|
|
|
610
1015
|
view.setUint16(central + 12, time, true);
|
|
611
1016
|
view.setUint16(central + 14, date, true);
|
|
612
1017
|
view.setUint32(central + 16, entry.crc32, true);
|
|
613
|
-
view.setUint32(central + 20,
|
|
614
|
-
view.setUint32(central + 24,
|
|
1018
|
+
view.setUint32(central + 20, member.compressed, true);
|
|
1019
|
+
view.setUint32(central + 24, member.size, true);
|
|
615
1020
|
view.setUint16(central + 28, rawName.length, true);
|
|
616
1021
|
view.setUint16(central + 30, centralExtra.length, true);
|
|
617
1022
|
view.setUint16(central + 32, comment.length, true);
|
|
618
1023
|
view.setUint16(central + 36, entry.internalAttributes ?? 0, true);
|
|
619
1024
|
const mode = entry.mode & 0o170000 ? entry.mode : entry.mode | (entry.directory ? 0o040000 : entry.symlink ? 0o120000 : 0o100000);
|
|
620
1025
|
view.setUint32(central + 38, entry.externalAttributes ?? (mode * 65536 + (entry.directory ? 16 : 0)), true);
|
|
621
|
-
view.setUint32(central + 42,
|
|
1026
|
+
view.setUint32(central + 42, member.offset, true);
|
|
622
1027
|
bytes.set(rawName, central + 46);
|
|
623
1028
|
bytes.set(centralExtra, central + 46 + rawName.length);
|
|
624
1029
|
bytes.set(comment, central + 46 + rawName.length + centralExtra.length);
|
|
625
1030
|
centralSize += bytes.length;
|
|
626
1031
|
yield* wireChunks(bytes, chunkSize, signal);
|
|
627
1032
|
}
|
|
628
|
-
|
|
629
|
-
const
|
|
630
|
-
const central = wideArchive ? writeZip64End(view, 0, encoded.length, centralSize, localLength, localLength + centralSize) : 0;
|
|
631
|
-
view.setUint32(central, 0x06054b50, true);
|
|
632
|
-
view.setUint16(central + 8, Math.min(encoded.length, 65535), true);
|
|
633
|
-
view.setUint16(central + 10, Math.min(encoded.length, 65535), true);
|
|
634
|
-
view.setUint32(central + 12, centralSize, true);
|
|
635
|
-
view.setUint32(central + 16, wideArchive ? 0xffffffff : localLength, true);
|
|
636
|
-
view.setUint16(central + 20, archive.comment.length, true);
|
|
637
|
-
bytes.set(archive.comment, central + 22);
|
|
1033
|
+
number(localLength + centralSize + 22 + archive.comment.length + (wideArchive || centralSize >= 0xffffffff || localLength >= 0xffffffff ? 76 : 0), limits.maxArchiveBytes, "archive byte");
|
|
1034
|
+
const bytes = zipEnd(encoded.length, centralSize, localLength, archive.comment, wideArchive, allowZip64);
|
|
638
1035
|
yield* wireChunks(bytes, chunkSize, signal);
|
|
639
1036
|
}
|
|
640
1037
|
async function* wireChunks(bytes, chunkSize, signal) {
|
|
641
1038
|
for (let offset = 0; offset < bytes.length; offset += chunkSize) {
|
|
642
1039
|
await yieldTurn(signal);
|
|
643
1040
|
yield new Uint8Array(bytes.subarray(offset, Math.min(bytes.length, offset + chunkSize)));
|
|
1041
|
+
signal.throwIfAborted();
|
|
644
1042
|
}
|
|
645
1043
|
}
|
|
646
|
-
export async function writeZipArchive(archive, limits, signal, descriptors = false, forceZip64 = false) {
|
|
647
|
-
return collectBytes(streamZipArchive(archive, limits, signal, descriptors, forceZip64), { maxBytes: limits.maxArchiveBytes, signal });
|
|
1044
|
+
export async function writeZipArchive(archive, limits, signal, descriptors = false, forceZip64 = false, allowZip64 = true) {
|
|
1045
|
+
return collectBytes(streamZipArchive(archive, limits, signal, descriptors, forceZip64, allowZip64), { maxBytes: limits.maxArchiveBytes, signal });
|
|
648
1046
|
}
|
|
649
1047
|
//# sourceMappingURL=zip-format.js.map
|