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/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,20 +236,36 @@
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
234
250
  }
235
251
  }
236
252
 
253
+ function endOfCBORError() {
254
+ let error = new Error('Unexpected end of CBOR data');
255
+ error.incomplete = true;
256
+ return error
257
+ }
258
+
237
259
  function read() {
260
+ // compared as < so that a NaN position (from a corrupt length) also throws instead of reading undefined
261
+ if (!(position$1 < srcEnd)) throw endOfCBORError()
238
262
  let token = src[position$1++];
239
263
  let majorType = token >> 5;
240
264
  token = token & 0x1f;
241
265
  if (token > 0x17) {
242
266
  switch (token) {
243
267
  case 0x18:
268
+ if (position$1 >= srcEnd) throw endOfCBORError()
244
269
  token = src[position$1++];
245
270
  break
246
271
  case 0x19:
@@ -303,13 +328,13 @@
303
328
  let i = 0;
304
329
  if (currentDecoder.keyMap) {
305
330
  while((key = read()) != STOP_CODE) {
306
- if (i++ >= maxMapSize) throw new Error(`Property count exceeds ${maxMapSize}`)
331
+ if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
307
332
  object[safeKey(currentDecoder.decodeKey(key))] = read();
308
333
  }
309
334
  }
310
335
  else {
311
336
  while ((key = read()) != STOP_CODE) {
312
- if (i++ >= maxMapSize) throw new Error(`Property count exceeds ${maxMapSize}`)
337
+ if (i++ >= objectSizeLimit) throw new Error(`Property count exceeds ${objectSizeLimit}`)
313
338
  object[safeKey(key)] = read();
314
339
  }
315
340
  }
@@ -355,11 +380,13 @@
355
380
  case 1: // negative int
356
381
  return ~token
357
382
  case 2: // buffer
383
+ if (token > srcEnd - position$1) throw endOfCBORError()
358
384
  return readBin(token)
359
385
  case 3: // string
360
- if (srcStringEnd >= position$1) {
386
+ if (srcStringEnd >= position$1) { // already extracted, which only covers complete strings
361
387
  return srcString.slice(position$1 - srcStringStart, (position$1 += token) - srcStringStart)
362
388
  }
389
+ if (token > srcEnd - position$1) throw endOfCBORError()
363
390
  if (srcStringEnd == 0 && srcEnd < 140 && token < 32) {
364
391
  // for small blocks, avoiding the overhead of the extract call is helpful
365
392
  let string = token < 16 ? shortStringInJS(token) : longStringInJS(token);
@@ -368,15 +395,19 @@
368
395
  }
369
396
  return readFixedString(token)
370
397
  case 4: // array
371
- if (token >= maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
398
+ if (token > maxArraySize) throw new Error(`Array length exceeds ${maxArraySize}`)
399
+ // every element occupies at least one byte, so a length beyond what remains in the source can
400
+ // never be satisfied; check before allocating so a tiny header can not force a huge allocation
401
+ if (token > srcEnd - position$1) throw endOfCBORError()
372
402
  let array = new Array(token);
373
403
  //if (currentDecoder.keyMap) for (let i = 0; i < token; i++) array[i] = currentDecoder.decodeKey(read())
374
404
  //else
375
405
  for (let i = 0; i < token; i++) array[i] = read();
376
406
  return array
377
407
  case 5: // map
378
- if (token >= maxMapSize) throw new Error(`Map size exceeds ${maxArraySize}`)
379
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
380
411
  let object = {};
381
412
  if (currentDecoder.keyMap) for (let i = 0; i < token; i++) object[safeKey(currentDecoder.decodeKey(read()))] = read();
382
413
  else for (let i = 0; i < token; i++) object[safeKey(read())] = read();
@@ -386,6 +417,8 @@
386
417
  currentDecoder.mapsAsObjects = true;
387
418
  restoreMapsAsObject = false;
388
419
  }
420
+ if (token > maxMapSize) throw new Error(`Map size exceeds ${maxMapSize}`)
421
+ if (token > (srcEnd - position$1) / 2) throw endOfCBORError()
389
422
  let map = new Map();
390
423
  if (currentDecoder.keyMap) for (let i = 0; i < token; i++) map.set(currentDecoder.decodeKey(read()),read());
391
424
  else for (let i = 0; i < token; i++) map.set(read(), read());
@@ -467,11 +500,7 @@
467
500
  throw new Error('Unknown token ' + token)
468
501
  }
469
502
  default: // negative int
470
- if (isNaN(token)) {
471
- let error = new Error('Unexpected end of CBOR data');
472
- error.incomplete = true;
473
- throw error
474
- }
503
+ if (isNaN(token)) throw endOfCBORError()
475
504
  throw new Error('Unknown CBOR token ' + token)
476
505
  }
477
506
  }
@@ -885,18 +914,12 @@
885
914
  return (glbl[data[0]] || Error)(data[1], data[2])
886
915
  };
887
916
  const packedTable = (read) => {
888
- if (src[position$1++] != 0x84) {
889
- let error = new Error('Packed values structure must be followed by a 4 element array');
890
- if (src.length < position$1)
891
- error.incomplete = true;
892
- throw error
893
- }
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')
894
920
  let newPackedValues = read(); // packed values
895
- if (!newPackedValues || !newPackedValues.length) {
896
- let error = new Error('Packed values structure must be followed by a 4 element array');
897
- error.incomplete = true;
898
- throw error
899
- }
921
+ if (!Array.isArray(newPackedValues) || !newPackedValues.length)
922
+ throw new Error('Packed values structure must be followed by a 4 element array')
900
923
  packedValues = packedValues ? newPackedValues.concat(packedValues.slice(newPackedValues.length)) : newPackedValues;
901
924
  packedValues.prefixes = read();
902
925
  packedValues.suffixes = read();
@@ -914,10 +937,7 @@
914
937
  }
915
938
  if (typeof data == 'number')
916
939
  return packedValues[16 + (data >= 0 ? 2 * data : (-2 * data - 1))]
917
- let error = new Error('No support for non-integer packed references yet');
918
- if (data === undefined)
919
- error.incomplete = true;
920
- throw error
940
+ throw new Error('No support for non-integer packed references yet')
921
941
  };
922
942
 
923
943
  // The following code is an incomplete implementation of http://cbor.schmorp.de/stringref
@@ -987,9 +1007,19 @@
987
1007
  (currentExtensions[259] = (read) => {
988
1008
  // https://github.com/shanewholloway/js-cbor-codec/blob/master/docs/CBOR-259-spec
989
1009
  // for decoding as a standard Map
990
- if (currentDecoder.mapsAsObjects) {
991
- currentDecoder.mapsAsObjects = false;
1010
+ let decoder = currentDecoder;
1011
+ if (decoder.mapsAsObjects) {
1012
+ decoder.mapsAsObjects = false;
992
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
+ }
993
1023
  }
994
1024
  return read()
995
1025
  }).handlesRead = true;
@@ -1100,17 +1130,21 @@
1100
1130
  }
1101
1131
 
1102
1132
  function readJustLength() {
1133
+ if (!(position$1 < srcEnd)) throw endOfCBORError()
1103
1134
  let token = src[position$1++] & 0x1f;
1104
1135
  if (token > 0x17) {
1105
1136
  switch (token) {
1106
1137
  case 0x18:
1138
+ if (position$1 >= srcEnd) throw endOfCBORError()
1107
1139
  token = src[position$1++];
1108
1140
  break
1109
1141
  case 0x19:
1142
+ if (position$1 + 2 > srcEnd) throw endOfCBORError()
1110
1143
  token = dataView.getUint16(position$1);
1111
1144
  position$1 += 2;
1112
1145
  break
1113
1146
  case 0x1a:
1147
+ if (position$1 + 4 > srcEnd) throw endOfCBORError()
1114
1148
  token = dataView.getUint32(position$1);
1115
1149
  position$1 += 4;
1116
1150
  break
@@ -1150,20 +1184,25 @@
1150
1184
  let savedStructures = currentStructures;
1151
1185
  let savedDecoder = currentDecoder;
1152
1186
  let savedSequentialMode = sequentialMode;
1153
- let value = callback();
1154
- srcEnd = savedSrcEnd;
1155
- position$1 = savedPosition;
1156
- srcStringStart = savedSrcStringStart;
1157
- srcStringEnd = savedSrcStringEnd;
1158
- srcString = savedSrcString;
1159
- referenceMap = savedReferenceMap;
1160
- bundledStrings$1 = savedBundledStrings;
1161
- src = savedSrc;
1162
- sequentialMode = savedSequentialMode;
1163
- currentStructures = savedStructures;
1164
- currentDecoder = savedDecoder;
1165
- dataView = new DataView(src.buffer, src.byteOffset, src.byteLength);
1166
- 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
+ }
1167
1206
  }
1168
1207
  function clearSource() {
1169
1208
  src = null;
@@ -1178,7 +1217,8 @@
1178
1217
  function setSizeLimits(limits) {
1179
1218
  if (limits.maxMapSize) maxMapSize = limits.maxMapSize;
1180
1219
  if (limits.maxArraySize) maxArraySize = limits.maxArraySize;
1181
- if (limits.maxObjectSize) limits.maxObjectSize;
1220
+ if (limits.maxObjectSize) maxObjectSize = limits.maxObjectSize;
1221
+ objectSizeLimit = Math.min(maxObjectSize, maxMapSize);
1182
1222
  }
1183
1223
 
1184
1224
  const mult10 = new Array(147); // this is a table matching binary exponents to the multiplier to determine significant digit rounding
@@ -1380,7 +1420,7 @@
1380
1420
  if (throwOnIterable)
1381
1421
  return;
1382
1422
  encode(value);
1383
- if (bundledStrings) {
1423
+ if (bundledStrings && bundledStrings.position) { // position is only set once a bundle has been started
1384
1424
  writeBundles(start, encode);
1385
1425
  }
1386
1426
  encoder.offset = position; // update the offset so next serialization doesn't write over our buffer, but can continue writing to same buffer sequentially
@@ -2478,7 +2518,7 @@
2478
2518
  }
2479
2519
 
2480
2520
  const decoder = new Decoder(options);
2481
- let incomplete;
2521
+ let incomplete, fatalError;
2482
2522
  const parser = (chunk) => {
2483
2523
  let yields;
2484
2524
  // if there's incomplete data from previous chunk, concatinate and try again
@@ -2490,26 +2530,28 @@
2490
2530
  try {
2491
2531
  yields = decoder.decodeMultiple(chunk);
2492
2532
  } catch (err) {
2533
+ yields = err.values;
2493
2534
  if (err.incomplete) {
2494
2535
  incomplete = chunk.slice(err.lastPosition);
2495
- yields = err.values;
2496
2536
  } else {
2497
- throw err
2537
+ fatalError = err;
2498
2538
  }
2499
2539
  }
2500
- return yields
2540
+ return yields || []
2501
2541
  };
2502
2542
 
2503
2543
  if (typeof bufferIterator[Symbol.iterator] === 'function') {
2504
2544
  return (function * iter () {
2505
2545
  for (const value of bufferIterator) {
2506
2546
  yield * parser(value);
2547
+ if (fatalError) throw fatalError
2507
2548
  }
2508
2549
  })()
2509
2550
  } else if (typeof bufferIterator[Symbol.asyncIterator] === 'function') {
2510
2551
  return (async function * iter () {
2511
2552
  for await (const value of bufferIterator) {
2512
2553
  yield * parser(value);
2554
+ if (fatalError) throw fatalError
2513
2555
  }
2514
2556
  })()
2515
2557
  }