@patrickdk77/archive-defuse 0.1.2 → 0.1.3

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.
@@ -1,41 +1,68 @@
1
1
  "use strict";
2
+ // Copied from lzma1 0.6.1 (Apache-2.0, Copyright Filip Seman) and changed for archive-defuse. NOTICE in this folder lists the changes.
3
+ /**
4
+ * Range encoder writing to its own growable buffer.
5
+ *
6
+ * Ported from XZ for Java (0BSD), `src/org/tukaani/xz/rangecoder/RangeEncoder.java`
7
+ * and `RangeEncoderToBuffer.java`, by Lasse Collin and Igor Pavlov.
8
+ *
9
+ * Integer adaptations (see the hazard checklist in `docs/verification.md`):
10
+ * - `low` needs 33 bits (32 bits plus a carry), so it is a regular number;
11
+ * doubles represent integers up to 2^53 exactly. The carry test uses
12
+ * division instead of `low >>> 32`.
13
+ * - `range` is an unsigned 32-bit value in a regular number: `<<` is
14
+ * followed by `>>> 0`, and `(range & TOP_MASK) == 0` becomes an
15
+ * unsigned `range < TOP_VALUE`.
16
+ * - Bitwise ops on values that may exceed 2^31 are kept off the signed
17
+ * int32 path (`low`'s 24-bit wrap multiplies by 256 instead of
18
+ * shifting).
19
+ */
2
20
  Object.defineProperty(exports, "__esModule", { value: true });
3
21
  exports.RangeEncoder = void 0;
22
+ exports.createPriceTable = createPriceTable;
4
23
  exports.getBitPrice = getBitPrice;
5
24
  exports.getBitTreePrice = getBitTreePrice;
6
25
  exports.getReverseBitTreePrice = getReverseBitTreePrice;
7
26
  exports.getDirectBitsPrice = getDirectBitsPrice;
8
- // Copied from lzma1 (Apache-2.0, Copyright Filip Seman) and changed for lzma-defuse. NOTICE in this folder lists the changes.
9
27
  const range_coder_1 = require("./range-coder");
10
28
  const MOVE_REDUCING_BITS = 4;
11
29
  const BIT_PRICE_SHIFT_BITS = 4;
12
- /**
13
- * Price (approximate cost in 1/16 bits) of encoding a bit with a given
14
- * probability, indexed by `probability >>> MOVE_REDUCING_BITS`.
15
- */
16
- const PRICES = createPriceTable();
30
+ // Bit prices in 1/16 bits: the price of a 0 bit, then the same prices
31
+ // reversed, which are the prices of a 1 bit, at
32
+ // (bit << 7) | (prob >>> MOVE_REDUCING_BITS). One lookup without the Java
33
+ // code's bit flip was measured faster.
34
+ const bitPrices = createBitPriceTable();
35
+ function createBitPriceTable() {
36
+ const prices = createPriceTable();
37
+ const half = prices.length;
38
+ const table = new Uint32Array(2 * half);
39
+ for (let i = 0; i < half; ++i) {
40
+ table[i] = prices[i];
41
+ table[half + i] = prices[half - 1 - i];
42
+ }
43
+ return table;
44
+ }
17
45
  function createPriceTable() {
18
- const prices = new Uint32Array(range_coder_1.BIT_MODEL_TOTAL >>> MOVE_REDUCING_BITS);
46
+ const table = new Uint32Array(range_coder_1.BIT_MODEL_TOTAL >>> MOVE_REDUCING_BITS);
19
47
  for (let i = (1 << MOVE_REDUCING_BITS) / 2; i < range_coder_1.BIT_MODEL_TOTAL; i += 1 << MOVE_REDUCING_BITS) {
20
- // Approximate -log2(i / BIT_MODEL_TOTAL) using repeated squaring.
21
48
  let w = i;
22
49
  let bitCount = 0;
23
50
  for (let j = 0; j < BIT_PRICE_SHIFT_BITS; ++j) {
24
51
  w *= w;
25
52
  bitCount <<= 1;
26
- while (w >= 0x10000) {
53
+ while ((w & 0xffff0000) !== 0) {
27
54
  w >>>= 1;
28
55
  ++bitCount;
29
56
  }
30
57
  }
31
- prices[i >> MOVE_REDUCING_BITS] = (range_coder_1.BIT_MODEL_TOTAL_BITS << BIT_PRICE_SHIFT_BITS) - 15 - bitCount;
58
+ table[i >> MOVE_REDUCING_BITS] = (range_coder_1.BIT_MODEL_TOTAL_BITS << BIT_PRICE_SHIFT_BITS) - 15 - bitCount;
32
59
  }
33
- return prices;
60
+ return table;
34
61
  }
35
62
  function getBitPrice(prob, bit) {
36
- return PRICES[(prob ^ (-bit & (range_coder_1.BIT_MODEL_TOTAL - 1))) >>> MOVE_REDUCING_BITS];
63
+ // NOTE: Unlike in encodeBit(), here bit must be 0 or 1.
64
+ return bitPrices[(bit << 7) | (prob >>> MOVE_REDUCING_BITS)];
37
65
  }
38
- /** Price of a `bits`-bit symbol coded MSB-first with the bit tree at `offset`. */
39
66
  function getBitTreePrice(probs, offset, bits, symbolIn) {
40
67
  let symbol = symbolIn;
41
68
  let price = 0;
@@ -47,42 +74,47 @@ function getBitTreePrice(probs, offset, bits, symbolIn) {
47
74
  } while (symbol !== 1);
48
75
  return price;
49
76
  }
50
- /** Price of a `bits`-bit symbol coded LSB-first with the bit tree at `offset`. */
51
77
  function getReverseBitTreePrice(probs, offset, bits, symbolIn) {
52
78
  let symbol = symbolIn;
53
79
  let price = 0;
54
80
  let index = 1;
55
- for (let i = 0; i < bits; ++i) {
81
+ symbol |= 1 << bits;
82
+ do {
56
83
  const bit = symbol & 1;
57
84
  symbol >>>= 1;
58
85
  price += getBitPrice(probs[offset + index], bit);
59
86
  index = (index << 1) | bit;
60
- }
87
+ } while (symbol !== 1);
61
88
  return price;
62
89
  }
63
- /** Price of bits coded with a fixed 50% probability. */
64
90
  function getDirectBitsPrice(count) {
65
91
  return count << BIT_PRICE_SHIFT_BITS;
66
92
  }
67
- /**
68
- * Range encoder writing to its own growable buffer.
69
- *
70
- * `low` needs 33 bits (32 bits plus a carry), so it is kept as a regular
71
- * number instead of an int32. Doubles represent integers up to 2^53 exactly.
72
- *
73
- * Performance: like `RangeDecoder`, the methods that encode several bits
74
- * keep `range` and `low` in local variables for the whole symbol.
75
- */
76
93
  class RangeEncoder {
77
94
  low = 0;
78
95
  range = 0xffffffff;
79
96
  cache = 0;
97
+ /**
98
+ * NOTE: int is OK for LZMA2 because a compressed chunk is not more
99
+ * than 64 KiB, but with LZMA1 there is no chunking so in theory
100
+ * cacheSize can grow very big. A JS number counts well past 2^32.
101
+ */
80
102
  cacheSize = 1;
103
+ // Instead of the fixed output array of RangeEncoderToBuffer, the
104
+ // buffer grows on demand and the written bytes are handed out with
105
+ // take()/finish(), because the compressed size isn't known in advance
106
+ // here (contract adaptation).
81
107
  buf;
82
108
  size = 0;
83
109
  constructor(initialCapacity) {
84
110
  this.buf = new Uint8Array(Math.max(64, initialCapacity));
85
111
  }
112
+ reset() {
113
+ this.low = 0;
114
+ this.range = 0xffffffff;
115
+ this.cache = 0x00;
116
+ this.cacheSize = 1;
117
+ }
86
118
  /** Appends bytes that are not range coded, e.g. a header. */
87
119
  writeBytes(bytes) {
88
120
  this.ensureCapacity(bytes.length);
@@ -97,16 +129,34 @@ class RangeEncoder {
97
129
  }
98
130
  /** Flushes all pending bytes and returns the rest of the output. */
99
131
  finish() {
100
- let low = this.low;
101
132
  for (let i = 0; i < 5; ++i) {
102
- low = this.shiftLow(low);
133
+ this.shiftLow();
103
134
  }
104
- this.low = low;
105
135
  return this.size === this.buf.length ? this.buf : this.buf.slice(0, this.size);
106
136
  }
137
+ writeByte(b) {
138
+ this.ensureCapacity(1);
139
+ this.buf[this.size++] = b;
140
+ }
141
+ shiftLow() {
142
+ // (int)(low >>> 32), the pending carry: 0 or 1.
143
+ const lowHi = Math.floor(this.low / 0x100000000);
144
+ if (lowHi !== 0 || this.low < 0xff000000) {
145
+ let temp = this.cache;
146
+ do {
147
+ this.writeByte(temp + lowHi);
148
+ temp = 0xff;
149
+ } while (--this.cacheSize !== 0);
150
+ this.cache = (this.low >>> 24) & 0xff;
151
+ }
152
+ ++this.cacheSize;
153
+ // (low & 0x00FFFFFF) << 8, kept exact above 2^31 with * 256.
154
+ this.low = (this.low & 0x00ffffff) * 256;
155
+ }
107
156
  encodeBit(probs, index, bit) {
108
157
  const prob = probs[index];
109
158
  const bound = (this.range >>> range_coder_1.BIT_MODEL_TOTAL_BITS) * prob;
159
+ // NOTE: Any non-zero value for bit is taken as 1.
110
160
  if (bit === 0) {
111
161
  this.range = bound;
112
162
  probs[index] = prob + ((range_coder_1.BIT_MODEL_TOTAL - prob) >>> range_coder_1.MOVE_BITS);
@@ -118,145 +168,43 @@ class RangeEncoder {
118
168
  }
119
169
  if (this.range < range_coder_1.TOP_VALUE) {
120
170
  this.range = (this.range << 8) >>> 0;
121
- this.low = this.shiftLow(this.low);
171
+ this.shiftLow();
122
172
  }
123
173
  }
124
- /** Encodes a `bits`-bit symbol MSB-first with the bit tree at `offset`. */
125
174
  encodeBitTree(probs, offset, bits, symbol) {
126
- let range = this.range;
127
- let low = this.low;
128
175
  let index = 1;
129
- for (let i = bits - 1; i >= 0; --i) {
130
- const bit = (symbol >>> i) & 1;
131
- const prob = probs[offset + index];
132
- const bound = (range >>> range_coder_1.BIT_MODEL_TOTAL_BITS) * prob;
133
- if (bit === 0) {
134
- range = bound;
135
- probs[offset + index] = prob + ((range_coder_1.BIT_MODEL_TOTAL - prob) >>> range_coder_1.MOVE_BITS);
136
- }
137
- else {
138
- low += bound;
139
- range -= bound;
140
- probs[offset + index] = prob - (prob >>> range_coder_1.MOVE_BITS);
141
- }
142
- if (range < range_coder_1.TOP_VALUE) {
143
- range = (range << 8) >>> 0;
144
- low = this.shiftLow(low);
176
+ let mask = 1 << bits;
177
+ do {
178
+ mask >>>= 1;
179
+ const bit = symbol & mask;
180
+ this.encodeBit(probs, offset + index, bit);
181
+ index <<= 1;
182
+ if (bit !== 0) {
183
+ index |= 1;
145
184
  }
146
- index = (index << 1) | bit;
147
- }
148
- this.range = range;
149
- this.low = low;
185
+ } while (mask !== 1);
150
186
  }
151
- /** Encodes a `bits`-bit symbol LSB-first with the bit tree at `offset`. */
152
187
  encodeReverseBitTree(probs, offset, bits, symbolIn) {
153
188
  let symbol = symbolIn;
154
- let range = this.range;
155
- let low = this.low;
156
189
  let index = 1;
157
- for (let i = 0; i < bits; ++i) {
190
+ symbol |= 1 << bits;
191
+ do {
158
192
  const bit = symbol & 1;
159
193
  symbol >>>= 1;
160
- const prob = probs[offset + index];
161
- const bound = (range >>> range_coder_1.BIT_MODEL_TOTAL_BITS) * prob;
162
- if (bit === 0) {
163
- range = bound;
164
- probs[offset + index] = prob + ((range_coder_1.BIT_MODEL_TOTAL - prob) >>> range_coder_1.MOVE_BITS);
165
- }
166
- else {
167
- low += bound;
168
- range -= bound;
169
- probs[offset + index] = prob - (prob >>> range_coder_1.MOVE_BITS);
170
- }
171
- if (range < range_coder_1.TOP_VALUE) {
172
- range = (range << 8) >>> 0;
173
- low = this.shiftLow(low);
174
- }
194
+ this.encodeBit(probs, offset + index, bit);
175
195
  index = (index << 1) | bit;
176
- }
177
- this.range = range;
178
- this.low = low;
179
- }
180
- /** Encodes a literal byte with the 0x100-leaf bit tree at `offset`. */
181
- encodeLiteral(probs, offset, symbol) {
182
- this.encodeBitTree(probs, offset, 8, symbol);
196
+ } while (symbol !== 1);
183
197
  }
184
- /**
185
- * Encodes a literal after a match. The bits of `matchByte` (the byte at
186
- * rep0) select separate probabilities until the first bit that differs.
187
- */
188
- encodeMatchedLiteral(probs, offset, symbolIn, matchByteIn) {
189
- let symbol = symbolIn;
190
- let matchByte = matchByteIn;
191
- let range = this.range;
192
- let low = this.low;
193
- let matchOffset = 0x100;
194
- symbol |= 0x100;
195
- do {
196
- matchByte <<= 1;
197
- const matchBit = matchByte & matchOffset;
198
- const index = offset + matchOffset + matchBit + (symbol >>> 8);
199
- const bit = (symbol >>> 7) & 1;
200
- const prob = probs[index];
201
- const bound = (range >>> range_coder_1.BIT_MODEL_TOTAL_BITS) * prob;
202
- if (bit === 0) {
203
- range = bound;
204
- probs[index] = prob + ((range_coder_1.BIT_MODEL_TOTAL - prob) >>> range_coder_1.MOVE_BITS);
205
- }
206
- else {
207
- low += bound;
208
- range -= bound;
209
- probs[index] = prob - (prob >>> range_coder_1.MOVE_BITS);
210
- }
211
- if (range < range_coder_1.TOP_VALUE) {
212
- range = (range << 8) >>> 0;
213
- low = this.shiftLow(low);
214
- }
215
- symbol <<= 1;
216
- matchOffset &= ~(matchByte ^ symbol);
217
- } while (symbol < 0x10000);
218
- this.range = range;
219
- this.low = low;
220
- }
221
- /** Encodes the `count` low bits of `value` with a fixed 50% probability. */
222
198
  encodeDirectBits(value, countIn) {
223
199
  let count = countIn;
224
- let range = this.range;
225
- let low = this.low;
226
200
  do {
227
- range >>>= 1;
228
- if (((value >>> --count) & 1) !== 0) {
229
- low += range;
230
- }
231
- if (range < range_coder_1.TOP_VALUE) {
232
- range = (range << 8) >>> 0;
233
- low = this.shiftLow(low);
201
+ this.range >>>= 1;
202
+ this.low += this.range & (0 - ((value >>> --count) & 1));
203
+ if (this.range < range_coder_1.TOP_VALUE) {
204
+ this.range = (this.range << 8) >>> 0;
205
+ this.shiftLow();
234
206
  }
235
207
  } while (count !== 0);
236
- this.range = range;
237
- this.low = low;
238
- }
239
- /**
240
- * Moves the top byte of `low` to the output and returns the new `low`.
241
- * A run of 0xFF bytes is held back (`cache` + `cacheSize`) until it is
242
- * known whether a carry will propagate into it.
243
- */
244
- shiftLow(low) {
245
- if (low < 0xff000000 || low >= 0x100000000) {
246
- const carry = low >= 0x100000000 ? 1 : 0;
247
- this.ensureCapacity(this.cacheSize);
248
- const buf = this.buf;
249
- let size = this.size;
250
- buf[size++] = (this.cache + carry) & 0xff;
251
- for (let i = 1; i < this.cacheSize; ++i) {
252
- buf[size++] = (0xff + carry) & 0xff;
253
- }
254
- this.size = size;
255
- this.cacheSize = 0;
256
- this.cache = (low >>> 24) & 0xff;
257
- }
258
- ++this.cacheSize;
259
- return (low & 0x00ffffff) * 256;
260
208
  }
261
209
  ensureCapacity(extra) {
262
210
  if (this.size + extra > this.buf.length) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patrickdk77/archive-defuse",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Sanitize zip, tar, gzip, bzip2, zstd, Brotli, cab, 7z, xz and lzma archives. Detects path traversal, links out of the tree, device files, duplicate names, overlapping entries, hidden and prepended data, encrypted entries and compression bombs, hands every entry to file plugins, scores the risk, and writes a defused archive in the same format with no runtime dependencies.",
5
5
  "license": "MIT AND Apache-2.0",
6
6
  "author": "patrickdk77",