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/test.js CHANGED
@@ -19,7 +19,11 @@
19
19
  let maxArraySize = 112810000; // This is the maximum array size in V8. We would potentially detect and set it higher
20
20
  // for JSC, but this is pretty large and should be sufficient for most use cases
21
21
  let maxMapSize = 16810000; // JavaScript has a fixed maximum map size of about 16710000, but JS itself enforces this,
22
- // many keys in an object, so also probably a reasonable choice there.
22
+ // so we don't need to
23
+
24
+ let maxObjectSize = 16710000; // It takes over a minute to create this many keys in an object, so this is probably a
25
+ // reasonable choice.
26
+ let objectSizeLimit = maxObjectSize; // objects are limited by both maxObjectSize and maxMapSize
23
27
  let strings = EMPTY_ARRAY;
24
28
  let stringPosition = 0;
25
29
  let currentDecoder = {};
@@ -122,7 +126,13 @@
122
126
  // re-entrant execution, save the state and restore it after we do this decode
123
127
  return saveState(() => {
124
128
  clearSource();
125
- return this ? this.decode(source, end) : Decoder.prototype.decode.call(defaultOptions, source, end)
129
+ try {
130
+ return this ? this.decode(source, end) : Decoder.prototype.decode.call(defaultOptions, source, end)
131
+ } catch (error) {
132
+ if (error && typeof error == 'object')
133
+ nestedDecodeErrors.add(error);
134
+ throw error
135
+ }
126
136
  })
127
137
  }
128
138
  srcEnd = end > -1 ? end : source.length;
@@ -199,15 +209,15 @@
199
209
  }
200
210
  }
201
211
  }
212
+ const nestedDecodeErrors = new WeakSet();
213
+ let arrayAllocationBudget;
202
214
  function checkedRead() {
203
215
  try {
216
+ arrayAllocationBudget = srcEnd - position$1;
204
217
  let result = read();
205
218
  if (bundledStrings$1) {
206
- if (position$1 >= bundledStrings$1.postBundlePosition) {
207
- let error = new Error('Unexpected bundle position');
208
- error.incomplete = true;
209
- throw error
210
- }
219
+ if (position$1 >= bundledStrings$1.postBundlePosition)
220
+ throw new Error('Unexpected bundle position')
211
221
  // bundled strings to skip past
212
222
  position$1 = bundledStrings$1.postBundlePosition;
213
223
  bundledStrings$1 = null;
@@ -231,7 +241,14 @@
231
241
  return result
232
242
  } catch(error) {
233
243
  clearSource();
234
- if (error instanceof RangeError || error.message.startsWith('Unexpected end of buffer')) {
244
+ if (nestedDecodeErrors.has(error)) {
245
+ // from a nested decode of embedded data (by an extension), which was complete in this source
246
+ if (error.incomplete)
247
+ error.incomplete = false;
248
+ } else if (error instanceof RangeError && position$1 + 8 > srcEnd ||
249
+ error && typeof error.message == 'string' && error.message.startsWith('Unexpected end of buffer')) {
250
+ // a DataView read past the end of the source; these read at most 8 bytes and precede advancing position.
251
+ // Any other RangeError (such as a stack overflow from deeply nested input) is fatal.
235
252
  error.incomplete = true;
236
253
  }
237
254
  throw error
@@ -316,13 +333,13 @@
316
333
  let i = 0;
317
334
  if (currentDecoder.keyMap) {
318
335
  while((key = read()) != STOP_CODE) {
319
- if (i++ >= maxMapSize) throw new Error(`Property count exceeds ${maxMapSize}`)
336
+ if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
320
337
  object[safeKey(currentDecoder.decodeKey(key))] = read();
321
338
  }
322
339
  }
323
340
  else {
324
341
  while ((key = read()) != STOP_CODE) {
325
- if (i++ >= maxMapSize) throw new Error(`Property count exceeds ${maxMapSize}`)
342
+ if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
326
343
  object[safeKey(key)] = read();
327
344
  }
328
345
  }
@@ -368,11 +385,13 @@
368
385
  case 1: // negative int
369
386
  return ~token
370
387
  case 2: // buffer
388
+ if (token > srcEnd - position$1) throw endOfCBORError()
371
389
  return readBin(token)
372
390
  case 3: // string
373
- if (srcStringEnd >= position$1) {
391
+ if (srcStringEnd >= position$1) { // already extracted, which only covers complete strings
374
392
  return srcString.slice(position$1 - srcStringStart, (position$1 += token) - srcStringStart)
375
393
  }
394
+ if (token > srcEnd - position$1) throw endOfCBORError()
376
395
  if (srcStringEnd == 0 && srcEnd < 140 && token < 32) {
377
396
  // for small blocks, avoiding the overhead of the extract call is helpful
378
397
  let string = token < 16 ? shortStringInJS(token) : longStringInJS(token);
@@ -381,19 +400,21 @@
381
400
  }
382
401
  return readFixedString(token)
383
402
  case 4: // array
384
- if (token >= maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
403
+ if (token > maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
385
404
  // every element occupies at least one byte, so a length beyond what remains in the source can
386
405
  // never be satisfied; check before allocating so a tiny header can not force a huge allocation
387
- if (token > srcEnd - position$1) throw endOfCBORError()
406
+ // nested arrays each pass the check above yet still preallocate, so also spend from a
407
+ // per-decode budget of the remaining bytes; the summed lengths of any valid tree stay under it
408
+ if (token > srcEnd - position$1 || (arrayAllocationBudget -= token) < 0) throw endOfCBORError()
388
409
  let array = new Array(token);
389
410
  //if (currentDecoder.keyMap) for (let i = 0; i < token; i++) array[i] = currentDecoder.decodeKey(read())
390
411
  //else
391
412
  for (let i = 0; i < token; i++) array[i] = read();
392
413
  return array
393
414
  case 5: // map
394
- if (token >= maxMapSize) throw new Error(`Map size exceeds ${maxArraySize}`)
395
- if (token > (srcEnd - position$1) / 2) throw endOfCBORError() // each entry needs at least two bytes
396
415
  if (currentDecoder.mapsAsObjects) {
416
+ if (token > objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
417
+ if (token > (srcEnd - position$1) / 2) throw endOfCBORError() // each entry needs at least two bytes
397
418
  let object = {};
398
419
  if (currentDecoder.keyMap) for (let i = 0; i < token; i++) object[safeKey(currentDecoder.decodeKey(read()))] = read();
399
420
  else for (let i = 0; i < token; i++) object[safeKey(read())] = read();
@@ -403,6 +424,8 @@
403
424
  currentDecoder.mapsAsObjects = true;
404
425
  restoreMapsAsObject = false;
405
426
  }
427
+ if (token > maxMapSize) throw new Error(`Map size exceeds ${maxMapSize}`)
428
+ if (token > (srcEnd - position$1) / 2) throw endOfCBORError()
406
429
  let map = new Map();
407
430
  if (currentDecoder.keyMap) for (let i = 0; i < token; i++) map.set(currentDecoder.decodeKey(read()),read());
408
431
  else for (let i = 0; i < token; i++) map.set(read(), read());
@@ -930,18 +953,12 @@
930
953
  return (glbl[data[0]] || Error)(data[1], data[2])
931
954
  };
932
955
  const packedTable = (read) => {
933
- if (src[position$1++] != 0x84) {
934
- let error = new Error('Packed values structure must be followed by a 4 element array');
935
- if (src.length < position$1)
936
- error.incomplete = true;
937
- throw error
938
- }
956
+ if (!(position$1 < srcEnd)) throw endOfCBORError()
957
+ if (src[position$1++] != 0x84)
958
+ throw new Error('Packed values structure must be followed by a 4 element array')
939
959
  let newPackedValues = read(); // packed values
940
- if (!newPackedValues || !newPackedValues.length) {
941
- let error = new Error('Packed values structure must be followed by a 4 element array');
942
- error.incomplete = true;
943
- throw error
944
- }
960
+ if (!Array.isArray(newPackedValues) || !newPackedValues.length)
961
+ throw new Error('Packed values structure must be followed by a 4 element array')
945
962
  packedValues = packedValues ? newPackedValues.concat(packedValues.slice(newPackedValues.length)) : newPackedValues;
946
963
  packedValues.prefixes = read();
947
964
  packedValues.suffixes = read();
@@ -959,10 +976,7 @@
959
976
  }
960
977
  if (typeof data == 'number')
961
978
  return packedValues[16 + (data >= 0 ? 2 * data : (-2 * data - 1))]
962
- let error = new Error('No support for non-integer packed references yet');
963
- if (data === undefined)
964
- error.incomplete = true;
965
- throw error
979
+ throw new Error('No support for non-integer packed references yet')
966
980
  };
967
981
 
968
982
  // The following code is an incomplete implementation of http://cbor.schmorp.de/stringref
@@ -1032,9 +1046,19 @@
1032
1046
  (currentExtensions[259] = (read) => {
1033
1047
  // https://github.com/shanewholloway/js-cbor-codec/blob/master/docs/CBOR-259-spec
1034
1048
  // for decoding as a standard Map
1035
- if (currentDecoder.mapsAsObjects) {
1036
- currentDecoder.mapsAsObjects = false;
1049
+ let decoder = currentDecoder;
1050
+ if (decoder.mapsAsObjects) {
1051
+ decoder.mapsAsObjects = false;
1037
1052
  restoreMapsAsObject = true;
1053
+ try {
1054
+ return read()
1055
+ } finally {
1056
+ // normally restored by the map header, but not if the tagged value is not a map or the read fails
1057
+ if (restoreMapsAsObject) {
1058
+ decoder.mapsAsObjects = true;
1059
+ restoreMapsAsObject = false;
1060
+ }
1061
+ }
1038
1062
  }
1039
1063
  return read()
1040
1064
  }).handlesRead = true;
@@ -1154,10 +1178,12 @@
1154
1178
  token = src[position$1++];
1155
1179
  break
1156
1180
  case 0x19:
1181
+ if (position$1 + 2 > srcEnd) throw endOfCBORError()
1157
1182
  token = dataView.getUint16(position$1);
1158
1183
  position$1 += 2;
1159
1184
  break
1160
1185
  case 0x1a:
1186
+ if (position$1 + 4 > srcEnd) throw endOfCBORError()
1161
1187
  token = dataView.getUint32(position$1);
1162
1188
  position$1 += 4;
1163
1189
  break
@@ -1199,22 +1225,29 @@
1199
1225
  let savedStructures = currentStructures;
1200
1226
  let savedDecoder = currentDecoder;
1201
1227
  let savedSequentialMode = sequentialMode;
1202
- let value = callback();
1203
- srcEnd = savedSrcEnd;
1204
- position$1 = savedPosition;
1205
- stringPosition = savedStringPosition;
1206
- srcStringStart = savedSrcStringStart;
1207
- srcStringEnd = savedSrcStringEnd;
1208
- srcString = savedSrcString;
1209
- strings = savedStrings;
1210
- referenceMap = savedReferenceMap;
1211
- bundledStrings$1 = savedBundledStrings;
1212
- src = savedSrc;
1213
- sequentialMode = savedSequentialMode;
1214
- currentStructures = savedStructures;
1215
- currentDecoder = savedDecoder;
1216
- dataView = new DataView(src.buffer, src.byteOffset, src.byteLength);
1217
- return value
1228
+ let savedRestoreMapsAsObject = restoreMapsAsObject;
1229
+ let savedArrayAllocationBudget = arrayAllocationBudget;
1230
+ restoreMapsAsObject = false;
1231
+ try {
1232
+ return callback()
1233
+ } finally {
1234
+ srcEnd = savedSrcEnd;
1235
+ position$1 = savedPosition;
1236
+ stringPosition = savedStringPosition;
1237
+ srcStringStart = savedSrcStringStart;
1238
+ srcStringEnd = savedSrcStringEnd;
1239
+ srcString = savedSrcString;
1240
+ strings = savedStrings;
1241
+ referenceMap = savedReferenceMap;
1242
+ bundledStrings$1 = savedBundledStrings;
1243
+ src = savedSrc;
1244
+ sequentialMode = savedSequentialMode;
1245
+ currentStructures = savedStructures;
1246
+ currentDecoder = savedDecoder;
1247
+ restoreMapsAsObject = savedRestoreMapsAsObject;
1248
+ arrayAllocationBudget = savedArrayAllocationBudget;
1249
+ dataView = new DataView(src.buffer, src.byteOffset, src.byteLength);
1250
+ }
1218
1251
  }
1219
1252
  function clearSource() {
1220
1253
  src = null;
@@ -1420,7 +1453,7 @@
1420
1453
  if (throwOnIterable)
1421
1454
  return;
1422
1455
  encode(value);
1423
- if (bundledStrings) {
1456
+ if (bundledStrings && bundledStrings.position) { // position is only set once a bundle has been started
1424
1457
  writeBundles(start, encode);
1425
1458
  }
1426
1459
  encoder.offset = position; // update the offset so next serialization doesn't write over our buffer, but can continue writing to same buffer sequentially