cbor-x 1.6.6 → 1.6.7

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,13 @@
195
206
  }
196
207
  }
197
208
  }
209
+ const nestedDecodeErrors = new WeakSet();
198
210
  function checkedRead() {
199
211
  try {
200
212
  let result = read();
201
213
  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
- }
214
+ if (position$1 >= bundledStrings$1.postBundlePosition)
215
+ throw new Error('Unexpected bundle position')
207
216
  // bundled strings to skip past
208
217
  position$1 = bundledStrings$1.postBundlePosition;
209
218
  bundledStrings$1 = null;
@@ -227,7 +236,14 @@
227
236
  return result
228
237
  } catch(error) {
229
238
  clearSource();
230
- if (error instanceof RangeError || error.message.startsWith('Unexpected end of buffer')) {
239
+ if (nestedDecodeErrors.has(error)) {
240
+ // from a nested decode of embedded data (by an extension), which was complete in this source
241
+ if (error.incomplete)
242
+ error.incomplete = false;
243
+ } else if (error instanceof RangeError && position$1 + 8 > srcEnd ||
244
+ error && typeof error.message == 'string' && error.message.startsWith('Unexpected end of buffer')) {
245
+ // a DataView read past the end of the source; these read at most 8 bytes and precede advancing position.
246
+ // Any other RangeError (such as a stack overflow from deeply nested input) is fatal.
231
247
  error.incomplete = true;
232
248
  }
233
249
  throw error
@@ -312,13 +328,13 @@
312
328
  let i = 0;
313
329
  if (currentDecoder.keyMap) {
314
330
  while((key = read()) != STOP_CODE) {
315
- if (i++ >= maxMapSize) throw new Error(`Property count exceeds ${maxMapSize}`)
331
+ if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
316
332
  object[safeKey(currentDecoder.decodeKey(key))] = read();
317
333
  }
318
334
  }
319
335
  else {
320
336
  while ((key = read()) != STOP_CODE) {
321
- if (i++ >= maxMapSize) throw new Error(`Property count exceeds ${maxMapSize}`)
337
+ if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
322
338
  object[safeKey(key)] = read();
323
339
  }
324
340
  }
@@ -364,11 +380,13 @@
364
380
  case 1: // negative int
365
381
  return ~token
366
382
  case 2: // buffer
383
+ if (token > srcEnd - position$1) throw endOfCBORError()
367
384
  return readBin(token)
368
385
  case 3: // string
369
- if (srcStringEnd >= position$1) {
386
+ if (srcStringEnd >= position$1) { // already extracted, which only covers complete strings
370
387
  return srcString.slice(position$1 - srcStringStart, (position$1 += token) - srcStringStart)
371
388
  }
389
+ if (token > srcEnd - position$1) throw endOfCBORError()
372
390
  if (srcStringEnd == 0 && srcEnd < 140 && token < 32) {
373
391
  // for small blocks, avoiding the overhead of the extract call is helpful
374
392
  let string = token < 16 ? shortStringInJS(token) : longStringInJS(token);
@@ -377,7 +395,7 @@
377
395
  }
378
396
  return readFixedString(token)
379
397
  case 4: // array
380
- if (token >= maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
398
+ if (token > maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
381
399
  // every element occupies at least one byte, so a length beyond what remains in the source can
382
400
  // never be satisfied; check before allocating so a tiny header can not force a huge allocation
383
401
  if (token > srcEnd - position$1) throw endOfCBORError()
@@ -387,9 +405,9 @@
387
405
  for (let i = 0; i < token; i++) array[i] = read();
388
406
  return array
389
407
  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
408
  if (currentDecoder.mapsAsObjects) {
409
+ if (token > objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
410
+ if (token > (srcEnd - position$1) / 2) throw endOfCBORError() // each entry needs at least two bytes
393
411
  let object = {};
394
412
  if (currentDecoder.keyMap) for (let i = 0; i < token; i++) object[safeKey(currentDecoder.decodeKey(read()))] = read();
395
413
  else for (let i = 0; i < token; i++) object[safeKey(read())] = read();
@@ -399,6 +417,8 @@
399
417
  currentDecoder.mapsAsObjects = true;
400
418
  restoreMapsAsObject = false;
401
419
  }
420
+ if (token > maxMapSize) throw new Error(`Map size exceeds ${maxMapSize}`)
421
+ if (token > (srcEnd - position$1) / 2) throw endOfCBORError()
402
422
  let map = new Map();
403
423
  if (currentDecoder.keyMap) for (let i = 0; i < token; i++) map.set(currentDecoder.decodeKey(read()),read());
404
424
  else for (let i = 0; i < token; i++) map.set(read(), read());
@@ -894,18 +914,12 @@
894
914
  return (glbl[data[0]] || Error)(data[1], data[2])
895
915
  };
896
916
  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
- }
917
+ if (!(position$1 < srcEnd)) throw endOfCBORError()
918
+ if (src[position$1++] != 0x84)
919
+ throw new Error('Packed values structure must be followed by a 4 element array')
903
920
  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
- }
921
+ if (!Array.isArray(newPackedValues) || !newPackedValues.length)
922
+ throw new Error('Packed values structure must be followed by a 4 element array')
909
923
  packedValues = packedValues ? newPackedValues.concat(packedValues.slice(newPackedValues.length)) : newPackedValues;
910
924
  packedValues.prefixes = read();
911
925
  packedValues.suffixes = read();
@@ -923,10 +937,7 @@
923
937
  }
924
938
  if (typeof data == 'number')
925
939
  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
940
+ throw new Error('No support for non-integer packed references yet')
930
941
  };
931
942
 
932
943
  // The following code is an incomplete implementation of http://cbor.schmorp.de/stringref
@@ -996,9 +1007,19 @@
996
1007
  (currentExtensions[259] = (read) => {
997
1008
  // https://github.com/shanewholloway/js-cbor-codec/blob/master/docs/CBOR-259-spec
998
1009
  // for decoding as a standard Map
999
- if (currentDecoder.mapsAsObjects) {
1000
- currentDecoder.mapsAsObjects = false;
1010
+ let decoder = currentDecoder;
1011
+ if (decoder.mapsAsObjects) {
1012
+ decoder.mapsAsObjects = false;
1001
1013
  restoreMapsAsObject = true;
1014
+ try {
1015
+ return read()
1016
+ } finally {
1017
+ // normally restored by the map header, but not if the tagged value is not a map or the read fails
1018
+ if (restoreMapsAsObject) {
1019
+ decoder.mapsAsObjects = true;
1020
+ restoreMapsAsObject = false;
1021
+ }
1022
+ }
1002
1023
  }
1003
1024
  return read()
1004
1025
  }).handlesRead = true;
@@ -1118,10 +1139,12 @@
1118
1139
  token = src[position$1++];
1119
1140
  break
1120
1141
  case 0x19:
1142
+ if (position$1 + 2 > srcEnd) throw endOfCBORError()
1121
1143
  token = dataView.getUint16(position$1);
1122
1144
  position$1 += 2;
1123
1145
  break
1124
1146
  case 0x1a:
1147
+ if (position$1 + 4 > srcEnd) throw endOfCBORError()
1125
1148
  token = dataView.getUint32(position$1);
1126
1149
  position$1 += 4;
1127
1150
  break
@@ -1161,20 +1184,25 @@
1161
1184
  let savedStructures = currentStructures;
1162
1185
  let savedDecoder = currentDecoder;
1163
1186
  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
1187
+ let savedRestoreMapsAsObject = restoreMapsAsObject;
1188
+ restoreMapsAsObject = false;
1189
+ try {
1190
+ return callback()
1191
+ } finally {
1192
+ srcEnd = savedSrcEnd;
1193
+ position$1 = savedPosition;
1194
+ srcStringStart = savedSrcStringStart;
1195
+ srcStringEnd = savedSrcStringEnd;
1196
+ srcString = savedSrcString;
1197
+ referenceMap = savedReferenceMap;
1198
+ bundledStrings$1 = savedBundledStrings;
1199
+ src = savedSrc;
1200
+ sequentialMode = savedSequentialMode;
1201
+ currentStructures = savedStructures;
1202
+ currentDecoder = savedDecoder;
1203
+ restoreMapsAsObject = savedRestoreMapsAsObject;
1204
+ dataView = new DataView(src.buffer, src.byteOffset, src.byteLength);
1205
+ }
1178
1206
  }
1179
1207
  function clearSource() {
1180
1208
  src = null;
@@ -1189,7 +1217,8 @@
1189
1217
  function setSizeLimits(limits) {
1190
1218
  if (limits.maxMapSize) maxMapSize = limits.maxMapSize;
1191
1219
  if (limits.maxArraySize) maxArraySize = limits.maxArraySize;
1192
- if (limits.maxObjectSize) limits.maxObjectSize;
1220
+ if (limits.maxObjectSize) maxObjectSize = limits.maxObjectSize;
1221
+ objectSizeLimit = Math.min(maxObjectSize, maxMapSize);
1193
1222
  }
1194
1223
 
1195
1224
  const mult10 = new Array(147); // this is a table matching binary exponents to the multiplier to determine significant digit rounding
@@ -1391,7 +1420,7 @@
1391
1420
  if (throwOnIterable)
1392
1421
  return;
1393
1422
  encode(value);
1394
- if (bundledStrings) {
1423
+ if (bundledStrings && bundledStrings.position) { // position is only set once a bundle has been started
1395
1424
  writeBundles(start, encode);
1396
1425
  }
1397
1426
  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 +2518,7 @@
2489
2518
  }
2490
2519
 
2491
2520
  const decoder = new Decoder(options);
2492
- let incomplete;
2521
+ let incomplete, fatalError;
2493
2522
  const parser = (chunk) => {
2494
2523
  let yields;
2495
2524
  // if there's incomplete data from previous chunk, concatinate and try again
@@ -2501,26 +2530,28 @@
2501
2530
  try {
2502
2531
  yields = decoder.decodeMultiple(chunk);
2503
2532
  } catch (err) {
2533
+ yields = err.values;
2504
2534
  if (err.incomplete) {
2505
2535
  incomplete = chunk.slice(err.lastPosition);
2506
- yields = err.values;
2507
2536
  } else {
2508
- throw err
2537
+ fatalError = err;
2509
2538
  }
2510
2539
  }
2511
- return yields
2540
+ return yields || []
2512
2541
  };
2513
2542
 
2514
2543
  if (typeof bufferIterator[Symbol.iterator] === 'function') {
2515
2544
  return (function * iter () {
2516
2545
  for (const value of bufferIterator) {
2517
2546
  yield * parser(value);
2547
+ if (fatalError) throw fatalError
2518
2548
  }
2519
2549
  })()
2520
2550
  } else if (typeof bufferIterator[Symbol.asyncIterator] === 'function') {
2521
2551
  return (async function * iter () {
2522
2552
  for await (const value of bufferIterator) {
2523
2553
  yield * parser(value);
2554
+ if (fatalError) throw fatalError
2524
2555
  }
2525
2556
  })()
2526
2557
  }