@patrickdk77/archive-defuse 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +378 -2
  3. package/dist/aes7z.d.ts +16 -0
  4. package/dist/aes7z.js +131 -0
  5. package/dist/alone.d.ts +21 -0
  6. package/dist/alone.js +65 -0
  7. package/dist/bcj2.d.ts +21 -0
  8. package/dist/bcj2.js +140 -0
  9. package/dist/bzip2.d.ts +21 -0
  10. package/dist/bzip2.js +688 -0
  11. package/dist/cab.d.ts +63 -0
  12. package/dist/cab.js +493 -0
  13. package/dist/checks.d.ts +23 -0
  14. package/dist/checks.js +123 -0
  15. package/dist/cli.d.ts +2 -0
  16. package/dist/cli.js +364 -0
  17. package/dist/codecs.d.ts +39 -0
  18. package/dist/codecs.js +124 -0
  19. package/dist/coders.d.ts +58 -0
  20. package/dist/coders.js +392 -0
  21. package/dist/crc32.d.ts +8 -0
  22. package/dist/crc32.js +59 -0
  23. package/dist/detect.d.ts +10 -0
  24. package/dist/detect.js +95 -0
  25. package/dist/encoder.d.ts +44 -0
  26. package/dist/encoder.js +114 -0
  27. package/dist/engine.d.ts +44 -0
  28. package/dist/engine.js +753 -0
  29. package/dist/errors.d.ts +64 -0
  30. package/dist/errors.js +98 -0
  31. package/dist/filters.d.ts +34 -0
  32. package/dist/filters.js +385 -0
  33. package/dist/findings.d.ts +26 -0
  34. package/dist/findings.js +176 -0
  35. package/dist/formats.d.ts +24 -0
  36. package/dist/formats.js +106 -0
  37. package/dist/index.d.ts +9 -0
  38. package/dist/index.js +53 -0
  39. package/dist/index.mjs +3 -0
  40. package/dist/inflate64.d.ts +6 -0
  41. package/dist/inflate64.js +388 -0
  42. package/dist/io.d.ts +78 -0
  43. package/dist/io.js +379 -0
  44. package/dist/lzma-formats.d.ts +75 -0
  45. package/dist/lzma-formats.js +373 -0
  46. package/dist/lzma-sniff.d.ts +20 -0
  47. package/dist/lzma-sniff.js +73 -0
  48. package/dist/lzma-writers.d.ts +35 -0
  49. package/dist/lzma-writers.js +276 -0
  50. package/dist/lzma1/LICENSE +201 -0
  51. package/dist/lzma1/NOTICE +36 -0
  52. package/dist/lzma1/crc32.d.ts +4 -0
  53. package/dist/lzma1/crc32.js +19 -0
  54. package/dist/lzma1/hash234.d.ts +22 -0
  55. package/dist/lzma1/hash234.js +75 -0
  56. package/dist/lzma1/hc4.d.ts +30 -0
  57. package/dist/lzma1/hc4.js +142 -0
  58. package/dist/lzma1/header.d.ts +32 -0
  59. package/dist/lzma1/header.js +73 -0
  60. package/dist/lzma1/lz-decoder.d.ts +41 -0
  61. package/dist/lzma1/lz-decoder.js +128 -0
  62. package/dist/lzma1/lz-encoder.d.ts +87 -0
  63. package/dist/lzma1/lz-encoder.js +166 -0
  64. package/dist/lzma1/lzma-coder.d.ts +107 -0
  65. package/dist/lzma1/lzma-coder.js +162 -0
  66. package/dist/lzma1/lzma-decoder.d.ts +36 -0
  67. package/dist/lzma1/lzma-decoder.js +484 -0
  68. package/dist/lzma1/lzma-encoder-fast.d.ts +13 -0
  69. package/dist/lzma1/lzma-encoder-fast.js +114 -0
  70. package/dist/lzma1/lzma-encoder.d.ts +105 -0
  71. package/dist/lzma1/lzma-encoder.js +497 -0
  72. package/dist/lzma1/lzma2-decoder.d.ts +85 -0
  73. package/dist/lzma1/lzma2-decoder.js +249 -0
  74. package/dist/lzma1/range-coder.d.ts +18 -0
  75. package/dist/lzma1/range-coder.js +23 -0
  76. package/dist/lzma1/range-decoder.d.ts +41 -0
  77. package/dist/lzma1/range-decoder.js +78 -0
  78. package/dist/lzma1/range-encoder.d.ts +53 -0
  79. package/dist/lzma1/range-encoder.js +269 -0
  80. package/dist/lzma1-stream.d.ts +60 -0
  81. package/dist/lzma1-stream.js +202 -0
  82. package/dist/model.d.ts +141 -0
  83. package/dist/model.js +16 -0
  84. package/dist/names.d.ts +13 -0
  85. package/dist/names.js +66 -0
  86. package/dist/pack.d.ts +79 -0
  87. package/dist/pack.js +590 -0
  88. package/dist/plugins.d.ts +13 -0
  89. package/dist/plugins.js +56 -0
  90. package/dist/rules.d.ts +25 -0
  91. package/dist/rules.js +403 -0
  92. package/dist/score.d.ts +17 -0
  93. package/dist/score.js +58 -0
  94. package/dist/seven.d.ts +75 -0
  95. package/dist/seven.js +492 -0
  96. package/dist/sevenzip.d.ts +133 -0
  97. package/dist/sevenzip.js +529 -0
  98. package/dist/single.d.ts +103 -0
  99. package/dist/single.js +453 -0
  100. package/dist/sniff.d.ts +20 -0
  101. package/dist/sniff.js +224 -0
  102. package/dist/stream-meter.d.ts +39 -0
  103. package/dist/stream-meter.js +54 -0
  104. package/dist/tar.d.ts +102 -0
  105. package/dist/tar.js +765 -0
  106. package/dist/types.d.ts +311 -0
  107. package/dist/types.js +133 -0
  108. package/dist/unpack.d.ts +67 -0
  109. package/dist/unpack.js +194 -0
  110. package/dist/xz.d.ts +25 -0
  111. package/dist/xz.js +333 -0
  112. package/dist/zip-lzma.d.ts +15 -0
  113. package/dist/zip-lzma.js +137 -0
  114. package/dist/zip-writer.d.ts +51 -0
  115. package/dist/zip-writer.js +305 -0
  116. package/dist/zip.d.ts +99 -0
  117. package/dist/zip.js +901 -0
  118. package/dist/zipcrypt.d.ts +47 -0
  119. package/dist/zipcrypt.js +233 -0
  120. package/dist/zstd.d.ts +25 -0
  121. package/dist/zstd.js +160 -0
  122. package/package.json +94 -4
@@ -0,0 +1,58 @@
1
+ import { type AesProps } from './aes7z';
2
+ import { type Coder, type Folder } from './sevenzip';
3
+ import type { ByteSource } from './types';
4
+ export type Chunks = AsyncIterable<Uint8Array>;
5
+ /** LZMA2's dictionary size from its properties byte, or undefined for a byte above 40. */
6
+ export declare function lzma2DictSize(p: number): number | undefined;
7
+ /** Turns an error from lzma1's decoders into a data error. Anything else, such as a limit, passes unchanged. */
8
+ export declare function lzmaError(e: unknown): unknown;
9
+ /**
10
+ * Feeds raw LZMA2 data to lzma1's decoder one chunk at a time. A chunk decodes to at most 2 MiB, so one step never
11
+ * holds more however small the chunks are made. It also tells where the data ends, which an xz block needs.
12
+ */
13
+ export declare class Lzma2Feeder {
14
+ private readonly dec;
15
+ private readonly out;
16
+ private pend;
17
+ /** The end marker has been fed. */
18
+ ended: boolean;
19
+ /** Input bytes used, up to and including the end marker. */
20
+ consumed: number;
21
+ constructor(dictSize: number);
22
+ /** The size of the chunk that starts with `pend`, once its header is there. */
23
+ private chunkSize;
24
+ /** Feeds `chunk` and returns how many of its bytes were used, fewer than all once the end marker is in. */
25
+ push(chunk: Uint8Array): number;
26
+ /** Decoded bytes waiting to be taken. */
27
+ take(): Uint8Array[];
28
+ end(): void;
29
+ }
30
+ export declare const methodName: (id: string) => string;
31
+ export declare const isAes: (c: Coder) => boolean;
32
+ /** The limits checked before a folder is decoded. */
33
+ export interface FolderLimits {
34
+ decompressedBytes: number;
35
+ dictionaryBytes: number;
36
+ }
37
+ /**
38
+ * Checks that every coder of `folder` is one this package decodes, with valid properties, and that no coder's output
39
+ * and no dictionary goes over the limits. Returns the dictionary or size that went over, for the caller's finding.
40
+ */
41
+ export declare function checkFolder(folder: Folder, limits: FolderLimits): {
42
+ limit: 'decompressed' | 'dictionary';
43
+ value: number;
44
+ } | undefined;
45
+ /** What decoding a folder needs from the run. */
46
+ export interface FolderEnv {
47
+ source: ByteSource;
48
+ deadline?: number;
49
+ /** Called with each count of packed bytes read. */
50
+ onPacked(n: number): void;
51
+ /** The key for a 7zAES coder, from the password being tried. */
52
+ key(props: AesProps): Buffer;
53
+ }
54
+ /**
55
+ * Decodes a folder checked with checkFolder: its main output, exactly as long as declared. `packOffsets` are the
56
+ * absolute offsets of the folder's packed streams.
57
+ */
58
+ export declare function decodeFolder(folder: Folder, packOffsets: number[], packSizes: number[], env: FolderEnv): Chunks;
package/dist/coders.js ADDED
@@ -0,0 +1,392 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.isAes = exports.methodName = exports.Lzma2Feeder = void 0;
37
+ exports.lzma2DictSize = lzma2DictSize;
38
+ exports.lzmaError = lzmaError;
39
+ exports.checkFolder = checkFolder;
40
+ exports.decodeFolder = decodeFolder;
41
+ const node_stream_1 = require("node:stream");
42
+ const zlib = __importStar(require("node:zlib"));
43
+ const aes7z_1 = require("./aes7z");
44
+ const bcj2_1 = require("./bcj2");
45
+ const bzip2_1 = require("./bzip2");
46
+ const errors_1 = require("./errors");
47
+ const filters_1 = require("./filters");
48
+ const inflate64_1 = require("./inflate64");
49
+ const lzma2_decoder_1 = require("./lzma1/lzma2-decoder");
50
+ const lzma1_stream_1 = require("./lzma1-stream");
51
+ const sevenzip_1 = require("./sevenzip");
52
+ /** Bytes of LZMA1 input decoded per step. LZMA1 decodes to at most about 7,000 times its input. */
53
+ const LZMA1_SLICE = 1024;
54
+ /** Bytes of packed data read from the source at a time. */
55
+ const READ_SIZE = 1 << 16;
56
+ /** LZMA2's dictionary size from its properties byte, or undefined for a byte above 40. */
57
+ function lzma2DictSize(p) {
58
+ if (p > 40)
59
+ return undefined;
60
+ return p === 40 ? 0xffffffff : (2 | (p & 1)) * 2 ** (Math.floor(p / 2) + 11);
61
+ }
62
+ /** Turns an error from lzma1's decoders into a data error. Anything else, such as a limit, passes unchanged. */
63
+ function lzmaError(e) {
64
+ if (e instanceof errors_1.DataError)
65
+ return e;
66
+ const message = String(e?.message ?? e);
67
+ if (message === 'Truncated input')
68
+ return new errors_1.DataError('the data ends before its end', true);
69
+ if (/^Corrupted input: /.test(message))
70
+ return new errors_1.DataError(message.replace(/^Corrupted input: /, ''));
71
+ return e;
72
+ }
73
+ /**
74
+ * Feeds raw LZMA2 data to lzma1's decoder one chunk at a time. A chunk decodes to at most 2 MiB, so one step never
75
+ * holds more however small the chunks are made. It also tells where the data ends, which an xz block needs.
76
+ */
77
+ class Lzma2Feeder {
78
+ dec;
79
+ out = [];
80
+ pend = new Uint8Array(0);
81
+ /** The end marker has been fed. */
82
+ ended = false;
83
+ /** Input bytes used, up to and including the end marker. */
84
+ consumed = 0;
85
+ constructor(dictSize) {
86
+ this.dec = new lzma2_decoder_1.Lzma2Decoder(Math.max(dictSize, 4096), chunk => this.out.push(chunk.slice()));
87
+ }
88
+ /** The size of the chunk that starts with `pend`, once its header is there. */
89
+ chunkSize() {
90
+ const p = this.pend;
91
+ if (p.length < 1)
92
+ return undefined;
93
+ const control = p[0];
94
+ if (control === 0x00 || (control > 0x02 && control < 0x80))
95
+ return 1;
96
+ if (control < 0x80)
97
+ return p.length < 3 ? undefined : 3 + ((p[1] << 8) | p[2]) + 1;
98
+ const header = control >= 0xc0 ? 6 : 5;
99
+ return p.length < 5 ? undefined : header + ((p[3] << 8) | p[4]) + 1;
100
+ }
101
+ /** Feeds `chunk` and returns how many of its bytes were used, fewer than all once the end marker is in. */
102
+ push(chunk) {
103
+ let used = 0;
104
+ while (used < chunk.length && !this.ended) {
105
+ const size = this.chunkSize();
106
+ const want = size === undefined ? (this.pend.length < 1 ? 1 : this.pend[0] >= 0x80 ? 5 : 3) : size;
107
+ const take = Math.min(want - this.pend.length, chunk.length - used);
108
+ this.pend = (0, lzma1_stream_1.concat)(this.pend, chunk.slice(used, used + take));
109
+ used += take;
110
+ const whole = this.chunkSize();
111
+ if (whole !== undefined && this.pend.length === whole) {
112
+ const bytes = this.pend;
113
+ this.pend = new Uint8Array(0);
114
+ try {
115
+ this.dec.write(bytes);
116
+ }
117
+ catch (e) {
118
+ throw lzmaError(e);
119
+ }
120
+ if (bytes[0] === 0x00)
121
+ this.ended = true;
122
+ }
123
+ }
124
+ this.consumed += used;
125
+ return used;
126
+ }
127
+ /** Decoded bytes waiting to be taken. */
128
+ take() {
129
+ return this.out.splice(0);
130
+ }
131
+ end() {
132
+ if (!this.ended)
133
+ throw new errors_1.DataError('the LZMA2 data ends before its end marker', true);
134
+ }
135
+ }
136
+ exports.Lzma2Feeder = Lzma2Feeder;
137
+ /** Bytes of a packed stream handed on at a time, so the count of compressed bytes follows what the coders took. */
138
+ const PACK_SLICE = 4096;
139
+ /** Reads `size` bytes of `source` from `offset`, reporting each piece as it is handed on. */
140
+ async function* packStream(source, offset, size, onRead, deadline) {
141
+ for (let pos = 0; pos < size;) {
142
+ (0, errors_1.checkTime)(deadline);
143
+ const want = Math.min(READ_SIZE, size - pos);
144
+ const b = await source.read(offset + pos, want);
145
+ if (b.length === 0)
146
+ throw new errors_1.DataError('a packed stream ends past the end of the file', true);
147
+ for (let k = 0; k < b.length; k += PACK_SLICE) {
148
+ const piece = b.subarray(k, k + PACK_SLICE);
149
+ onRead(piece.length);
150
+ yield piece;
151
+ }
152
+ pos += b.length;
153
+ }
154
+ }
155
+ async function* lzma1Decode(input, props, size, deadline) {
156
+ if (props.length < 5)
157
+ throw new errors_1.DataError('LZMA properties are cut short');
158
+ const dictSize = props[1] + props[2] * 2 ** 8 + props[3] * 2 ** 16 + props[4] * 2 ** 24;
159
+ const out = [];
160
+ const dec = new lzma1_stream_1.Lzma1Stream((0, lzma1_stream_1.decodeProps)(props[0], dictSize), size, chunk => out.push(chunk.slice()), false);
161
+ for await (const chunk of input) {
162
+ for (let p = 0; p < chunk.length && !dec.done; p += LZMA1_SLICE) {
163
+ dec.write(chunk.subarray(p, p + LZMA1_SLICE));
164
+ (0, errors_1.checkTime)(deadline);
165
+ for (const c of out.splice(0))
166
+ yield c;
167
+ }
168
+ if (dec.done)
169
+ break;
170
+ }
171
+ dec.end();
172
+ for (const c of out.splice(0))
173
+ yield c;
174
+ }
175
+ async function* lzma2Decode(input, props, deadline) {
176
+ const dict = props.length === 1 ? lzma2DictSize(props[0]) : undefined;
177
+ if (dict === undefined)
178
+ throw new errors_1.DataError('invalid LZMA2 properties');
179
+ const feeder = new Lzma2Feeder(dict);
180
+ for await (const chunk of input) {
181
+ for (let used = 0; used < chunk.length && !feeder.ended;) {
182
+ used += feeder.push(chunk.subarray(used, used + READ_SIZE));
183
+ (0, errors_1.checkTime)(deadline);
184
+ for (const c of feeder.take())
185
+ yield c;
186
+ }
187
+ if (feeder.ended)
188
+ break;
189
+ }
190
+ feeder.end();
191
+ }
192
+ async function* transform(input, t) {
193
+ for await (const chunk of input) {
194
+ // The transform changes its input in place, and the chunk may be a view of bytes someone else holds. A Buffer's
195
+ // slice() is such a view too, so the copy is made explicitly.
196
+ const out = t.push(new Uint8Array(chunk));
197
+ if (out.length)
198
+ yield out;
199
+ }
200
+ const rest = t.end();
201
+ if (rest.length)
202
+ yield rest;
203
+ }
204
+ /** Inflates raw Deflate data through Node's zlib. */
205
+ async function* inflate(input) {
206
+ const src = node_stream_1.Readable.from(input);
207
+ const z = zlib.createInflateRaw();
208
+ let failure;
209
+ (0, node_stream_1.pipeline)(src, z, e => {
210
+ if (e)
211
+ failure ??= e;
212
+ });
213
+ try {
214
+ for await (const chunk of z)
215
+ yield chunk;
216
+ }
217
+ catch (e) {
218
+ // zlib's own errors carry a Z_ code. Anything else, such as a limit or bad data before the inflate, passes unchanged.
219
+ const code = e?.code;
220
+ if (typeof code === 'string' && code.startsWith('Z_'))
221
+ throw new errors_1.DataError(`Deflate data does not inflate: ${String(e.message)}`);
222
+ throw e;
223
+ }
224
+ finally {
225
+ src.destroy();
226
+ z.destroy();
227
+ }
228
+ if (failure !== undefined)
229
+ throw failure;
230
+ }
231
+ /** Yields exactly `size` bytes of `input`, and stops reading it there. */
232
+ async function* exactly(input, size) {
233
+ let n = 0;
234
+ if (size > 0) {
235
+ for await (const chunk of input) {
236
+ const room = size - n;
237
+ if (chunk.length >= room) {
238
+ if (room > 0)
239
+ yield chunk.subarray(0, room);
240
+ n = size;
241
+ break;
242
+ }
243
+ n += chunk.length;
244
+ yield chunk;
245
+ }
246
+ }
247
+ if (n < size)
248
+ throw new errors_1.DataError('a coder decodes to less than its declared size', true);
249
+ }
250
+ const METHODS = {
251
+ '00': { name: 'Copy', numIn: 1 },
252
+ '03': { name: 'Delta', numIn: 1 },
253
+ '04': { name: 'BCJ', numIn: 1, branch: 'x86' },
254
+ '03030103': { name: 'BCJ', numIn: 1, branch: 'x86' },
255
+ '05': { name: 'PPC', numIn: 1, branch: 'powerpc' },
256
+ '03030205': { name: 'PPC', numIn: 1, branch: 'powerpc' },
257
+ '06': { name: 'IA64', numIn: 1, branch: 'ia64' },
258
+ '03030401': { name: 'IA64', numIn: 1, branch: 'ia64' },
259
+ '07': { name: 'ARM', numIn: 1, branch: 'arm' },
260
+ '03030501': { name: 'ARM', numIn: 1, branch: 'arm' },
261
+ '08': { name: 'ARMT', numIn: 1, branch: 'armthumb' },
262
+ '03030701': { name: 'ARMT', numIn: 1, branch: 'armthumb' },
263
+ '09': { name: 'SPARC', numIn: 1, branch: 'sparc' },
264
+ '03030805': { name: 'SPARC', numIn: 1, branch: 'sparc' },
265
+ '0a': { name: 'ARM64', numIn: 1, branch: 'arm64' },
266
+ '0b': { name: 'RISCV', numIn: 1, branch: 'riscv' },
267
+ '0303011b': { name: 'BCJ2', numIn: 4 },
268
+ '030101': { name: 'LZMA', numIn: 1 },
269
+ '21': { name: 'LZMA2', numIn: 1 },
270
+ '040108': { name: 'Deflate', numIn: 1 },
271
+ '040109': { name: 'Deflate64', numIn: 1 },
272
+ '040202': { name: 'BZip2', numIn: 1 },
273
+ '06f10701': { name: '7zAES', numIn: 1 },
274
+ };
275
+ /** Names of methods this package knows but does not decode, for the finding. */
276
+ const UNSUPPORTED = {
277
+ '030401': 'PPMd',
278
+ '04f71101': 'Zstandard',
279
+ '04f71102': 'Brotli',
280
+ '04f71104': 'LZ4',
281
+ '04f71105': 'LZ5',
282
+ '04f71106': 'Lizard',
283
+ '04f71107': 'Fast LZMA2',
284
+ '03030601': 'M68',
285
+ '03030301': 'Alpha',
286
+ '020302': 'Swap2',
287
+ '020304': 'Swap4',
288
+ };
289
+ const methodName = (id) => METHODS[id]?.name ?? UNSUPPORTED[id] ?? `method ${id}`;
290
+ exports.methodName = methodName;
291
+ const isAes = (c) => c.method === '06f10701';
292
+ exports.isAes = isAes;
293
+ /**
294
+ * Checks that every coder of `folder` is one this package decodes, with valid properties, and that no coder's output
295
+ * and no dictionary goes over the limits. Returns the dictionary or size that went over, for the caller's finding.
296
+ */
297
+ function checkFolder(folder, limits) {
298
+ // The coders of a folder decode together, so their dictionaries and probability tables count together.
299
+ let memory = 0;
300
+ for (let c = 0; c < folder.coders.length; c++) {
301
+ const coder = folder.coders[c];
302
+ const m = METHODS[coder.method];
303
+ if (!m)
304
+ throw new errors_1.UnsupportedError(`7z method ${(0, exports.methodName)(coder.method)}`, true, { method: (0, exports.methodName)(coder.method) });
305
+ if (coder.numIn !== m.numIn || coder.numOut !== 1)
306
+ throw new errors_1.ParseError(`the ${m.name} coder has the wrong number of streams`);
307
+ const size = folder.unpackSizes[(0, sevenzip_1.firstStream)(folder, c, 'out')];
308
+ if (size > limits.decompressedBytes)
309
+ return { limit: 'decompressed', value: size };
310
+ if (coder.method === '030101') {
311
+ if (coder.props.length < 5)
312
+ throw new errors_1.ParseError('LZMA properties are cut short');
313
+ const p = (0, lzma1_stream_1.decodeProps)(coder.props[0], 0);
314
+ const dict = coder.props[1] + coder.props[2] * 2 ** 8 + coder.props[3] * 2 ** 16 + coder.props[4] * 2 ** 24;
315
+ memory += (0, lzma1_stream_1.effectiveDictSize)(dict, size) + 2 * 0x300 * 2 ** (p.lc + p.lp);
316
+ if (memory > limits.dictionaryBytes)
317
+ return { limit: 'dictionary', value: memory };
318
+ }
319
+ else if (coder.method === '21') {
320
+ const dict = coder.props.length === 1 ? lzma2DictSize(coder.props[0]) : undefined;
321
+ if (dict === undefined)
322
+ throw new errors_1.ParseError('invalid LZMA2 properties');
323
+ memory += (0, lzma1_stream_1.effectiveDictSize)(dict, size);
324
+ if (memory > limits.dictionaryBytes)
325
+ return { limit: 'dictionary', value: memory };
326
+ }
327
+ else if (coder.method === '03') {
328
+ if (coder.props.length !== 1)
329
+ throw new errors_1.ParseError('invalid Delta properties');
330
+ }
331
+ else if (m.branch && coder.props.length !== 0 && coder.props.length !== 4) {
332
+ throw new errors_1.ParseError(`invalid ${m.name} properties`);
333
+ }
334
+ else if ((0, exports.isAes)(coder))
335
+ (0, aes7z_1.parseAesProps)(coder.props);
336
+ }
337
+ return undefined;
338
+ }
339
+ function runCoder(coder, inputs, size, env) {
340
+ const m = METHODS[coder.method];
341
+ const start = coder.props.length === 4 ? (coder.props[0] | (coder.props[1] << 8) | (coder.props[2] << 16) | (coder.props[3] << 24)) >>> 0 : 0;
342
+ if (m.branch)
343
+ return transform(inputs[0], new filters_1.SimpleFilterStream(m.branch, start));
344
+ switch (coder.method) {
345
+ case '00':
346
+ return inputs[0];
347
+ case '03':
348
+ return transform(inputs[0], new filters_1.DeltaStream(coder.props[0] + 1));
349
+ case '0303011b':
350
+ return (0, bcj2_1.decodeBcj2)(inputs[0], inputs[1], inputs[2], inputs[3], size);
351
+ case '030101':
352
+ return lzma1Decode(inputs[0], coder.props, size, env.deadline);
353
+ case '21':
354
+ return lzma2Decode(inputs[0], coder.props, env.deadline);
355
+ case '040108':
356
+ return inflate(inputs[0]);
357
+ case '040109':
358
+ return (0, inflate64_1.inflate64)(inputs[0], undefined, env.deadline);
359
+ case '040202':
360
+ // Streams after the first decode too instead of failing the folder. The folder's declared size bounds the output.
361
+ return (0, bzip2_1.bunzip2)(inputs[0], { streams: [], trailing: 0, trailingZeros: true }, true);
362
+ default: {
363
+ const props = (0, aes7z_1.parseAesProps)(coder.props);
364
+ return (0, aes7z_1.decryptAes)(inputs[0], env.key(props), props.iv);
365
+ }
366
+ }
367
+ }
368
+ /**
369
+ * Decodes a folder checked with checkFolder: its main output, exactly as long as declared. `packOffsets` are the
370
+ * absolute offsets of the folder's packed streams.
371
+ */
372
+ function decodeFolder(folder, packOffsets, packSizes, env) {
373
+ const outStream = (outIndex) => {
374
+ const { coder: c } = (0, sevenzip_1.coderOf)(folder, outIndex, 'out');
375
+ const coder = folder.coders[c];
376
+ const first = (0, sevenzip_1.firstStream)(folder, c, 'in');
377
+ const inputs = [];
378
+ for (let i = 0; i < coder.numIn; i++) {
379
+ const inIndex = first + i;
380
+ const bp = folder.bindPairs.find(p => p.inIndex === inIndex);
381
+ if (bp)
382
+ inputs.push(outStream(bp.outIndex));
383
+ else {
384
+ const k = folder.packedStreams.indexOf(inIndex);
385
+ inputs.push(packStream(env.source, packOffsets[k], packSizes[k], env.onPacked, env.deadline));
386
+ }
387
+ }
388
+ const size = folder.unpackSizes[outIndex];
389
+ return exactly(runCoder(coder, inputs, size, env), size);
390
+ };
391
+ return outStream(folder.mainOut);
392
+ }
@@ -0,0 +1,8 @@
1
+ /** Continues a CRC-32, the one ZIP uses, over `bytes`, with a lookup table. Start from 0. */
2
+ export declare function tableCrc32(bytes: Uint8Array, crc?: number): number;
3
+ /**
4
+ * Continues a CRC-32, the one ZIP uses, over `bytes`. Start from 0. This is zlib.crc32 where Node has it, about 50
5
+ * times faster, and the table otherwise. Node 18 and early releases of Node 20 and 22 lack it. The choice is made
6
+ * once, when the module loads.
7
+ */
8
+ export declare const crc32: (bytes: Uint8Array, crc?: number) => number;
package/dist/crc32.js ADDED
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.crc32 = void 0;
37
+ exports.tableCrc32 = tableCrc32;
38
+ // Copied from office-defuse src/crc32.ts.
39
+ const zlib = __importStar(require("node:zlib"));
40
+ const TABLE = new Uint32Array(256);
41
+ for (let n = 0; n < 256; n++) {
42
+ let c = n;
43
+ for (let k = 0; k < 8; k++)
44
+ c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
45
+ TABLE[n] = c >>> 0;
46
+ }
47
+ /** Continues a CRC-32, the one ZIP uses, over `bytes`, with a lookup table. Start from 0. */
48
+ function tableCrc32(bytes, crc = 0) {
49
+ let c = ~crc >>> 0;
50
+ for (const b of bytes)
51
+ c = TABLE[(c ^ b) & 0xff] ^ (c >>> 8);
52
+ return ~c >>> 0;
53
+ }
54
+ /**
55
+ * Continues a CRC-32, the one ZIP uses, over `bytes`. Start from 0. This is zlib.crc32 where Node has it, about 50
56
+ * times faster, and the table otherwise. Node 18 and early releases of Node 20 and 22 lack it. The choice is made
57
+ * once, when the module loads.
58
+ */
59
+ exports.crc32 = zlib.crc32 ?? tableCrc32;
@@ -0,0 +1,10 @@
1
+ import type { ByteSource, DetectOptions, FormatDetection } from './types';
2
+ /**
3
+ * Whether archive-defuse reads `source`, and in which format: what the inspection's own format selection picks, from
4
+ * the content alone, except that Brotli, which has no signature, counts only when the name or the declared type says
5
+ * Brotli. A format detected only to be rejected, such as RAR, is not claimed. A ZIP archive found from its end record starts where the ZIP reader finds its first entry, or at
6
+ * 0 when the reader cannot open it. The source stays open.
7
+ */
8
+ export declare function detectArchiveSource(source: ByteSource, options?: DetectOptions): Promise<FormatDetection | undefined>;
9
+ /** `detectArchiveSource` over bytes in memory. */
10
+ export declare function detectArchive(bytes: Uint8Array, options?: DetectOptions): Promise<FormatDetection | undefined>;
package/dist/detect.js ADDED
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.detectArchiveSource = detectArchiveSource;
4
+ exports.detectArchive = detectArchive;
5
+ const engine_1 = require("./engine");
6
+ const errors_1 = require("./errors");
7
+ const formats_1 = require("./formats");
8
+ const io_1 = require("./io");
9
+ const sniff_1 = require("./sniff");
10
+ const zip_1 = require("./zip");
11
+ /** Each format's MIME type and extension, the names sniff.ts gives them. */
12
+ const TYPES = {
13
+ zip: { mimeType: 'application/zip', extension: 'zip' },
14
+ tar: { mimeType: 'application/x-tar', extension: 'tar' },
15
+ gzip: { mimeType: 'application/gzip', extension: 'gz' },
16
+ bzip2: { mimeType: 'application/x-bzip2', extension: 'bz2' },
17
+ zstd: { mimeType: 'application/zstd', extension: 'zst' },
18
+ brotli: { mimeType: 'application/x-brotli', extension: 'br' },
19
+ cab: { mimeType: 'application/vnd.ms-cab-compressed', extension: 'cab' },
20
+ '7z': { mimeType: 'application/x-7z-compressed', extension: '7z' },
21
+ xz: { mimeType: 'application/x-xz', extension: 'xz' },
22
+ lzma: { mimeType: 'application/x-lzma', extension: 'lzma' },
23
+ };
24
+ /**
25
+ * `source` without its close(), checking the deadline before every call and keeping the first error any call throws.
26
+ * The Brotli test catches every error, so the kept one is how a failed read or the deadline still gets out.
27
+ */
28
+ function watch(source, deadline) {
29
+ const failed = {};
30
+ const keep = async (op) => {
31
+ try {
32
+ (0, errors_1.checkTime)(deadline);
33
+ return await op();
34
+ }
35
+ catch (e) {
36
+ failed.error ??= e;
37
+ throw e;
38
+ }
39
+ };
40
+ return { source: { size: () => keep(() => source.size()), read: (offset, length) => keep(() => source.read(offset, length)) }, failed };
41
+ }
42
+ const BROTLI_TYPES = new Set(['application/x-brotli', 'application/brotli']);
43
+ const saysBrotli = (o) => (0, sniff_1.typeFromName)(o.name) === 'application/x-brotli' || BROTLI_TYPES.has((o.declaredType ?? '').toLowerCase().split(';')[0].trim());
44
+ /**
45
+ * Whether archive-defuse reads `source`, and in which format: what the inspection's own format selection picks, from
46
+ * the content alone, except that Brotli, which has no signature, counts only when the name or the declared type says
47
+ * Brotli. A format detected only to be rejected, such as RAR, is not claimed. A ZIP archive found from its end record starts where the ZIP reader finds its first entry, or at
48
+ * 0 when the reader cannot open it. The source stays open.
49
+ */
50
+ async function detectArchiveSource(source, options = {}) {
51
+ const watched = watch(source, options.deadline);
52
+ const pass = (e) => {
53
+ if (watched.failed.error !== undefined)
54
+ throw watched.failed.error;
55
+ if ((0, errors_1.stopsTree)(e) || (0, errors_1.isIoError)(e))
56
+ throw e;
57
+ };
58
+ let picked;
59
+ try {
60
+ (0, errors_1.checkTime)(options.deadline);
61
+ picked = await (0, formats_1.select)(watched.source);
62
+ pass(undefined);
63
+ }
64
+ catch (e) {
65
+ pass(e);
66
+ // Any other failure, as in an inspection, means archive-defuse does not read the file.
67
+ return undefined;
68
+ }
69
+ if (picked === undefined)
70
+ return undefined;
71
+ // Brotli has no signature, and a one-byte file such as "7" decodes as an empty stream. Without a name or declared
72
+ // type that says Brotli, archive-defuse cannot know the file is one, so it does not claim it.
73
+ if (picked.format === 'brotli' && !saysBrotli(options))
74
+ return undefined;
75
+ let offset = picked.offset;
76
+ if (offset === undefined) {
77
+ try {
78
+ offset = (await zip_1.ZipArchive.open(watched.source, engine_1.DEFAULT_LIMITS, options.deadline)).start;
79
+ pass(undefined);
80
+ }
81
+ catch (e) {
82
+ pass(e);
83
+ // The selection picked ZIP, so the file is one, which the inspection rejects with its own finding.
84
+ offset = 0;
85
+ }
86
+ }
87
+ const found = { ...TYPES[picked.format], offset };
88
+ if (picked.heuristic !== undefined)
89
+ found.heuristic = picked.heuristic;
90
+ return found;
91
+ }
92
+ /** `detectArchiveSource` over bytes in memory. */
93
+ function detectArchive(bytes, options = {}) {
94
+ return detectArchiveSource((0, io_1.bufferSource)(bytes), options);
95
+ }
@@ -0,0 +1,44 @@
1
+ /** lc, lp and pb of everything this package encodes: lzma1's defaults, and xz's. */
2
+ export declare const LC = 3;
3
+ export declare const LP = 0;
4
+ export declare const PB = 2;
5
+ /** The properties byte of an LZMA header or 7z coder for LC, LP and PB. */
6
+ export declare const PROPS_BYTE: number;
7
+ /**
8
+ * The smallest dictionary that covers `size` bytes, at most 1 MiB and at least 4 KiB, rounded up to 2^n or 2^n +
9
+ * 2^(n-1) as decoders of the .lzma format expect.
10
+ */
11
+ export declare function fitDictSize(size: number): number;
12
+ /**
13
+ * Encodes raw LZMA data of a known size, without an end marker, with lzma1's encoder at its preset 1. The output is
14
+ * the range coder's bytes, as a 7z LZMA coder and the data of an .lzma file hold them.
15
+ */
16
+ export declare class RawLzmaEncoder {
17
+ private rc;
18
+ private readonly lzma;
19
+ readonly dictSize: number;
20
+ /** `size` is the input's whole length, or undefined to encode pieces with restart(). */
21
+ constructor(size: number | undefined, dictSize?: number);
22
+ /** Encodes `input` and returns the bytes ready so far. */
23
+ write(input: Uint8Array): Uint8Array;
24
+ /** Encodes what is left and returns the last bytes. */
25
+ finish(): Uint8Array;
26
+ /** Starts an independent input with the same encoder, whose tables need no new allocation. */
27
+ restart(): void;
28
+ }
29
+ /** Dictionary of the LZMA2 chunks below: every chunk resets it, and no chunk holds more than this. */
30
+ export declare const LZMA2_DICT_SIZE: number;
31
+ /** The LZMA2 properties byte for LZMA2_DICT_SIZE: (2 | (p & 1)) << (p / 2 + 11) is 64 KiB for 8. */
32
+ export declare const LZMA2_DICT_PROP = 8;
33
+ /**
34
+ * Writes raw LZMA2 data as independent chunks of at most 64 KiB, each compressed by the LZMA1 encoder above, or
35
+ * stored when that does not make it smaller. Every chunk resets the dictionary and the state, which LZMA2 allows, so
36
+ * the encoder needs no knowledge of LZMA2. `forceCompressed` keeps a chunk compressed even when that is larger, so the
37
+ * stored bytes of the content do not show at the start of a file, where other readers look for their own signatures.
38
+ */
39
+ export declare class Lzma2ChunkWriter {
40
+ private readonly encoder;
41
+ private used;
42
+ /** The chunks for one piece of at most 64 KiB. With `store`, a piece not forced compressed is stored as it is. */
43
+ chunk(piece: Uint8Array, forceCompressed: boolean, store?: boolean): Uint8Array;
44
+ }