msgpackr 1.10.0 → 1.10.2

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/README.md CHANGED
@@ -188,6 +188,7 @@ The following options properties can be provided to the Packr or Unpackr constru
188
188
  * `encodeUndefinedAsNil` - Encodes a value of `undefined` as a MessagePack `nil`, the same as a `null`.
189
189
  * `int64AsType` - This will decode uint64 and int64 numbers as the specified type. The type can be `bigint` (default), `number`, `string`, or `auto` (where range [-2^53...2^53] is represented by number and everything else by a bigint).
190
190
  * `onInvalidDate` - This can be provided as function that will be called when an invalid date is provided. The function can throw an error, or return a value that will be encoded in place of the invalid date. If not provided, an invalid date will be encoded as an invalid timestamp (which decodes with msgpackr back to an invalid date).
191
+ * `writeFunction` - This can be provided as function that will be called when a function is encountered. The function can throw an error, or return a value that will be encoded in place of the function. If not provided, a function will be encoded as undefined (similar to `JSON.stringify`).
191
192
  * `mapAsEmptyObject` - Encodes JS `Map`s as empty objects (for back-compat with older libraries).
192
193
  * `setAsEmptyObject` - Encodes JS `Set`s as empty objects (for back-compat with older libraries).
193
194
 
@@ -859,7 +859,7 @@
859
859
  return readFixedString(length)
860
860
  } else { // not cacheable, go back and do a standard read
861
861
  position$1--;
862
- return read().toString()
862
+ return asSafeString(read())
863
863
  }
864
864
  let key = ((length << 5) ^ (length > 1 ? dataView.getUint16(position$1) : length > 0 ? src[position$1] : 0)) & 0xfff;
865
865
  let entry = keyCache[key];
@@ -911,9 +911,17 @@
911
911
  return entry.string = readFixedString(length)
912
912
  }
913
913
 
914
+ function asSafeString(property) {
915
+ // protect against expensive (DoS) string conversions
916
+ if (typeof property === 'string') return property;
917
+ if (typeof property === 'number' || typeof property === 'boolean' || typeof property === 'bigint') return property.toString();
918
+ if (property == null) return property + '';
919
+ throw new Error('Invalid property type for record', typeof property);
920
+ }
914
921
  // the registration of the record definition extension (as "r")
915
922
  const recordDefinition = (id, highByte) => {
916
- let structure = read().map(property => property.toString()); // ensure that all keys are strings and that the array is mutable
923
+ let structure = read().map(asSafeString); // ensure that all keys are strings and
924
+ // that the array is mutable
917
925
  let firstByte = id;
918
926
  if (highByte !== undefined) {
919
927
  id = id < 32 ? -((highByte << 5) + id) : ((highByte << 5) + id);
@@ -947,11 +955,12 @@
947
955
  let errors = { Error, TypeError, ReferenceError };
948
956
  currentExtensions[0x65] = () => {
949
957
  let data = read();
950
- return (errors[data[0]] || Error)(data[1])
958
+ return (errors[data[0]] || Error)(data[1], { cause: data[2] })
951
959
  };
952
960
 
953
961
  currentExtensions[0x69] = (data) => {
954
962
  // id extension (for structured clones)
963
+ if (currentUnpackr.structuredClone === false) throw new Error('Structured clone extension is disabled')
955
964
  let id = dataView.getUint32(position$1 - 4);
956
965
  if (!referenceMap)
957
966
  referenceMap = new Map();
@@ -975,6 +984,7 @@
975
984
 
976
985
  currentExtensions[0x70] = (data) => {
977
986
  // pointer extension (for structured clones)
987
+ if (currentUnpackr.structuredClone === false) throw new Error('Structured clone extension is disabled')
978
988
  let id = dataView.getUint32(position$1 - 4);
979
989
  let refEntry = referenceMap.get(id);
980
990
  refEntry.used = true;
@@ -989,8 +999,15 @@
989
999
  currentExtensions[0x74] = (data) => {
990
1000
  let typeCode = data[0];
991
1001
  let typedArrayName = typedArrays[typeCode];
992
- if (!typedArrayName)
1002
+ if (!typedArrayName) {
1003
+ if (typeCode === 16) {
1004
+ let ab = new ArrayBuffer(data.length - 1);
1005
+ let u8 = new Uint8Array(ab);
1006
+ u8.set(data.subarray(1));
1007
+ return ab;
1008
+ }
993
1009
  throw new Error('Could not find typed array for code ' + typeCode)
1010
+ }
994
1011
  // we have to always slice/copy here to get a new ArrayBuffer that is word/byte aligned
995
1012
  return new glbl[typedArrayName](Uint8Array.prototype.slice.call(data, 1).buffer)
996
1013
  };
@@ -1286,10 +1303,14 @@
1286
1303
  return packr.pack(value, encodeOptions)
1287
1304
  }
1288
1305
  packr.lastNamedStructuresLength = sharedLength;
1306
+ // don't keep large buffers around
1307
+ if (target.length > 0x40000000) target = null;
1289
1308
  return returnBuffer
1290
1309
  }
1291
1310
  }
1292
1311
  }
1312
+ // don't keep large buffers around, they take too much memory and cause problems (limit at 1GB)
1313
+ if (target.length > 0x40000000) target = null;
1293
1314
  if (encodeOptions & RESET_BUFFER_MODE)
1294
1315
  position = start;
1295
1316
  }
@@ -1512,7 +1533,7 @@
1512
1533
  }
1513
1534
  let constructor = value.constructor;
1514
1535
  if (constructor === Object) {
1515
- writeObject(value, true);
1536
+ writeObject(value);
1516
1537
  } else if (constructor === Array) {
1517
1538
  packArray(value);
1518
1539
  } else if (constructor === Map) {
@@ -1608,8 +1629,8 @@
1608
1629
  if (type === 'function')
1609
1630
  return pack(this.writeFunction && this.writeFunction(value));
1610
1631
 
1611
- // no extension found, write as object
1612
- writeObject(value, !value.hasOwnProperty); // if it doesn't have hasOwnProperty, don't do hasOwnProperty checks
1632
+ // no extension found, write as plain object
1633
+ writeObject(value);
1613
1634
  }
1614
1635
  }
1615
1636
  }
@@ -1696,13 +1717,13 @@
1696
1717
  }
1697
1718
  }
1698
1719
  } :
1699
- (object, safePrototype) => {
1720
+ (object) => {
1700
1721
  target[position++] = 0xde; // always using map 16, so we can preallocate and set the length afterwards
1701
1722
  let objectOffset = position - start;
1702
1723
  position += 2;
1703
1724
  let size = 0;
1704
1725
  for (let key in object) {
1705
- if (safePrototype || object.hasOwnProperty(key)) {
1726
+ if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) {
1706
1727
  pack(key);
1707
1728
  pack(object[key]);
1708
1729
  size++;
@@ -1714,12 +1735,12 @@
1714
1735
 
1715
1736
  const writeRecord = this.useRecords === false ? writePlainObject :
1716
1737
  (options.progressiveRecords && !useTwoByteRecords) ? // this is about 2% faster for highly stable structures, since it only requires one for-in loop (but much more expensive when new structure needs to be written)
1717
- (object, safePrototype) => {
1738
+ (object) => {
1718
1739
  let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
1719
1740
  let objectOffset = position++ - start;
1720
1741
  let wroteKeys;
1721
1742
  for (let key in object) {
1722
- if (safePrototype || object.hasOwnProperty(key)) {
1743
+ if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) {
1723
1744
  nextTransition = transition[key];
1724
1745
  if (nextTransition)
1725
1746
  transition = nextTransition;
@@ -1758,10 +1779,10 @@
1758
1779
  insertNewRecord(transition, Object.keys(object), objectOffset, 0);
1759
1780
  }
1760
1781
  } :
1761
- (object, safePrototype) => {
1782
+ (object) => {
1762
1783
  let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null));
1763
1784
  let newTransitions = 0;
1764
- for (let key in object) if (safePrototype || object.hasOwnProperty(key)) {
1785
+ for (let key in object) if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) {
1765
1786
  nextTransition = transition[key];
1766
1787
  if (!nextTransition) {
1767
1788
  nextTransition = transition[key] = Object.create(null);
@@ -1781,7 +1802,7 @@
1781
1802
  }
1782
1803
  // now write the values
1783
1804
  for (let key in object)
1784
- if (safePrototype || object.hasOwnProperty(key)) {
1805
+ if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) {
1785
1806
  pack(object[key]);
1786
1807
  }
1787
1808
  };
@@ -1789,8 +1810,8 @@
1789
1810
  // craete reference to useRecords if useRecords is a function
1790
1811
  const checkUseRecords = typeof this.useRecords == 'function' && this.useRecords;
1791
1812
 
1792
- const writeObject = checkUseRecords ? (object, safePrototype) => {
1793
- checkUseRecords(object) ? writeRecord(object,safePrototype) : writePlainObject(object,safePrototype);
1813
+ const writeObject = checkUseRecords ? (object) => {
1814
+ checkUseRecords(object) ? writeRecord(object) : writePlainObject(object);
1794
1815
  } : writeRecord;
1795
1816
 
1796
1817
  const makeRoom = (end) => {
@@ -1895,7 +1916,7 @@
1895
1916
  target[insertionOffset + start] = keysTarget[0];
1896
1917
  }
1897
1918
  };
1898
- const writeStruct = (object, safePrototype) => {
1919
+ const writeStruct = (object) => {
1899
1920
  let newPosition = writeStructSlots(object, target, start, position, structures, makeRoom, (value, newPosition, notifySharedUpdate) => {
1900
1921
  if (notifySharedUpdate)
1901
1922
  return hasSharedUpdate = true;
@@ -1909,7 +1930,7 @@
1909
1930
  return position;
1910
1931
  }, this);
1911
1932
  if (newPosition === 0) // bail and go to a msgpack object
1912
- return writeObject(object, true);
1933
+ return writeObject(object);
1913
1934
  position = newPosition;
1914
1935
  };
1915
1936
  }
@@ -1987,7 +2008,7 @@
1987
2008
  target[position++] = 0x65; // 'e' for error
1988
2009
  target[position++] = 0;
1989
2010
  }
1990
- pack([ error.name, error.message ]);
2011
+ pack([ error.name, error.message, error.cause ]);
1991
2012
  }
1992
2013
  }, {
1993
2014
  pack(regex, allocateForWrite, pack) {
@@ -2040,6 +2061,7 @@
2040
2061
  }
2041
2062
  target[position++] = 0x74; // "t" for typed array
2042
2063
  target[position++] = type;
2064
+ if (!typedArray.buffer) typedArray = new Uint8Array(typedArray);
2043
2065
  target.set(new Uint8Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength), position);
2044
2066
  }
2045
2067
  function writeBuffer(buffer, allocateForWrite) {