cbor-x 1.6.5 → 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/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,13 @@ class Decoder {
201
211
  }
202
212
  }
203
213
  }
214
+ const nestedDecodeErrors = new WeakSet();
204
215
  function checkedRead() {
205
216
  try {
206
217
  let result = read();
207
218
  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
- }
219
+ if (position$1 >= bundledStrings$1.postBundlePosition)
220
+ throw new Error('Unexpected bundle position')
213
221
  // bundled strings to skip past
214
222
  position$1 = bundledStrings$1.postBundlePosition;
215
223
  bundledStrings$1 = null;
@@ -233,20 +241,36 @@ function checkedRead() {
233
241
  return result
234
242
  } catch(error) {
235
243
  clearSource();
236
- 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.
237
252
  error.incomplete = true;
238
253
  }
239
254
  throw error
240
255
  }
241
256
  }
242
257
 
258
+ function endOfCBORError() {
259
+ let error = new Error('Unexpected end of CBOR data');
260
+ error.incomplete = true;
261
+ return error
262
+ }
263
+
243
264
  function read() {
265
+ // compared as < so that a NaN position (from a corrupt length) also throws instead of reading undefined
266
+ if (!(position$1 < srcEnd)) throw endOfCBORError()
244
267
  let token = src[position$1++];
245
268
  let majorType = token >> 5;
246
269
  token = token & 0x1f;
247
270
  if (token > 0x17) {
248
271
  switch (token) {
249
272
  case 0x18:
273
+ if (position$1 >= srcEnd) throw endOfCBORError()
250
274
  token = src[position$1++];
251
275
  break
252
276
  case 0x19:
@@ -309,13 +333,13 @@ function read() {
309
333
  let i = 0;
310
334
  if (currentDecoder.keyMap) {
311
335
  while((key = read()) != STOP_CODE) {
312
- if (i++ >= maxMapSize) throw new Error(`Property count exceeds ${maxMapSize}`)
336
+ if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
313
337
  object[safeKey(currentDecoder.decodeKey(key))] = read();
314
338
  }
315
339
  }
316
340
  else {
317
341
  while ((key = read()) != STOP_CODE) {
318
- if (i++ >= maxMapSize) throw new Error(`Property count exceeds ${maxMapSize}`)
342
+ if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
319
343
  object[safeKey(key)] = read();
320
344
  }
321
345
  }
@@ -361,11 +385,13 @@ function read() {
361
385
  case 1: // negative int
362
386
  return ~token
363
387
  case 2: // buffer
388
+ if (token > srcEnd - position$1) throw endOfCBORError()
364
389
  return readBin(token)
365
390
  case 3: // string
366
- if (srcStringEnd >= position$1) {
391
+ if (srcStringEnd >= position$1) { // already extracted, which only covers complete strings
367
392
  return srcString.slice(position$1 - srcStringStart, (position$1 += token) - srcStringStart)
368
393
  }
394
+ if (token > srcEnd - position$1) throw endOfCBORError()
369
395
  if (srcStringEnd == 0 && srcEnd < 140 && token < 32) {
370
396
  // for small blocks, avoiding the overhead of the extract call is helpful
371
397
  let string = token < 16 ? shortStringInJS(token) : longStringInJS(token);
@@ -374,15 +400,19 @@ function read() {
374
400
  }
375
401
  return readFixedString(token)
376
402
  case 4: // array
377
- if (token >= maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
403
+ if (token > maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
404
+ // every element occupies at least one byte, so a length beyond what remains in the source can
405
+ // never be satisfied; check before allocating so a tiny header can not force a huge allocation
406
+ if (token > srcEnd - position$1) throw endOfCBORError()
378
407
  let array = new Array(token);
379
408
  //if (currentDecoder.keyMap) for (let i = 0; i < token; i++) array[i] = currentDecoder.decodeKey(read())
380
409
  //else
381
410
  for (let i = 0; i < token; i++) array[i] = read();
382
411
  return array
383
412
  case 5: // map
384
- if (token >= maxMapSize) throw new Error(`Map size exceeds ${maxArraySize}`)
385
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
386
416
  let object = {};
387
417
  if (currentDecoder.keyMap) for (let i = 0; i < token; i++) object[safeKey(currentDecoder.decodeKey(read()))] = read();
388
418
  else for (let i = 0; i < token; i++) object[safeKey(read())] = read();
@@ -392,6 +422,8 @@ function read() {
392
422
  currentDecoder.mapsAsObjects = true;
393
423
  restoreMapsAsObject = false;
394
424
  }
425
+ if (token > maxMapSize) throw new Error(`Map size exceeds ${maxMapSize}`)
426
+ if (token > (srcEnd - position$1) / 2) throw endOfCBORError()
395
427
  let map = new Map();
396
428
  if (currentDecoder.keyMap) for (let i = 0; i < token; i++) map.set(currentDecoder.decodeKey(read()),read());
397
429
  else for (let i = 0; i < token; i++) map.set(read(), read());
@@ -473,11 +505,7 @@ function read() {
473
505
  throw new Error('Unknown token ' + token)
474
506
  }
475
507
  default: // negative int
476
- if (isNaN(token)) {
477
- let error = new Error('Unexpected end of CBOR data');
478
- error.incomplete = true;
479
- throw error
480
- }
508
+ if (isNaN(token)) throw endOfCBORError()
481
509
  throw new Error('Unknown CBOR token ' + token)
482
510
  }
483
511
  }
@@ -926,18 +954,12 @@ currentExtensions[27] = (data) => { // http://cbor.schmorp.de/generic-object
926
954
  return (glbl[data[0]] || Error)(data[1], data[2])
927
955
  };
928
956
  const packedTable = (read) => {
929
- if (src[position$1++] != 0x84) {
930
- let error = new Error('Packed values structure must be followed by a 4 element array');
931
- if (src.length < position$1)
932
- error.incomplete = true;
933
- throw error
934
- }
957
+ if (!(position$1 < srcEnd)) throw endOfCBORError()
958
+ if (src[position$1++] != 0x84)
959
+ throw new Error('Packed values structure must be followed by a 4 element array')
935
960
  let newPackedValues = read(); // packed values
936
- if (!newPackedValues || !newPackedValues.length) {
937
- let error = new Error('Packed values structure must be followed by a 4 element array');
938
- error.incomplete = true;
939
- throw error
940
- }
961
+ if (!Array.isArray(newPackedValues) || !newPackedValues.length)
962
+ throw new Error('Packed values structure must be followed by a 4 element array')
941
963
  packedValues = packedValues ? newPackedValues.concat(packedValues.slice(newPackedValues.length)) : newPackedValues;
942
964
  packedValues.prefixes = read();
943
965
  packedValues.suffixes = read();
@@ -955,10 +977,7 @@ currentExtensions[PACKED_REFERENCE_TAG_ID] = (data) => { // packed reference
955
977
  }
956
978
  if (typeof data == 'number')
957
979
  return packedValues[16 + (data >= 0 ? 2 * data : (-2 * data - 1))]
958
- let error = new Error('No support for non-integer packed references yet');
959
- if (data === undefined)
960
- error.incomplete = true;
961
- throw error
980
+ throw new Error('No support for non-integer packed references yet')
962
981
  };
963
982
 
964
983
  // The following code is an incomplete implementation of http://cbor.schmorp.de/stringref
@@ -1028,9 +1047,19 @@ currentExtensions[258] = (array) => new Set(array); // https://github.com/input-
1028
1047
  (currentExtensions[259] = (read) => {
1029
1048
  // https://github.com/shanewholloway/js-cbor-codec/blob/master/docs/CBOR-259-spec
1030
1049
  // for decoding as a standard Map
1031
- if (currentDecoder.mapsAsObjects) {
1032
- currentDecoder.mapsAsObjects = false;
1050
+ let decoder = currentDecoder;
1051
+ if (decoder.mapsAsObjects) {
1052
+ decoder.mapsAsObjects = false;
1033
1053
  restoreMapsAsObject = true;
1054
+ try {
1055
+ return read()
1056
+ } finally {
1057
+ // normally restored by the map header, but not if the tagged value is not a map or the read fails
1058
+ if (restoreMapsAsObject) {
1059
+ decoder.mapsAsObjects = true;
1060
+ restoreMapsAsObject = false;
1061
+ }
1062
+ }
1034
1063
  }
1035
1064
  return read()
1036
1065
  }).handlesRead = true;
@@ -1141,17 +1170,21 @@ function readBundleExt() {
1141
1170
  }
1142
1171
 
1143
1172
  function readJustLength() {
1173
+ if (!(position$1 < srcEnd)) throw endOfCBORError()
1144
1174
  let token = src[position$1++] & 0x1f;
1145
1175
  if (token > 0x17) {
1146
1176
  switch (token) {
1147
1177
  case 0x18:
1178
+ if (position$1 >= srcEnd) throw endOfCBORError()
1148
1179
  token = src[position$1++];
1149
1180
  break
1150
1181
  case 0x19:
1182
+ if (position$1 + 2 > srcEnd) throw endOfCBORError()
1151
1183
  token = dataView.getUint16(position$1);
1152
1184
  position$1 += 2;
1153
1185
  break
1154
1186
  case 0x1a:
1187
+ if (position$1 + 4 > srcEnd) throw endOfCBORError()
1155
1188
  token = dataView.getUint32(position$1);
1156
1189
  position$1 += 4;
1157
1190
  break
@@ -1193,22 +1226,27 @@ function saveState(callback) {
1193
1226
  let savedStructures = currentStructures;
1194
1227
  let savedDecoder = currentDecoder;
1195
1228
  let savedSequentialMode = sequentialMode;
1196
- let value = callback();
1197
- srcEnd = savedSrcEnd;
1198
- position$1 = savedPosition;
1199
- stringPosition = savedStringPosition;
1200
- srcStringStart = savedSrcStringStart;
1201
- srcStringEnd = savedSrcStringEnd;
1202
- srcString = savedSrcString;
1203
- strings = savedStrings;
1204
- referenceMap = savedReferenceMap;
1205
- bundledStrings$1 = savedBundledStrings;
1206
- src = savedSrc;
1207
- sequentialMode = savedSequentialMode;
1208
- currentStructures = savedStructures;
1209
- currentDecoder = savedDecoder;
1210
- dataView = new DataView(src.buffer, src.byteOffset, src.byteLength);
1211
- return value
1229
+ let savedRestoreMapsAsObject = restoreMapsAsObject;
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
+ dataView = new DataView(src.buffer, src.byteOffset, src.byteLength);
1249
+ }
1212
1250
  }
1213
1251
  function clearSource() {
1214
1252
  src = null;
@@ -1223,7 +1261,8 @@ function addExtension$1(extension) {
1223
1261
  function setSizeLimits(limits) {
1224
1262
  if (limits.maxMapSize) maxMapSize = limits.maxMapSize;
1225
1263
  if (limits.maxArraySize) maxArraySize = limits.maxArraySize;
1226
- if (limits.maxObjectSize) limits.maxObjectSize;
1264
+ if (limits.maxObjectSize) maxObjectSize = limits.maxObjectSize;
1265
+ objectSizeLimit = Math.min(maxObjectSize, maxMapSize);
1227
1266
  }
1228
1267
 
1229
1268
  const mult10 = new Array(147); // this is a table matching binary exponents to the multiplier to determine significant digit rounding
@@ -1425,7 +1464,7 @@ class Encoder extends Decoder {
1425
1464
  if (throwOnIterable)
1426
1465
  return;
1427
1466
  encode(value);
1428
- if (bundledStrings) {
1467
+ if (bundledStrings && bundledStrings.position) { // position is only set once a bundle has been started
1429
1468
  writeBundles(start, encode);
1430
1469
  }
1431
1470
  encoder.offset = position; // update the offset so next serialization doesn't write over our buffer, but can continue writing to same buffer sequentially
@@ -2510,24 +2549,22 @@ class DecoderStream extends stream.Transform {
2510
2549
  chunk = Buffer.concat([this.incompleteBuffer, chunk]);
2511
2550
  this.incompleteBuffer = null;
2512
2551
  }
2513
- let values;
2552
+ let values, fatalError;
2514
2553
  try {
2515
2554
  values = this.decoder.decodeMultiple(chunk);
2516
2555
  } catch(error) {
2517
- if (error.incomplete) {
2556
+ values = error.values;
2557
+ if (error.incomplete)
2518
2558
  this.incompleteBuffer = chunk.slice(error.lastPosition);
2519
- values = error.values;
2520
- } else {
2521
- return callback(error)
2522
- }
2523
- } finally {
2524
- for (let value of values || []) {
2525
- if (value === null)
2526
- value = this.getNullValue();
2527
- this.push(value);
2528
- }
2559
+ else
2560
+ fatalError = error;
2561
+ }
2562
+ for (let value of values || []) {
2563
+ if (value === null)
2564
+ value = this.getNullValue();
2565
+ this.push(value);
2529
2566
  }
2530
- callback();
2567
+ callback(fatalError);
2531
2568
  }
2532
2569
  getNullValue() {
2533
2570
  return Symbol.for(null)
@@ -2580,7 +2617,7 @@ function decodeIter (bufferIterator, options = {}) {
2580
2617
  }
2581
2618
 
2582
2619
  const decoder = new Decoder(options);
2583
- let incomplete;
2620
+ let incomplete, fatalError;
2584
2621
  const parser = (chunk) => {
2585
2622
  let yields;
2586
2623
  // if there's incomplete data from previous chunk, concatinate and try again
@@ -2592,26 +2629,28 @@ function decodeIter (bufferIterator, options = {}) {
2592
2629
  try {
2593
2630
  yields = decoder.decodeMultiple(chunk);
2594
2631
  } catch (err) {
2632
+ yields = err.values;
2595
2633
  if (err.incomplete) {
2596
2634
  incomplete = chunk.slice(err.lastPosition);
2597
- yields = err.values;
2598
2635
  } else {
2599
- throw err
2636
+ fatalError = err;
2600
2637
  }
2601
2638
  }
2602
- return yields
2639
+ return yields || []
2603
2640
  };
2604
2641
 
2605
2642
  if (typeof bufferIterator[Symbol.iterator] === 'function') {
2606
2643
  return (function * iter () {
2607
2644
  for (const value of bufferIterator) {
2608
2645
  yield * parser(value);
2646
+ if (fatalError) throw fatalError
2609
2647
  }
2610
2648
  })()
2611
2649
  } else if (typeof bufferIterator[Symbol.asyncIterator] === 'function') {
2612
2650
  return (async function * iter () {
2613
2651
  for await (const value of bufferIterator) {
2614
2652
  yield * parser(value);
2653
+ if (fatalError) throw fatalError
2615
2654
  }
2616
2655
  })()
2617
2656
  }