cbor-x 1.6.6 → 1.6.8

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/node.cjs CHANGED
@@ -21,7 +21,11 @@ const STOP_CODE = {};
21
21
  let maxArraySize = 112810000; // This is the maximum array size in V8. We would potentially detect and set it higher
22
22
  // for JSC, but this is pretty large and should be sufficient for most use cases
23
23
  let maxMapSize = 16810000; // JavaScript has a fixed maximum map size of about 16710000, but JS itself enforces this,
24
- // many keys in an object, so also probably a reasonable choice there.
24
+ // so we don't need to
25
+
26
+ let maxObjectSize = 16710000; // It takes over a minute to create this many keys in an object, so this is probably a
27
+ // reasonable choice.
28
+ let objectSizeLimit = maxObjectSize; // objects are limited by both maxObjectSize and maxMapSize
25
29
  let strings = EMPTY_ARRAY;
26
30
  let stringPosition = 0;
27
31
  let currentDecoder = {};
@@ -124,7 +128,13 @@ class Decoder {
124
128
  // re-entrant execution, save the state and restore it after we do this decode
125
129
  return saveState(() => {
126
130
  clearSource();
127
- return this ? this.decode(source, end) : Decoder.prototype.decode.call(defaultOptions, source, end)
131
+ try {
132
+ return this ? this.decode(source, end) : Decoder.prototype.decode.call(defaultOptions, source, end)
133
+ } catch (error) {
134
+ if (error && typeof error == 'object')
135
+ nestedDecodeErrors.add(error);
136
+ throw error
137
+ }
128
138
  })
129
139
  }
130
140
  srcEnd = end > -1 ? end : source.length;
@@ -201,15 +211,15 @@ class Decoder {
201
211
  }
202
212
  }
203
213
  }
214
+ const nestedDecodeErrors = new WeakSet();
215
+ let arrayAllocationBudget;
204
216
  function checkedRead() {
205
217
  try {
218
+ arrayAllocationBudget = srcEnd - position$1;
206
219
  let result = read();
207
220
  if (bundledStrings$1) {
208
- if (position$1 >= bundledStrings$1.postBundlePosition) {
209
- let error = new Error('Unexpected bundle position');
210
- error.incomplete = true;
211
- throw error
212
- }
221
+ if (position$1 >= bundledStrings$1.postBundlePosition)
222
+ throw new Error('Unexpected bundle position')
213
223
  // bundled strings to skip past
214
224
  position$1 = bundledStrings$1.postBundlePosition;
215
225
  bundledStrings$1 = null;
@@ -233,7 +243,14 @@ function checkedRead() {
233
243
  return result
234
244
  } catch(error) {
235
245
  clearSource();
236
- if (error instanceof RangeError || error.message.startsWith('Unexpected end of buffer')) {
246
+ if (nestedDecodeErrors.has(error)) {
247
+ // from a nested decode of embedded data (by an extension), which was complete in this source
248
+ if (error.incomplete)
249
+ error.incomplete = false;
250
+ } else if (error instanceof RangeError && position$1 + 8 > srcEnd ||
251
+ error && typeof error.message == 'string' && error.message.startsWith('Unexpected end of buffer')) {
252
+ // a DataView read past the end of the source; these read at most 8 bytes and precede advancing position.
253
+ // Any other RangeError (such as a stack overflow from deeply nested input) is fatal.
237
254
  error.incomplete = true;
238
255
  }
239
256
  throw error
@@ -318,13 +335,13 @@ function read() {
318
335
  let i = 0;
319
336
  if (currentDecoder.keyMap) {
320
337
  while((key = read()) != STOP_CODE) {
321
- if (i++ >= maxMapSize) throw new Error(`Property count exceeds ${maxMapSize}`)
338
+ if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
322
339
  object[safeKey(currentDecoder.decodeKey(key))] = read();
323
340
  }
324
341
  }
325
342
  else {
326
343
  while ((key = read()) != STOP_CODE) {
327
- if (i++ >= maxMapSize) throw new Error(`Property count exceeds ${maxMapSize}`)
344
+ if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
328
345
  object[safeKey(key)] = read();
329
346
  }
330
347
  }
@@ -370,11 +387,13 @@ function read() {
370
387
  case 1: // negative int
371
388
  return ~token
372
389
  case 2: // buffer
390
+ if (token > srcEnd - position$1) throw endOfCBORError()
373
391
  return readBin(token)
374
392
  case 3: // string
375
- if (srcStringEnd >= position$1) {
393
+ if (srcStringEnd >= position$1) { // already extracted, which only covers complete strings
376
394
  return srcString.slice(position$1 - srcStringStart, (position$1 += token) - srcStringStart)
377
395
  }
396
+ if (token > srcEnd - position$1) throw endOfCBORError()
378
397
  if (srcStringEnd == 0 && srcEnd < 140 && token < 32) {
379
398
  // for small blocks, avoiding the overhead of the extract call is helpful
380
399
  let string = token < 16 ? shortStringInJS(token) : longStringInJS(token);
@@ -383,19 +402,21 @@ function read() {
383
402
  }
384
403
  return readFixedString(token)
385
404
  case 4: // array
386
- if (token >= maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
405
+ if (token > maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
387
406
  // every element occupies at least one byte, so a length beyond what remains in the source can
388
407
  // never be satisfied; check before allocating so a tiny header can not force a huge allocation
389
- if (token > srcEnd - position$1) throw endOfCBORError()
408
+ // nested arrays each pass the check above yet still preallocate, so also spend from a
409
+ // per-decode budget of the remaining bytes; the summed lengths of any valid tree stay under it
410
+ if (token > srcEnd - position$1 || (arrayAllocationBudget -= token) < 0) throw endOfCBORError()
390
411
  let array = new Array(token);
391
412
  //if (currentDecoder.keyMap) for (let i = 0; i < token; i++) array[i] = currentDecoder.decodeKey(read())
392
413
  //else
393
414
  for (let i = 0; i < token; i++) array[i] = read();
394
415
  return array
395
416
  case 5: // map
396
- if (token >= maxMapSize) throw new Error(`Map size exceeds ${maxArraySize}`)
397
- if (token > (srcEnd - position$1) / 2) throw endOfCBORError() // each entry needs at least two bytes
398
417
  if (currentDecoder.mapsAsObjects) {
418
+ if (token > objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
419
+ if (token > (srcEnd - position$1) / 2) throw endOfCBORError() // each entry needs at least two bytes
399
420
  let object = {};
400
421
  if (currentDecoder.keyMap) for (let i = 0; i < token; i++) object[safeKey(currentDecoder.decodeKey(read()))] = read();
401
422
  else for (let i = 0; i < token; i++) object[safeKey(read())] = read();
@@ -405,6 +426,8 @@ function read() {
405
426
  currentDecoder.mapsAsObjects = true;
406
427
  restoreMapsAsObject = false;
407
428
  }
429
+ if (token > maxMapSize) throw new Error(`Map size exceeds ${maxMapSize}`)
430
+ if (token > (srcEnd - position$1) / 2) throw endOfCBORError()
408
431
  let map = new Map();
409
432
  if (currentDecoder.keyMap) for (let i = 0; i < token; i++) map.set(currentDecoder.decodeKey(read()),read());
410
433
  else for (let i = 0; i < token; i++) map.set(read(), read());
@@ -935,18 +958,12 @@ currentExtensions[27] = (data) => { // http://cbor.schmorp.de/generic-object
935
958
  return (glbl[data[0]] || Error)(data[1], data[2])
936
959
  };
937
960
  const packedTable = (read) => {
938
- if (src[position$1++] != 0x84) {
939
- let error = new Error('Packed values structure must be followed by a 4 element array');
940
- if (src.length < position$1)
941
- error.incomplete = true;
942
- throw error
943
- }
961
+ if (!(position$1 < srcEnd)) throw endOfCBORError()
962
+ if (src[position$1++] != 0x84)
963
+ throw new Error('Packed values structure must be followed by a 4 element array')
944
964
  let newPackedValues = read(); // packed values
945
- if (!newPackedValues || !newPackedValues.length) {
946
- let error = new Error('Packed values structure must be followed by a 4 element array');
947
- error.incomplete = true;
948
- throw error
949
- }
965
+ if (!Array.isArray(newPackedValues) || !newPackedValues.length)
966
+ throw new Error('Packed values structure must be followed by a 4 element array')
950
967
  packedValues = packedValues ? newPackedValues.concat(packedValues.slice(newPackedValues.length)) : newPackedValues;
951
968
  packedValues.prefixes = read();
952
969
  packedValues.suffixes = read();
@@ -964,10 +981,7 @@ currentExtensions[PACKED_REFERENCE_TAG_ID] = (data) => { // packed reference
964
981
  }
965
982
  if (typeof data == 'number')
966
983
  return packedValues[16 + (data >= 0 ? 2 * data : (-2 * data - 1))]
967
- let error = new Error('No support for non-integer packed references yet');
968
- if (data === undefined)
969
- error.incomplete = true;
970
- throw error
984
+ throw new Error('No support for non-integer packed references yet')
971
985
  };
972
986
 
973
987
  // The following code is an incomplete implementation of http://cbor.schmorp.de/stringref
@@ -1037,9 +1051,19 @@ currentExtensions[258] = (array) => new Set(array); // https://github.com/input-
1037
1051
  (currentExtensions[259] = (read) => {
1038
1052
  // https://github.com/shanewholloway/js-cbor-codec/blob/master/docs/CBOR-259-spec
1039
1053
  // for decoding as a standard Map
1040
- if (currentDecoder.mapsAsObjects) {
1041
- currentDecoder.mapsAsObjects = false;
1054
+ let decoder = currentDecoder;
1055
+ if (decoder.mapsAsObjects) {
1056
+ decoder.mapsAsObjects = false;
1042
1057
  restoreMapsAsObject = true;
1058
+ try {
1059
+ return read()
1060
+ } finally {
1061
+ // normally restored by the map header, but not if the tagged value is not a map or the read fails
1062
+ if (restoreMapsAsObject) {
1063
+ decoder.mapsAsObjects = true;
1064
+ restoreMapsAsObject = false;
1065
+ }
1066
+ }
1043
1067
  }
1044
1068
  return read()
1045
1069
  }).handlesRead = true;
@@ -1159,10 +1183,12 @@ function readJustLength() {
1159
1183
  token = src[position$1++];
1160
1184
  break
1161
1185
  case 0x19:
1186
+ if (position$1 + 2 > srcEnd) throw endOfCBORError()
1162
1187
  token = dataView.getUint16(position$1);
1163
1188
  position$1 += 2;
1164
1189
  break
1165
1190
  case 0x1a:
1191
+ if (position$1 + 4 > srcEnd) throw endOfCBORError()
1166
1192
  token = dataView.getUint32(position$1);
1167
1193
  position$1 += 4;
1168
1194
  break
@@ -1204,22 +1230,29 @@ function saveState(callback) {
1204
1230
  let savedStructures = currentStructures;
1205
1231
  let savedDecoder = currentDecoder;
1206
1232
  let savedSequentialMode = sequentialMode;
1207
- let value = callback();
1208
- srcEnd = savedSrcEnd;
1209
- position$1 = savedPosition;
1210
- stringPosition = savedStringPosition;
1211
- srcStringStart = savedSrcStringStart;
1212
- srcStringEnd = savedSrcStringEnd;
1213
- srcString = savedSrcString;
1214
- strings = savedStrings;
1215
- referenceMap = savedReferenceMap;
1216
- bundledStrings$1 = savedBundledStrings;
1217
- src = savedSrc;
1218
- sequentialMode = savedSequentialMode;
1219
- currentStructures = savedStructures;
1220
- currentDecoder = savedDecoder;
1221
- dataView = new DataView(src.buffer, src.byteOffset, src.byteLength);
1222
- return value
1233
+ let savedRestoreMapsAsObject = restoreMapsAsObject;
1234
+ let savedArrayAllocationBudget = arrayAllocationBudget;
1235
+ restoreMapsAsObject = false;
1236
+ try {
1237
+ return callback()
1238
+ } finally {
1239
+ srcEnd = savedSrcEnd;
1240
+ position$1 = savedPosition;
1241
+ stringPosition = savedStringPosition;
1242
+ srcStringStart = savedSrcStringStart;
1243
+ srcStringEnd = savedSrcStringEnd;
1244
+ srcString = savedSrcString;
1245
+ strings = savedStrings;
1246
+ referenceMap = savedReferenceMap;
1247
+ bundledStrings$1 = savedBundledStrings;
1248
+ src = savedSrc;
1249
+ sequentialMode = savedSequentialMode;
1250
+ currentStructures = savedStructures;
1251
+ currentDecoder = savedDecoder;
1252
+ restoreMapsAsObject = savedRestoreMapsAsObject;
1253
+ arrayAllocationBudget = savedArrayAllocationBudget;
1254
+ dataView = new DataView(src.buffer, src.byteOffset, src.byteLength);
1255
+ }
1223
1256
  }
1224
1257
  function clearSource() {
1225
1258
  src = null;
@@ -1234,7 +1267,8 @@ function addExtension$1(extension) {
1234
1267
  function setSizeLimits(limits) {
1235
1268
  if (limits.maxMapSize) maxMapSize = limits.maxMapSize;
1236
1269
  if (limits.maxArraySize) maxArraySize = limits.maxArraySize;
1237
- if (limits.maxObjectSize) limits.maxObjectSize;
1270
+ if (limits.maxObjectSize) maxObjectSize = limits.maxObjectSize;
1271
+ objectSizeLimit = Math.min(maxObjectSize, maxMapSize);
1238
1272
  }
1239
1273
 
1240
1274
  const mult10 = new Array(147); // this is a table matching binary exponents to the multiplier to determine significant digit rounding
@@ -1436,7 +1470,7 @@ class Encoder extends Decoder {
1436
1470
  if (throwOnIterable)
1437
1471
  return;
1438
1472
  encode(value);
1439
- if (bundledStrings) {
1473
+ if (bundledStrings && bundledStrings.position) { // position is only set once a bundle has been started
1440
1474
  writeBundles(start, encode);
1441
1475
  }
1442
1476
  encoder.offset = position; // update the offset so next serialization doesn't write over our buffer, but can continue writing to same buffer sequentially
@@ -2521,24 +2555,22 @@ class DecoderStream extends stream.Transform {
2521
2555
  chunk = Buffer.concat([this.incompleteBuffer, chunk]);
2522
2556
  this.incompleteBuffer = null;
2523
2557
  }
2524
- let values;
2558
+ let values, fatalError;
2525
2559
  try {
2526
2560
  values = this.decoder.decodeMultiple(chunk);
2527
2561
  } catch(error) {
2528
- if (error.incomplete) {
2562
+ values = error.values;
2563
+ if (error.incomplete)
2529
2564
  this.incompleteBuffer = chunk.slice(error.lastPosition);
2530
- values = error.values;
2531
- } else {
2532
- return callback(error)
2533
- }
2534
- } finally {
2535
- for (let value of values || []) {
2536
- if (value === null)
2537
- value = this.getNullValue();
2538
- this.push(value);
2539
- }
2565
+ else
2566
+ fatalError = error;
2567
+ }
2568
+ for (let value of values || []) {
2569
+ if (value === null)
2570
+ value = this.getNullValue();
2571
+ this.push(value);
2540
2572
  }
2541
- callback();
2573
+ callback(fatalError);
2542
2574
  }
2543
2575
  getNullValue() {
2544
2576
  return Symbol.for(null)
@@ -2591,7 +2623,7 @@ function decodeIter (bufferIterator, options = {}) {
2591
2623
  }
2592
2624
 
2593
2625
  const decoder = new Decoder(options);
2594
- let incomplete;
2626
+ let incomplete, fatalError;
2595
2627
  const parser = (chunk) => {
2596
2628
  let yields;
2597
2629
  // if there's incomplete data from previous chunk, concatinate and try again
@@ -2603,26 +2635,28 @@ function decodeIter (bufferIterator, options = {}) {
2603
2635
  try {
2604
2636
  yields = decoder.decodeMultiple(chunk);
2605
2637
  } catch (err) {
2638
+ yields = err.values;
2606
2639
  if (err.incomplete) {
2607
2640
  incomplete = chunk.slice(err.lastPosition);
2608
- yields = err.values;
2609
2641
  } else {
2610
- throw err
2642
+ fatalError = err;
2611
2643
  }
2612
2644
  }
2613
- return yields
2645
+ return yields || []
2614
2646
  };
2615
2647
 
2616
2648
  if (typeof bufferIterator[Symbol.iterator] === 'function') {
2617
2649
  return (function * iter () {
2618
2650
  for (const value of bufferIterator) {
2619
2651
  yield * parser(value);
2652
+ if (fatalError) throw fatalError
2620
2653
  }
2621
2654
  })()
2622
2655
  } else if (typeof bufferIterator[Symbol.asyncIterator] === 'function') {
2623
2656
  return (async function * iter () {
2624
2657
  for await (const value of bufferIterator) {
2625
2658
  yield * parser(value);
2659
+ if (fatalError) throw fatalError
2626
2660
  }
2627
2661
  })()
2628
2662
  }