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.
@@ -20,6 +20,11 @@
20
20
  let maxArraySize = 112810000; // This is the maximum array size in V8. We would potentially detect and set it higher
21
21
  // for JSC, but this is pretty large and should be sufficient for most use cases
22
22
  let maxMapSize = 16810000; // JavaScript has a fixed maximum map size of about 16710000, but JS itself enforces this,
23
+ // so we don't need to
24
+
25
+ let maxObjectSize = 16710000; // It takes over a minute to create this many keys in an object, so this is probably a
26
+ // reasonable choice.
27
+ let objectSizeLimit = maxObjectSize; // objects are limited by both maxObjectSize and maxMapSize
23
28
  let currentDecoder = {};
24
29
  let currentStructures;
25
30
  let srcString;
@@ -121,7 +126,13 @@
121
126
  // re-entrant execution, save the state and restore it after we do this decode
122
127
  return saveState(() => {
123
128
  clearSource();
124
- 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
+ }
125
136
  })
126
137
  }
127
138
  srcEnd = end > -1 ? end : source.length;
@@ -196,15 +207,15 @@
196
207
  }
197
208
  }
198
209
  }
210
+ const nestedDecodeErrors = new WeakSet();
211
+ let arrayAllocationBudget;
199
212
  function checkedRead() {
200
213
  try {
214
+ arrayAllocationBudget = srcEnd - position$1;
201
215
  let result = read();
202
216
  if (bundledStrings$1) {
203
- if (position$1 >= bundledStrings$1.postBundlePosition) {
204
- let error = new Error('Unexpected bundle position');
205
- error.incomplete = true;
206
- throw error
207
- }
217
+ if (position$1 >= bundledStrings$1.postBundlePosition)
218
+ throw new Error('Unexpected bundle position')
208
219
  // bundled strings to skip past
209
220
  position$1 = bundledStrings$1.postBundlePosition;
210
221
  bundledStrings$1 = null;
@@ -228,7 +239,14 @@
228
239
  return result
229
240
  } catch(error) {
230
241
  clearSource();
231
- if (error instanceof RangeError || error.message.startsWith('Unexpected end of buffer')) {
242
+ if (nestedDecodeErrors.has(error)) {
243
+ // from a nested decode of embedded data (by an extension), which was complete in this source
244
+ if (error.incomplete)
245
+ error.incomplete = false;
246
+ } else if (error instanceof RangeError && position$1 + 8 > srcEnd ||
247
+ error && typeof error.message == 'string' && error.message.startsWith('Unexpected end of buffer')) {
248
+ // a DataView read past the end of the source; these read at most 8 bytes and precede advancing position.
249
+ // Any other RangeError (such as a stack overflow from deeply nested input) is fatal.
232
250
  error.incomplete = true;
233
251
  }
234
252
  throw error
@@ -313,13 +331,13 @@
313
331
  let i = 0;
314
332
  if (currentDecoder.keyMap) {
315
333
  while((key = read()) != STOP_CODE) {
316
- if (i++ >= maxMapSize) throw new Error(`Property count exceeds ${maxMapSize}`)
334
+ if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
317
335
  object[safeKey(currentDecoder.decodeKey(key))] = read();
318
336
  }
319
337
  }
320
338
  else {
321
339
  while ((key = read()) != STOP_CODE) {
322
- if (i++ >= maxMapSize) throw new Error(`Property count exceeds ${maxMapSize}`)
340
+ if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
323
341
  object[safeKey(key)] = read();
324
342
  }
325
343
  }
@@ -365,11 +383,13 @@
365
383
  case 1: // negative int
366
384
  return ~token
367
385
  case 2: // buffer
386
+ if (token > srcEnd - position$1) throw endOfCBORError()
368
387
  return readBin(token)
369
388
  case 3: // string
370
- if (srcStringEnd >= position$1) {
389
+ if (srcStringEnd >= position$1) { // already extracted, which only covers complete strings
371
390
  return srcString.slice(position$1 - srcStringStart, (position$1 += token) - srcStringStart)
372
391
  }
392
+ if (token > srcEnd - position$1) throw endOfCBORError()
373
393
  if (srcStringEnd == 0 && srcEnd < 140 && token < 32) {
374
394
  // for small blocks, avoiding the overhead of the extract call is helpful
375
395
  let string = token < 16 ? shortStringInJS(token) : longStringInJS(token);
@@ -378,19 +398,21 @@
378
398
  }
379
399
  return readFixedString(token)
380
400
  case 4: // array
381
- if (token >= maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
401
+ if (token > maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
382
402
  // every element occupies at least one byte, so a length beyond what remains in the source can
383
403
  // never be satisfied; check before allocating so a tiny header can not force a huge allocation
384
- if (token > srcEnd - position$1) throw endOfCBORError()
404
+ // nested arrays each pass the check above yet still preallocate, so also spend from a
405
+ // per-decode budget of the remaining bytes; the summed lengths of any valid tree stay under it
406
+ if (token > srcEnd - position$1 || (arrayAllocationBudget -= token) < 0) throw endOfCBORError()
385
407
  let array = new Array(token);
386
408
  //if (currentDecoder.keyMap) for (let i = 0; i < token; i++) array[i] = currentDecoder.decodeKey(read())
387
409
  //else
388
410
  for (let i = 0; i < token; i++) array[i] = read();
389
411
  return array
390
412
  case 5: // map
391
- if (token >= maxMapSize) throw new Error(`Map size exceeds ${maxArraySize}`)
392
- if (token > (srcEnd - position$1) / 2) throw endOfCBORError() // each entry needs at least two bytes
393
413
  if (currentDecoder.mapsAsObjects) {
414
+ if (token > objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
415
+ if (token > (srcEnd - position$1) / 2) throw endOfCBORError() // each entry needs at least two bytes
394
416
  let object = {};
395
417
  if (currentDecoder.keyMap) for (let i = 0; i < token; i++) object[safeKey(currentDecoder.decodeKey(read()))] = read();
396
418
  else for (let i = 0; i < token; i++) object[safeKey(read())] = read();
@@ -400,6 +422,8 @@
400
422
  currentDecoder.mapsAsObjects = true;
401
423
  restoreMapsAsObject = false;
402
424
  }
425
+ if (token > maxMapSize) throw new Error(`Map size exceeds ${maxMapSize}`)
426
+ if (token > (srcEnd - position$1) / 2) throw endOfCBORError()
403
427
  let map = new Map();
404
428
  if (currentDecoder.keyMap) for (let i = 0; i < token; i++) map.set(currentDecoder.decodeKey(read()),read());
405
429
  else for (let i = 0; i < token; i++) map.set(read(), read());
@@ -895,18 +919,12 @@
895
919
  return (glbl[data[0]] || Error)(data[1], data[2])
896
920
  };
897
921
  const packedTable = (read) => {
898
- if (src[position$1++] != 0x84) {
899
- let error = new Error('Packed values structure must be followed by a 4 element array');
900
- if (src.length < position$1)
901
- error.incomplete = true;
902
- throw error
903
- }
922
+ if (!(position$1 < srcEnd)) throw endOfCBORError()
923
+ if (src[position$1++] != 0x84)
924
+ throw new Error('Packed values structure must be followed by a 4 element array')
904
925
  let newPackedValues = read(); // packed values
905
- if (!newPackedValues || !newPackedValues.length) {
906
- let error = new Error('Packed values structure must be followed by a 4 element array');
907
- error.incomplete = true;
908
- throw error
909
- }
926
+ if (!Array.isArray(newPackedValues) || !newPackedValues.length)
927
+ throw new Error('Packed values structure must be followed by a 4 element array')
910
928
  packedValues = packedValues ? newPackedValues.concat(packedValues.slice(newPackedValues.length)) : newPackedValues;
911
929
  packedValues.prefixes = read();
912
930
  packedValues.suffixes = read();
@@ -924,10 +942,7 @@
924
942
  }
925
943
  if (typeof data == 'number')
926
944
  return packedValues[16 + (data >= 0 ? 2 * data : (-2 * data - 1))]
927
- let error = new Error('No support for non-integer packed references yet');
928
- if (data === undefined)
929
- error.incomplete = true;
930
- throw error
945
+ throw new Error('No support for non-integer packed references yet')
931
946
  };
932
947
 
933
948
  // The following code is an incomplete implementation of http://cbor.schmorp.de/stringref
@@ -997,9 +1012,19 @@
997
1012
  (currentExtensions[259] = (read) => {
998
1013
  // https://github.com/shanewholloway/js-cbor-codec/blob/master/docs/CBOR-259-spec
999
1014
  // for decoding as a standard Map
1000
- if (currentDecoder.mapsAsObjects) {
1001
- currentDecoder.mapsAsObjects = false;
1015
+ let decoder = currentDecoder;
1016
+ if (decoder.mapsAsObjects) {
1017
+ decoder.mapsAsObjects = false;
1002
1018
  restoreMapsAsObject = true;
1019
+ try {
1020
+ return read()
1021
+ } finally {
1022
+ // normally restored by the map header, but not if the tagged value is not a map or the read fails
1023
+ if (restoreMapsAsObject) {
1024
+ decoder.mapsAsObjects = true;
1025
+ restoreMapsAsObject = false;
1026
+ }
1027
+ }
1003
1028
  }
1004
1029
  return read()
1005
1030
  }).handlesRead = true;
@@ -1119,10 +1144,12 @@
1119
1144
  token = src[position$1++];
1120
1145
  break
1121
1146
  case 0x19:
1147
+ if (position$1 + 2 > srcEnd) throw endOfCBORError()
1122
1148
  token = dataView.getUint16(position$1);
1123
1149
  position$1 += 2;
1124
1150
  break
1125
1151
  case 0x1a:
1152
+ if (position$1 + 4 > srcEnd) throw endOfCBORError()
1126
1153
  token = dataView.getUint32(position$1);
1127
1154
  position$1 += 4;
1128
1155
  break
@@ -1162,20 +1189,27 @@
1162
1189
  let savedStructures = currentStructures;
1163
1190
  let savedDecoder = currentDecoder;
1164
1191
  let savedSequentialMode = sequentialMode;
1165
- let value = callback();
1166
- srcEnd = savedSrcEnd;
1167
- position$1 = savedPosition;
1168
- srcStringStart = savedSrcStringStart;
1169
- srcStringEnd = savedSrcStringEnd;
1170
- srcString = savedSrcString;
1171
- referenceMap = savedReferenceMap;
1172
- bundledStrings$1 = savedBundledStrings;
1173
- src = savedSrc;
1174
- sequentialMode = savedSequentialMode;
1175
- currentStructures = savedStructures;
1176
- currentDecoder = savedDecoder;
1177
- dataView = new DataView(src.buffer, src.byteOffset, src.byteLength);
1178
- return value
1192
+ let savedRestoreMapsAsObject = restoreMapsAsObject;
1193
+ let savedArrayAllocationBudget = arrayAllocationBudget;
1194
+ restoreMapsAsObject = false;
1195
+ try {
1196
+ return callback()
1197
+ } finally {
1198
+ srcEnd = savedSrcEnd;
1199
+ position$1 = savedPosition;
1200
+ srcStringStart = savedSrcStringStart;
1201
+ srcStringEnd = savedSrcStringEnd;
1202
+ srcString = savedSrcString;
1203
+ referenceMap = savedReferenceMap;
1204
+ bundledStrings$1 = savedBundledStrings;
1205
+ src = savedSrc;
1206
+ sequentialMode = savedSequentialMode;
1207
+ currentStructures = savedStructures;
1208
+ currentDecoder = savedDecoder;
1209
+ restoreMapsAsObject = savedRestoreMapsAsObject;
1210
+ arrayAllocationBudget = savedArrayAllocationBudget;
1211
+ dataView = new DataView(src.buffer, src.byteOffset, src.byteLength);
1212
+ }
1179
1213
  }
1180
1214
  function clearSource() {
1181
1215
  src = null;
@@ -1190,7 +1224,8 @@
1190
1224
  function setSizeLimits(limits) {
1191
1225
  if (limits.maxMapSize) maxMapSize = limits.maxMapSize;
1192
1226
  if (limits.maxArraySize) maxArraySize = limits.maxArraySize;
1193
- if (limits.maxObjectSize) limits.maxObjectSize;
1227
+ if (limits.maxObjectSize) maxObjectSize = limits.maxObjectSize;
1228
+ objectSizeLimit = Math.min(maxObjectSize, maxMapSize);
1194
1229
  }
1195
1230
 
1196
1231
  const mult10 = new Array(147); // this is a table matching binary exponents to the multiplier to determine significant digit rounding
@@ -1392,7 +1427,7 @@
1392
1427
  if (throwOnIterable)
1393
1428
  return;
1394
1429
  encode(value);
1395
- if (bundledStrings) {
1430
+ if (bundledStrings && bundledStrings.position) { // position is only set once a bundle has been started
1396
1431
  writeBundles(start, encode);
1397
1432
  }
1398
1433
  encoder.offset = position; // update the offset so next serialization doesn't write over our buffer, but can continue writing to same buffer sequentially
@@ -2490,7 +2525,7 @@
2490
2525
  }
2491
2526
 
2492
2527
  const decoder = new Decoder(options);
2493
- let incomplete;
2528
+ let incomplete, fatalError;
2494
2529
  const parser = (chunk) => {
2495
2530
  let yields;
2496
2531
  // if there's incomplete data from previous chunk, concatinate and try again
@@ -2502,26 +2537,28 @@
2502
2537
  try {
2503
2538
  yields = decoder.decodeMultiple(chunk);
2504
2539
  } catch (err) {
2540
+ yields = err.values;
2505
2541
  if (err.incomplete) {
2506
2542
  incomplete = chunk.slice(err.lastPosition);
2507
- yields = err.values;
2508
2543
  } else {
2509
- throw err
2544
+ fatalError = err;
2510
2545
  }
2511
2546
  }
2512
- return yields
2547
+ return yields || []
2513
2548
  };
2514
2549
 
2515
2550
  if (typeof bufferIterator[Symbol.iterator] === 'function') {
2516
2551
  return (function * iter () {
2517
2552
  for (const value of bufferIterator) {
2518
2553
  yield * parser(value);
2554
+ if (fatalError) throw fatalError
2519
2555
  }
2520
2556
  })()
2521
2557
  } else if (typeof bufferIterator[Symbol.asyncIterator] === 'function') {
2522
2558
  return (async function * iter () {
2523
2559
  for await (const value of bufferIterator) {
2524
2560
  yield * parser(value);
2561
+ if (fatalError) throw fatalError
2525
2562
  }
2526
2563
  })()
2527
2564
  }