cbor-x 1.5.7 → 1.5.9

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/test.js CHANGED
@@ -499,7 +499,12 @@
499
499
  }
500
500
 
501
501
  function safeKey(key) {
502
- return key === '__proto__' ? '__proto_' : key
502
+ // protect against prototype pollution
503
+ if (typeof key === 'string') return key === '__proto__' ? '__proto_' : key
504
+ if (typeof key === 'number' || typeof key === 'boolean' || typeof key === 'bigint') return key.toString();
505
+ if (key == null) return key + '';
506
+ // protect against expensive (DoS) string conversions
507
+ throw new Error('Invalid property name type ' + typeof key);
503
508
  }
504
509
 
505
510
  let readFixedString = readStringJS;
@@ -914,6 +919,7 @@
914
919
  referenceMap.id = 0;
915
920
  }
916
921
  let id = referenceMap.id++;
922
+ let startingPosition = position$1;
917
923
  let token = src[position$1];
918
924
  let target;
919
925
  // TODO: handle Maps, Sets, and other types that can cycle; this is complicated, because you potentially need to read
@@ -926,8 +932,20 @@
926
932
  let refEntry = { target }; // a placeholder object
927
933
  referenceMap.set(id, refEntry);
928
934
  let targetProperties = read(); // read the next value as the target object to id
929
- if (refEntry.used) // there is a cycle, so we have to assign properties to original target
935
+ if (refEntry.used) {// there is a cycle, so we have to assign properties to original target
936
+ if (Object.getPrototypeOf(target) !== Object.getPrototypeOf(targetProperties)) {
937
+ // this means that the returned target does not match the targetProperties, so we need rerun the read to
938
+ // have the correctly create instance be assigned as a reference, then we do the copy the properties back to the
939
+ // target
940
+ // reset the position so that the read can be repeated
941
+ position$1 = startingPosition;
942
+ // the returned instance is our new target for references
943
+ target = targetProperties;
944
+ referenceMap.set(id, { target });
945
+ targetProperties = read();
946
+ }
930
947
  return Object.assign(target, targetProperties)
948
+ }
931
949
  refEntry.target = targetProperties; // the placeholder wasn't used, replace with the deserialized one
932
950
  return targetProperties // no cycle, can just use the returned read object
933
951
  };
@@ -1009,7 +1027,7 @@
1009
1027
  for (let littleEndian = 0; littleEndian < 2; littleEndian++) {
1010
1028
  if (!littleEndian && bytesPerElement == 1)
1011
1029
  continue
1012
- let sizeShift = bytesPerElement == 2 ? 1 : bytesPerElement == 4 ? 2 : 3;
1030
+ let sizeShift = bytesPerElement == 2 ? 1 : bytesPerElement == 4 ? 2 : bytesPerElement == 8 ? 3 : 0;
1013
1031
  currentExtensions[littleEndian ? tag : (tag - 4)] = (bytesPerElement == 1 || littleEndian == isLittleEndianMachine$1) ? (buffer) => {
1014
1032
  if (!TypedArray)
1015
1033
  throw new Error('Could not find typed array for code ' + tag)
@@ -1019,7 +1037,7 @@
1019
1037
  bytesPerElement === 2 && !(buffer.byteOffset & 1) ||
1020
1038
  bytesPerElement === 4 && !(buffer.byteOffset & 3) ||
1021
1039
  bytesPerElement === 8 && !(buffer.byteOffset & 7))
1022
- return new TypedArray(buffer.buffer, buffer.byteOffset, buffer.byteLength);
1040
+ return new TypedArray(buffer.buffer, buffer.byteOffset, buffer.byteLength >> sizeShift);
1023
1041
  }
1024
1042
  // we have to slice/copy here to get a new ArrayBuffer, if we are not word/byte aligned
1025
1043
  return new TypedArray(Uint8Array.prototype.slice.call(buffer, 0).buffer)
@@ -1618,7 +1636,7 @@
1618
1636
  }
1619
1637
  let constructor = value.constructor;
1620
1638
  if (constructor === Object) {
1621
- writeObject(value, true);
1639
+ writeObject(value);
1622
1640
  } else if (constructor === Array) {
1623
1641
  length = value.length;
1624
1642
  if (length < 0x18) {
@@ -1713,8 +1731,8 @@
1713
1731
  return encode(json)
1714
1732
  }
1715
1733
 
1716
- // no extension found, write as object
1717
- writeObject(value, !value.hasOwnProperty); // if it doesn't have hasOwnProperty, don't do hasOwnProperty checks
1734
+ // no extension found, write as a plain object
1735
+ writeObject(value);
1718
1736
  }
1719
1737
  }
1720
1738
  } else if (type === 'boolean') {
@@ -1776,19 +1794,19 @@
1776
1794
  }
1777
1795
  }
1778
1796
  } :
1779
- (object, safePrototype) => {
1797
+ (object) => {
1780
1798
  target[position++] = 0xb9; // always use map 16, so we can preallocate and set the length afterwards
1781
1799
  let objectOffset = position - start;
1782
1800
  position += 2;
1783
1801
  let size = 0;
1784
1802
  if (encoder.keyMap) {
1785
- for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
1803
+ for (let key in object) if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) {
1786
1804
  encode(encoder.encodeKey(key));
1787
1805
  encode(object[key]);
1788
1806
  size++;
1789
1807
  }
1790
1808
  } else {
1791
- for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
1809
+ for (let key in object) if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) {
1792
1810
  encode(key);
1793
1811
  encode(object[key]);
1794
1812
  size++;
@@ -1797,7 +1815,7 @@
1797
1815
  target[objectOffset++ + start] = size >> 8;
1798
1816
  target[objectOffset + start] = size & 0xff;
1799
1817
  } :
1800
- (object, safePrototype) => {
1818
+ (object, skipValues) => {
1801
1819
  let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
1802
1820
  let newTransitions = 0;
1803
1821
  let length = 0;
@@ -1816,7 +1834,7 @@
1816
1834
  transition = nextTransition;
1817
1835
  }
1818
1836
  } else {
1819
- for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
1837
+ for (let key in object) if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) {
1820
1838
  nextTransition = transition[key];
1821
1839
  if (!nextTransition) {
1822
1840
  if (transition[RECORD_SYMBOL] & 0x100000) {// this indicates it is a brancheable/extendable terminal node, so we will use this record id and extend it
@@ -1876,9 +1894,9 @@
1876
1894
  writeArrayHeader(length + 2);
1877
1895
  encode(0xe000 + recordId);
1878
1896
  encode(keys);
1879
- if (safePrototype === null) return; // special exit for iterator
1897
+ if (skipValues) return; // special exit for iterator
1880
1898
  for (let key in object)
1881
- if (safePrototype || object.hasOwnProperty(key))
1899
+ if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key))
1882
1900
  encode(object[key]);
1883
1901
  return
1884
1902
  }
@@ -1888,9 +1906,9 @@
1888
1906
  } else {
1889
1907
  writeArrayHeader(length);
1890
1908
  }
1891
- if (safePrototype === null) return; // special exit for iterator
1909
+ if (skipValues) return; // special exit for iterator
1892
1910
  for (let key in object)
1893
- if (safePrototype || object.hasOwnProperty(key))
1911
+ if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key))
1894
1912
  encode(object[key]);
1895
1913
  };
1896
1914
  const makeRoom = (end) => {
@@ -1928,7 +1946,7 @@
1928
1946
  if (constructor === Object) {
1929
1947
  let useRecords = encoder.useRecords !== false;
1930
1948
  if (useRecords)
1931
- writeObject(object, null); // write the record identifier
1949
+ writeObject(object, true); // write the record identifier
1932
1950
  else
1933
1951
  writeEntityLength(Object.keys(object).length, 0xa0);
1934
1952
  for (let key in object) {
@@ -2645,22 +2663,17 @@
2645
2663
  assert.deepEqual(deserialized1, data1);
2646
2664
  assert.deepEqual(deserialized2, data2);
2647
2665
  });
2648
- test('extended class encode/decode', function(){
2666
+ test('extended class encode/decode', function() {
2649
2667
  function Extended() {
2650
2668
 
2651
2669
  }
2670
+
2652
2671
  Extended.prototype.getDouble = function() {
2653
2672
  return this.value * 2
2654
2673
  };
2655
2674
  var instance = new Extended();
2656
2675
  instance.value = 4;
2657
2676
  instance.string = 'decode this: ᾜ';
2658
- var data = {
2659
- prop1: 'has multi-byte: ᾜ',
2660
- extendedInstance: instance,
2661
- prop2: 'more string',
2662
- num: 3,
2663
- };
2664
2677
  let encoder = new Encoder();
2665
2678
  addExtension({
2666
2679
  Class: Extended,
@@ -2675,10 +2688,37 @@
2675
2688
  return encoder.encode([instance.value, instance.string])
2676
2689
  }
2677
2690
  });
2678
- var serialized = encode(data);
2679
- var deserialized = decode(serialized);
2680
- assert.deepEqual(data, deserialized);
2681
- assert.equal(deserialized.extendedInstance.getDouble(), 8);
2691
+ });
2692
+ test('extended class encode/decode with self reference in structered clone', function(){
2693
+ function Extended() {
2694
+
2695
+ }
2696
+ addExtension({
2697
+ Class: Extended,
2698
+ tag: 301,
2699
+ decode: function(data) {
2700
+ let e = new Extended();
2701
+ e.value = data[0];
2702
+ e.string = data[1];
2703
+ return e
2704
+ },
2705
+ encode: function(instance, encode) {
2706
+ return encode([instance.value, instance.string])
2707
+ }
2708
+ });
2709
+ var instance = new Extended();
2710
+ instance.value = instance;
2711
+ instance.string = 'hi';
2712
+ let data = {
2713
+ extended: instance
2714
+ };
2715
+ let encoder = new Encoder({
2716
+ structuredClone: true,
2717
+ });
2718
+ let serialized = encoder.encode(data);
2719
+ encoder.decode(serialized);
2720
+ assert(data.extended.value.value === data.extended);
2721
+ assert(data.extended instanceof Extended);
2682
2722
  });
2683
2723
 
2684
2724
  test('addExtension with map', function(){
@@ -2820,6 +2860,12 @@
2820
2860
  var deserialized = decode(serialized);
2821
2861
  assert.deepEqual(deserialized, data);
2822
2862
  });
2863
+ test('object with __proto__', function(){
2864
+ const data = { foo: 'bar', __proto__: { isAdmin: true } };
2865
+ var serialized = encode(data);
2866
+ var deserialized = decode(serialized);
2867
+ assert.deepEqual(deserialized, { foo: 'bar' });
2868
+ });
2823
2869
 
2824
2870
  test('big buffer', function() {
2825
2871
  var size = 100000000;
@@ -3088,6 +3134,11 @@
3088
3134
  let badInput = Buffer.from('7b2273657269616c6e6f223a2265343a30222c226970223a223139322e3136382e312e3335222c226b6579223a226770735f736563726574227d', 'hex');
3089
3135
  assert.throws(function(){ decode(badInput); }); // should throw, not crash
3090
3136
  });
3137
+ test('buffer key', function() {
3138
+ let encoder = new Encoder({ mapsAsObjects: false });
3139
+ let test = encoder.decode(Buffer.from('D87982A1446E616D654361626301', 'hex'));
3140
+ console.log(test);
3141
+ });
3091
3142
  test('encode as iterator', function() {
3092
3143
  let hasIterables = {
3093
3144
  a: 1,
@@ -3180,6 +3231,15 @@
3180
3231
  decode(Buffer.concat(result));
3181
3232
  console.log(performance.now() - start, result.length);
3182
3233
  });
3234
+
3235
+ test('little-endian typed array with aligned data', function() {
3236
+ // array[1] { uint32-little-endian-typed-array { bytes <00 00 00 00> } }
3237
+ let data = new Uint8Array([ 129, 216, 70, 68, 0, 0, 0, 0 ]);
3238
+ assert.deepEqual(decode(data), [new Uint32Array([0])]);
3239
+
3240
+ let value = {x: new Float32Array([1, 2, 3])};
3241
+ assert.deepEqual(decode(encode(value)), value);
3242
+ });
3183
3243
  });
3184
3244
  suite('CBOR performance tests', function(){
3185
3245
  test('performance JSON.parse', function() {