@patrickdk77/archive-defuse 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +345 -2
- package/dist/aes7z.d.ts +16 -0
- package/dist/aes7z.js +131 -0
- package/dist/alone.d.ts +21 -0
- package/dist/alone.js +65 -0
- package/dist/bcj2.d.ts +21 -0
- package/dist/bcj2.js +140 -0
- package/dist/bzip2.d.ts +21 -0
- package/dist/bzip2.js +688 -0
- package/dist/cab.d.ts +63 -0
- package/dist/cab.js +493 -0
- package/dist/checks.d.ts +23 -0
- package/dist/checks.js +123 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +364 -0
- package/dist/codecs.d.ts +39 -0
- package/dist/codecs.js +124 -0
- package/dist/coders.d.ts +58 -0
- package/dist/coders.js +392 -0
- package/dist/crc32.d.ts +8 -0
- package/dist/crc32.js +59 -0
- package/dist/encoder.d.ts +44 -0
- package/dist/encoder.js +114 -0
- package/dist/engine.d.ts +44 -0
- package/dist/engine.js +753 -0
- package/dist/errors.d.ts +64 -0
- package/dist/errors.js +98 -0
- package/dist/filters.d.ts +34 -0
- package/dist/filters.js +385 -0
- package/dist/findings.d.ts +26 -0
- package/dist/findings.js +176 -0
- package/dist/formats.d.ts +14 -0
- package/dist/formats.js +97 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +50 -0
- package/dist/index.mjs +3 -0
- package/dist/inflate64.d.ts +6 -0
- package/dist/inflate64.js +388 -0
- package/dist/io.d.ts +78 -0
- package/dist/io.js +379 -0
- package/dist/lzma-formats.d.ts +75 -0
- package/dist/lzma-formats.js +373 -0
- package/dist/lzma-sniff.d.ts +20 -0
- package/dist/lzma-sniff.js +73 -0
- package/dist/lzma-writers.d.ts +35 -0
- package/dist/lzma-writers.js +276 -0
- package/dist/lzma1/LICENSE +201 -0
- package/dist/lzma1/NOTICE +36 -0
- package/dist/lzma1/crc32.d.ts +4 -0
- package/dist/lzma1/crc32.js +19 -0
- package/dist/lzma1/hash234.d.ts +22 -0
- package/dist/lzma1/hash234.js +75 -0
- package/dist/lzma1/hc4.d.ts +30 -0
- package/dist/lzma1/hc4.js +142 -0
- package/dist/lzma1/header.d.ts +32 -0
- package/dist/lzma1/header.js +73 -0
- package/dist/lzma1/lz-decoder.d.ts +41 -0
- package/dist/lzma1/lz-decoder.js +128 -0
- package/dist/lzma1/lz-encoder.d.ts +87 -0
- package/dist/lzma1/lz-encoder.js +166 -0
- package/dist/lzma1/lzma-coder.d.ts +107 -0
- package/dist/lzma1/lzma-coder.js +162 -0
- package/dist/lzma1/lzma-decoder.d.ts +36 -0
- package/dist/lzma1/lzma-decoder.js +484 -0
- package/dist/lzma1/lzma-encoder-fast.d.ts +13 -0
- package/dist/lzma1/lzma-encoder-fast.js +114 -0
- package/dist/lzma1/lzma-encoder.d.ts +105 -0
- package/dist/lzma1/lzma-encoder.js +497 -0
- package/dist/lzma1/lzma2-decoder.d.ts +85 -0
- package/dist/lzma1/lzma2-decoder.js +249 -0
- package/dist/lzma1/range-coder.d.ts +18 -0
- package/dist/lzma1/range-coder.js +23 -0
- package/dist/lzma1/range-decoder.d.ts +41 -0
- package/dist/lzma1/range-decoder.js +78 -0
- package/dist/lzma1/range-encoder.d.ts +53 -0
- package/dist/lzma1/range-encoder.js +269 -0
- package/dist/lzma1-stream.d.ts +60 -0
- package/dist/lzma1-stream.js +202 -0
- package/dist/model.d.ts +141 -0
- package/dist/model.js +16 -0
- package/dist/names.d.ts +13 -0
- package/dist/names.js +66 -0
- package/dist/pack.d.ts +72 -0
- package/dist/pack.js +514 -0
- package/dist/plugins.d.ts +13 -0
- package/dist/plugins.js +55 -0
- package/dist/rules.d.ts +25 -0
- package/dist/rules.js +403 -0
- package/dist/score.d.ts +17 -0
- package/dist/score.js +58 -0
- package/dist/seven.d.ts +75 -0
- package/dist/seven.js +492 -0
- package/dist/sevenzip.d.ts +133 -0
- package/dist/sevenzip.js +529 -0
- package/dist/single.d.ts +103 -0
- package/dist/single.js +453 -0
- package/dist/sniff.d.ts +20 -0
- package/dist/sniff.js +224 -0
- package/dist/stream-meter.d.ts +39 -0
- package/dist/stream-meter.js +54 -0
- package/dist/tar.d.ts +102 -0
- package/dist/tar.js +765 -0
- package/dist/types.d.ts +284 -0
- package/dist/types.js +133 -0
- package/dist/unpack.d.ts +67 -0
- package/dist/unpack.js +194 -0
- package/dist/xz.d.ts +25 -0
- package/dist/xz.js +333 -0
- package/dist/zip-lzma.d.ts +15 -0
- package/dist/zip-lzma.js +137 -0
- package/dist/zip-writer.d.ts +41 -0
- package/dist/zip-writer.js +263 -0
- package/dist/zip.d.ts +99 -0
- package/dist/zip.js +901 -0
- package/dist/zipcrypt.d.ts +47 -0
- package/dist/zipcrypt.js +233 -0
- package/dist/zstd.d.ts +25 -0
- package/dist/zstd.js +160 -0
- package/package.json +94 -4
package/dist/sniff.js
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isTarHeader = isTarHeader;
|
|
4
|
+
exports.sniffType = sniffType;
|
|
5
|
+
exports.family = family;
|
|
6
|
+
exports.extensionOf = extensionOf;
|
|
7
|
+
exports.typeFromName = typeFromName;
|
|
8
|
+
exports.typesDisagree = typesDisagree;
|
|
9
|
+
// Copied from pdf-defuse src/sniff.ts. Changed: sniffType tries the signatures at the start of the file before a PDF
|
|
10
|
+
// header further in, as file-defuse does, and knows the archive formats and RTF. The extension table and the families
|
|
11
|
+
// know them too. extensionOf trims trailing dots and spaces with a loop, since /[. ]+$/ backtracks on long runs.
|
|
12
|
+
const lzma_sniff_1 = require("./lzma-sniff");
|
|
13
|
+
const names_1 = require("./names");
|
|
14
|
+
/** The value of an octal field of a tar header: digits after any spaces, up to a space or NUL. */
|
|
15
|
+
function octal(b, start, length) {
|
|
16
|
+
let i = start;
|
|
17
|
+
const end = start + length;
|
|
18
|
+
while (i < end && b[i] === 0x20)
|
|
19
|
+
i++;
|
|
20
|
+
let v = 0;
|
|
21
|
+
let digits = 0;
|
|
22
|
+
for (; i < end && b[i] >= 0x30 && b[i] <= 0x37; i++, digits++)
|
|
23
|
+
v = v * 8 + (b[i] - 0x30);
|
|
24
|
+
if (digits === 0)
|
|
25
|
+
return undefined;
|
|
26
|
+
for (; i < end; i++)
|
|
27
|
+
if (b[i] !== 0x20 && b[i] !== 0)
|
|
28
|
+
return undefined;
|
|
29
|
+
return v;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* True when `block` is a tar header: its checksum field matches the sum of its bytes, counted unsigned or, as some old
|
|
33
|
+
* writers did, signed, with the field itself counted as spaces. An all-zero block is the end of an archive, not a header.
|
|
34
|
+
*/
|
|
35
|
+
function isTarHeader(block) {
|
|
36
|
+
if (block.length < 512)
|
|
37
|
+
return false;
|
|
38
|
+
const stored = octal(block, 148, 8);
|
|
39
|
+
if (stored === undefined)
|
|
40
|
+
return false;
|
|
41
|
+
let unsigned = 0;
|
|
42
|
+
let signed = 0;
|
|
43
|
+
for (let i = 0; i < 512; i++) {
|
|
44
|
+
const b = i >= 148 && i < 156 ? 0x20 : block[i];
|
|
45
|
+
unsigned += b;
|
|
46
|
+
signed += b > 127 ? b - 256 : b;
|
|
47
|
+
}
|
|
48
|
+
return unsigned !== 8 * 0x20 && (stored === unsigned || stored === signed);
|
|
49
|
+
}
|
|
50
|
+
const at = (b, offset, bytes) => bytes.every((x, i) => b[offset + i] === x);
|
|
51
|
+
/** Identifies a file type from its first bytes. Returns a MIME type or undefined. */
|
|
52
|
+
function sniffType(head) {
|
|
53
|
+
const b = Buffer.from(head.buffer, head.byteOffset, head.byteLength);
|
|
54
|
+
const s = b.subarray(0, 1024).toString('latin1');
|
|
55
|
+
if (s.startsWith('%PDF-'))
|
|
56
|
+
return 'application/pdf';
|
|
57
|
+
if (b[0] === 0x50 && b[1] === 0x4b && (b[2] === 0x03 || b[2] === 0x05 || b[2] === 0x07))
|
|
58
|
+
return 'application/zip';
|
|
59
|
+
if (b[0] === 0x1f && b[1] === 0x8b)
|
|
60
|
+
return 'application/gzip';
|
|
61
|
+
if (s.startsWith('BZh') && b[3] >= 0x31 && b[3] <= 0x39)
|
|
62
|
+
return 'application/x-bzip2';
|
|
63
|
+
if (at(b, 0, [0x28, 0xb5, 0x2f, 0xfd]) || (at(b, 1, [0x2a, 0x4d, 0x18]) && (b[0] & 0xf0) === 0x50))
|
|
64
|
+
return 'application/zstd';
|
|
65
|
+
if (s.startsWith('MSCF\0\0\0\0'))
|
|
66
|
+
return 'application/vnd.ms-cab-compressed';
|
|
67
|
+
if (s.startsWith('{\\rt'))
|
|
68
|
+
return 'application/rtf';
|
|
69
|
+
if (at(b, 0, [0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c]))
|
|
70
|
+
return 'application/x-7z-compressed';
|
|
71
|
+
if (at(b, 0, [0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00]))
|
|
72
|
+
return 'application/x-xz';
|
|
73
|
+
if (b[0] === 0x89 && s.startsWith('\x89PNG'))
|
|
74
|
+
return 'image/png';
|
|
75
|
+
if (b[0] === 0xff && b[1] === 0xd8 && b[2] === 0xff)
|
|
76
|
+
return 'image/jpeg';
|
|
77
|
+
if (s.startsWith('GIF87a') || s.startsWith('GIF89a'))
|
|
78
|
+
return 'image/gif';
|
|
79
|
+
if (b[0] === 0xd0 && b[1] === 0xcf && b[2] === 0x11 && b[3] === 0xe0)
|
|
80
|
+
return 'application/x-ole-storage';
|
|
81
|
+
if (s.startsWith('MZ'))
|
|
82
|
+
return 'application/x-msdownload';
|
|
83
|
+
if (s.startsWith('\x7fELF'))
|
|
84
|
+
return 'application/x-executable';
|
|
85
|
+
if (isTarHeader(b))
|
|
86
|
+
return 'application/x-tar';
|
|
87
|
+
// Zeros pass the .lzma header test, and zero padding is no format.
|
|
88
|
+
if ((0, lzma_sniff_1.looksLikeLzma)(b) && !b.subarray(0, 13).every(x => x === 0))
|
|
89
|
+
return 'application/x-lzma';
|
|
90
|
+
// PDF readers accept the header anywhere in the first 1024 bytes.
|
|
91
|
+
if (s.includes('%PDF-'))
|
|
92
|
+
return 'application/pdf';
|
|
93
|
+
const text = s.replace(/^\xef\xbb\xbf/, '').trimStart();
|
|
94
|
+
if (/^<svg[\s>/]/i.test(text) || (/^<\?xml/i.test(text) && /<svg[\s>/]/i.test(s)))
|
|
95
|
+
return 'image/svg+xml';
|
|
96
|
+
if (/^<\?xml/i.test(text))
|
|
97
|
+
return 'application/xml';
|
|
98
|
+
if (/^<!doctype html|^<html[\s>]/i.test(text))
|
|
99
|
+
return 'text/html';
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
const EXT = {
|
|
103
|
+
pdf: 'application/pdf',
|
|
104
|
+
zip: 'application/zip',
|
|
105
|
+
docx: 'application/zip',
|
|
106
|
+
xlsx: 'application/zip',
|
|
107
|
+
pptx: 'application/zip',
|
|
108
|
+
odt: 'application/zip',
|
|
109
|
+
ods: 'application/zip',
|
|
110
|
+
odp: 'application/zip',
|
|
111
|
+
epub: 'application/zip',
|
|
112
|
+
jar: 'application/zip',
|
|
113
|
+
png: 'image/png',
|
|
114
|
+
jpg: 'image/jpeg',
|
|
115
|
+
jpeg: 'image/jpeg',
|
|
116
|
+
gif: 'image/gif',
|
|
117
|
+
svg: 'image/svg+xml',
|
|
118
|
+
xml: 'application/xml',
|
|
119
|
+
html: 'text/html',
|
|
120
|
+
htm: 'text/html',
|
|
121
|
+
doc: 'application/x-ole-storage',
|
|
122
|
+
xls: 'application/x-ole-storage',
|
|
123
|
+
ppt: 'application/x-ole-storage',
|
|
124
|
+
msg: 'application/x-ole-storage',
|
|
125
|
+
exe: 'application/x-msdownload',
|
|
126
|
+
dll: 'application/x-msdownload',
|
|
127
|
+
csv: 'text/csv',
|
|
128
|
+
tsv: 'text/tab-separated-values',
|
|
129
|
+
tab: 'text/tab-separated-values',
|
|
130
|
+
json: 'application/json',
|
|
131
|
+
gz: 'application/gzip',
|
|
132
|
+
tgz: 'application/gzip',
|
|
133
|
+
bz2: 'application/x-bzip2',
|
|
134
|
+
tbz: 'application/x-bzip2',
|
|
135
|
+
tbz2: 'application/x-bzip2',
|
|
136
|
+
zst: 'application/zstd',
|
|
137
|
+
tzst: 'application/zstd',
|
|
138
|
+
tar: 'application/x-tar',
|
|
139
|
+
cab: 'application/vnd.ms-cab-compressed',
|
|
140
|
+
'7z': 'application/x-7z-compressed',
|
|
141
|
+
xz: 'application/x-xz',
|
|
142
|
+
txz: 'application/x-xz',
|
|
143
|
+
lzma: 'application/x-lzma',
|
|
144
|
+
tlz: 'application/x-lzma',
|
|
145
|
+
br: 'application/x-brotli',
|
|
146
|
+
rtf: 'application/rtf',
|
|
147
|
+
};
|
|
148
|
+
/** Maps MIME types that share a container format, and common aliases of one type, onto one family. */
|
|
149
|
+
function family(mime) {
|
|
150
|
+
const m = mime.toLowerCase().split(';')[0].trim();
|
|
151
|
+
if (m === 'image/jpg' || m === 'image/pjpeg')
|
|
152
|
+
return 'image/jpeg';
|
|
153
|
+
if (m === 'image/x-png')
|
|
154
|
+
return 'image/png';
|
|
155
|
+
if (m === 'application/x-pdf')
|
|
156
|
+
return 'application/pdf';
|
|
157
|
+
if (m === 'application/zip' ||
|
|
158
|
+
m === 'application/x-zip-compressed' ||
|
|
159
|
+
m === 'application/x-zip' ||
|
|
160
|
+
m.includes('openxmlformats') ||
|
|
161
|
+
m.includes('opendocument') ||
|
|
162
|
+
m === 'application/epub+zip' ||
|
|
163
|
+
m === 'application/java-archive')
|
|
164
|
+
return 'zip';
|
|
165
|
+
if (m === 'application/x-gzip' || m === 'application/gzip')
|
|
166
|
+
return 'gzip';
|
|
167
|
+
if (m === 'application/x-bzip2' || m === 'application/x-bzip')
|
|
168
|
+
return 'bzip2';
|
|
169
|
+
if (m === 'application/zstd' || m === 'application/x-zstd')
|
|
170
|
+
return 'zstd';
|
|
171
|
+
if (m === 'image/svg+xml')
|
|
172
|
+
return 'svg';
|
|
173
|
+
if (m === 'application/xml' || m === 'text/xml')
|
|
174
|
+
return 'xml';
|
|
175
|
+
if (m === 'application/msword' || m === 'application/vnd.ms-excel' || m === 'application/vnd.ms-powerpoint' || m === 'application/x-ole-storage' || m === 'application/vnd.ms-outlook')
|
|
176
|
+
return 'ole';
|
|
177
|
+
return m;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* A file name's extension, lowercased. Windows drops trailing dots and spaces when it saves a file, so the extension
|
|
181
|
+
* of "page.html." is html.
|
|
182
|
+
*/
|
|
183
|
+
function extensionOf(name) {
|
|
184
|
+
return /\.([^./\\]+)$/.exec((0, names_1.trimEnd)(name ?? '', '. '))?.[1].toLowerCase();
|
|
185
|
+
}
|
|
186
|
+
function typeFromName(name) {
|
|
187
|
+
const ext = extensionOf(name);
|
|
188
|
+
// Only the table's own entries: "x.constructor" names no type.
|
|
189
|
+
return ext !== undefined && Object.hasOwn(EXT, ext) ? EXT[ext] : undefined;
|
|
190
|
+
}
|
|
191
|
+
/** Families sniffType has a signature for. Content that claims one of them and matches no signature is not what it claims. */
|
|
192
|
+
const SNIFFABLE = new Set([
|
|
193
|
+
'application/pdf',
|
|
194
|
+
'zip',
|
|
195
|
+
'image/png',
|
|
196
|
+
'image/jpeg',
|
|
197
|
+
'image/gif',
|
|
198
|
+
'ole',
|
|
199
|
+
'application/x-msdownload',
|
|
200
|
+
'application/x-executable',
|
|
201
|
+
'svg',
|
|
202
|
+
'xml',
|
|
203
|
+
'text/html',
|
|
204
|
+
'gzip',
|
|
205
|
+
'bzip2',
|
|
206
|
+
'zstd',
|
|
207
|
+
'application/x-tar',
|
|
208
|
+
'application/vnd.ms-cab-compressed',
|
|
209
|
+
'application/x-7z-compressed',
|
|
210
|
+
'application/x-xz',
|
|
211
|
+
'application/x-lzma',
|
|
212
|
+
]);
|
|
213
|
+
/**
|
|
214
|
+
* True when any two of the declared type, the name's extension and the sniffed content contradict each other, or
|
|
215
|
+
* when the declared type or the name claims a type sniffType recognizes and the content matched no signature.
|
|
216
|
+
*/
|
|
217
|
+
function typesDisagree(declared, name, sniffed) {
|
|
218
|
+
const claimed = [declared, typeFromName(name)].filter((t) => t !== undefined && t !== '' && family(t) !== 'application/octet-stream').map(family);
|
|
219
|
+
const all = sniffed ? [...claimed, family(sniffed)] : claimed;
|
|
220
|
+
const same = (a, b) => a === b || (a === 'svg' && b === 'xml') || (a === 'xml' && b === 'svg');
|
|
221
|
+
if (all.some(a => all.some(b => !same(a, b))))
|
|
222
|
+
return true;
|
|
223
|
+
return !sniffed && claimed.some(f => SNIFFABLE.has(f));
|
|
224
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** The limits the xz, lzma and 7z readers check, with their defaults filled in. */
|
|
2
|
+
export interface Limits {
|
|
3
|
+
entries: number;
|
|
4
|
+
decompressedBytes: number;
|
|
5
|
+
totalDecompressedBytes: number;
|
|
6
|
+
compressionRatio: number;
|
|
7
|
+
dictionaryBytes: number;
|
|
8
|
+
}
|
|
9
|
+
/** What one decode has produced and the compressed bytes it took, shared by its meters. */
|
|
10
|
+
export interface Totals {
|
|
11
|
+
decoded: number;
|
|
12
|
+
compressed: number;
|
|
13
|
+
}
|
|
14
|
+
/** What a reader counts a stream through: the deadline, the compressed bytes it takes and the bytes it decodes. */
|
|
15
|
+
export interface StreamCounter {
|
|
16
|
+
readonly decoded: number;
|
|
17
|
+
readonly compressed: number;
|
|
18
|
+
/** Checks the deadline. */
|
|
19
|
+
tick(): void;
|
|
20
|
+
packed(n: number): void;
|
|
21
|
+
add(n: number): void;
|
|
22
|
+
}
|
|
23
|
+
/** Counts what one stream decodes against the limits, and checks the deadline. */
|
|
24
|
+
export declare class Meter implements StreamCounter {
|
|
25
|
+
private readonly limits;
|
|
26
|
+
private readonly totals;
|
|
27
|
+
private readonly deadline;
|
|
28
|
+
/** Set for a stream whose size counts against `decompressedBytes`, such as an entry; unset for a solid 7z folder. */
|
|
29
|
+
private readonly single;
|
|
30
|
+
private readonly location?;
|
|
31
|
+
decoded: number;
|
|
32
|
+
compressed: number;
|
|
33
|
+
constructor(limits: Limits, totals: Totals, deadline: number | undefined,
|
|
34
|
+
/** Set for a stream whose size counts against `decompressedBytes`, such as an entry; unset for a solid 7z folder. */
|
|
35
|
+
single: boolean, location?: string | undefined);
|
|
36
|
+
tick(): void;
|
|
37
|
+
packed(n: number): void;
|
|
38
|
+
add(n: number): void;
|
|
39
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Meter = void 0;
|
|
4
|
+
// Copied from lzma-defuse src/meter.ts when it merged into this package. Changed: the limits come from the engine, and
|
|
5
|
+
// StreamCounter is the shape the xz, lzma and 7z readers count through, so the engine's own meter can stand in.
|
|
6
|
+
const errors_1 = require("./errors");
|
|
7
|
+
const types_1 = require("./types");
|
|
8
|
+
/** The ratio limit applies once a stream has decoded this much, so small files with long runs pass. */
|
|
9
|
+
const RATIO_FLOOR = 1024 * 1024;
|
|
10
|
+
/** Counts what one stream decodes against the limits, and checks the deadline. */
|
|
11
|
+
class Meter {
|
|
12
|
+
limits;
|
|
13
|
+
totals;
|
|
14
|
+
deadline;
|
|
15
|
+
single;
|
|
16
|
+
location;
|
|
17
|
+
decoded = 0;
|
|
18
|
+
compressed = 0;
|
|
19
|
+
constructor(limits, totals, deadline,
|
|
20
|
+
/** Set for a stream whose size counts against `decompressedBytes`, such as an entry; unset for a solid 7z folder. */
|
|
21
|
+
single, location) {
|
|
22
|
+
this.limits = limits;
|
|
23
|
+
this.totals = totals;
|
|
24
|
+
this.deadline = deadline;
|
|
25
|
+
this.single = single;
|
|
26
|
+
this.location = location;
|
|
27
|
+
}
|
|
28
|
+
tick() {
|
|
29
|
+
(0, errors_1.checkTime)(this.deadline);
|
|
30
|
+
}
|
|
31
|
+
packed(n) {
|
|
32
|
+
this.compressed += n;
|
|
33
|
+
this.totals.compressed += n;
|
|
34
|
+
}
|
|
35
|
+
add(n) {
|
|
36
|
+
this.decoded += n;
|
|
37
|
+
this.totals.decoded += n;
|
|
38
|
+
const loc = this.location;
|
|
39
|
+
if (this.single && this.decoded > this.limits.decompressedBytes) {
|
|
40
|
+
throw new errors_1.StopError(types_1.ArchiveCategory.Limit, types_1.ArchiveDetail.DecompressedSize, 'Decompressed size limit exceeded', { limit: this.limits.decompressedBytes }, loc);
|
|
41
|
+
}
|
|
42
|
+
if (this.totals.decoded > this.limits.totalDecompressedBytes) {
|
|
43
|
+
throw new errors_1.StopError(types_1.ArchiveCategory.Limit, types_1.ArchiveDetail.TotalDecompressedSize, 'Total decompressed size limit exceeded', { limit: this.limits.totalDecompressedBytes }, loc);
|
|
44
|
+
}
|
|
45
|
+
// The ratio of the whole decode, so one long run of zeros among other files is judged with them, as a solid
|
|
46
|
+
// folder is.
|
|
47
|
+
const t = this.totals;
|
|
48
|
+
if (t.decoded > RATIO_FLOOR && t.decoded > this.limits.compressionRatio * Math.max(1, t.compressed)) {
|
|
49
|
+
throw new errors_1.StopError(types_1.ArchiveCategory.Limit, types_1.ArchiveDetail.Ratio, 'Compression ratio limit exceeded', { limit: this.limits.compressionRatio }, loc);
|
|
50
|
+
}
|
|
51
|
+
(0, errors_1.checkTime)(this.deadline);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.Meter = Meter;
|
package/dist/tar.d.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { Reader } from './io';
|
|
2
|
+
import type { Archive, Entry, Meter, Note, WriteContext, WritePlan } from './model';
|
|
3
|
+
import { type ByteSink, type ByteSource } from './types';
|
|
4
|
+
export declare const BLOCK = 512;
|
|
5
|
+
/** GNU tar's default record: the archive is padded with zeros to a multiple of it. */
|
|
6
|
+
export declare const RECORD: number;
|
|
7
|
+
/** A pax record kept as written: its key, and its value's bytes. */
|
|
8
|
+
export interface PaxRecord {
|
|
9
|
+
key: string;
|
|
10
|
+
value: Buffer;
|
|
11
|
+
}
|
|
12
|
+
export interface TarEntry extends Entry {
|
|
13
|
+
headerOffset: number;
|
|
14
|
+
dataOffset: number;
|
|
15
|
+
/** Bytes of data the archive stores for the entry. For a sparse file, the data segments without the holes. */
|
|
16
|
+
stored: number;
|
|
17
|
+
typeflag: string;
|
|
18
|
+
uid: number;
|
|
19
|
+
gid: number;
|
|
20
|
+
uname: string;
|
|
21
|
+
gname: string;
|
|
22
|
+
mtime: number;
|
|
23
|
+
devmajor: number;
|
|
24
|
+
devminor: number;
|
|
25
|
+
/** Pax records the rewrite keeps as they are: extended attributes, access and change times, vendor records. */
|
|
26
|
+
pax: PaxRecord[];
|
|
27
|
+
/** A sparse file's data segments, as offset and length in the whole file. */
|
|
28
|
+
sparse?: Array<[number, number]>;
|
|
29
|
+
/** Bytes at the start of the data that hold a pax 1.0 sparse map. */
|
|
30
|
+
sparseMapBytes?: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* A numeric field: octal digits after any spaces or NULs, up to a space or NUL, or GNU's base-256 with the top bit of
|
|
34
|
+
* the first byte set. Undefined when the field holds anything else or a value beyond a safe integer.
|
|
35
|
+
*/
|
|
36
|
+
export declare function numeric(b: Uint8Array, at: number, length: number): number | undefined;
|
|
37
|
+
/** Parses pax records, "<length> <key>=<value>\n". Returns them in order, with the reason for any fault that stopped it. */
|
|
38
|
+
export declare function parsePax(data: Uint8Array): {
|
|
39
|
+
records: PaxRecord[];
|
|
40
|
+
malformed?: string;
|
|
41
|
+
};
|
|
42
|
+
/** A tar archive read header by header. */
|
|
43
|
+
export declare class TarArchive implements Archive<TarEntry> {
|
|
44
|
+
readonly reader: Reader;
|
|
45
|
+
readonly entries: TarEntry[];
|
|
46
|
+
readonly notes: Note[];
|
|
47
|
+
/** Where the end-of-archive blocks start, or the archive's data ends. */
|
|
48
|
+
readonly end: number;
|
|
49
|
+
/** Where the data after the archive starts. */
|
|
50
|
+
readonly trailingStart: number;
|
|
51
|
+
/** The volume label, from a GNU 'V' header. */
|
|
52
|
+
readonly label?: string | undefined;
|
|
53
|
+
readonly format = "tar";
|
|
54
|
+
readonly single = false;
|
|
55
|
+
readonly lateNotes: Note[];
|
|
56
|
+
comment?: string;
|
|
57
|
+
private constructor();
|
|
58
|
+
static open(source: ByteSource, limits: {
|
|
59
|
+
entries: number;
|
|
60
|
+
decompressedBytes: number;
|
|
61
|
+
totalDecompressedBytes: number;
|
|
62
|
+
}, deadline?: number): Promise<TarArchive>;
|
|
63
|
+
/** The offset of the first byte that is not zero, from `from`. */
|
|
64
|
+
private static firstNonZero;
|
|
65
|
+
/** True when a tar header sits at a block boundary from `from` on. */
|
|
66
|
+
private static headerAfter;
|
|
67
|
+
/** Reads a sparse file's map: GNU's old header and its extensions, or pax 0.0, 0.1 or 1.0. */
|
|
68
|
+
private static sparse;
|
|
69
|
+
unlock(): Promise<boolean>;
|
|
70
|
+
hasMetadata(): boolean;
|
|
71
|
+
/** The entry's stored data. */
|
|
72
|
+
raw(e: TarEntry): AsyncGenerator<Uint8Array>;
|
|
73
|
+
read(e: TarEntry, meter: Meter): AsyncGenerator<Uint8Array>;
|
|
74
|
+
write(plan: WritePlan<TarEntry>, sink: ByteSink, context: WriteContext): Promise<void>;
|
|
75
|
+
trailing(): AsyncGenerator<Uint8Array>;
|
|
76
|
+
}
|
|
77
|
+
/** One pax record, whose length counts its own digits. */
|
|
78
|
+
export declare function paxRecord(key: string, value: Buffer): Buffer;
|
|
79
|
+
/** A header block. */
|
|
80
|
+
export declare function header(f: {
|
|
81
|
+
name: Buffer;
|
|
82
|
+
mode: number;
|
|
83
|
+
uid: number;
|
|
84
|
+
gid: number;
|
|
85
|
+
size: number;
|
|
86
|
+
mtime: number;
|
|
87
|
+
typeflag: string;
|
|
88
|
+
linkname: Buffer;
|
|
89
|
+
uname: string;
|
|
90
|
+
gname: string;
|
|
91
|
+
prefix: Buffer;
|
|
92
|
+
devmajor: number;
|
|
93
|
+
devminor: number;
|
|
94
|
+
}): Buffer;
|
|
95
|
+
/** A name in ASCII for the header itself, for readers without pax support. The pax record holds the real one. */
|
|
96
|
+
export declare const asciiFallback: (s: string) => Buffer<ArrayBuffer>;
|
|
97
|
+
/** The name split across the ustar prefix and name fields, or undefined when it does not fit. */
|
|
98
|
+
export declare function split(name: string): {
|
|
99
|
+
prefix: Buffer;
|
|
100
|
+
name: Buffer;
|
|
101
|
+
} | undefined;
|
|
102
|
+
export declare const isTar: (head: Uint8Array) => boolean;
|