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