@node-red/util 4.1.13 → 4.1.14

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.
Files changed (2) hide show
  1. package/lib/util.js +55 -9
  2. package/package.json +2 -2
package/lib/util.js CHANGED
@@ -24,7 +24,6 @@ const jsonata = require("jsonata");
24
24
  const moment = require("moment-timezone");
25
25
  const safeJSONStringify = require("json-stringify-safe");
26
26
  const util = require("util");
27
- const { hasOwnProperty } = Object.prototype;
28
27
  const log = require("./log")
29
28
  /**
30
29
  * Safely returns the object constructor name.
@@ -182,7 +181,7 @@ function compareObjects(obj1,obj2) {
182
181
  }
183
182
  for (var k in obj1) {
184
183
  /* istanbul ignore else */
185
- if (hasOwnProperty.call(obj1, k)) {
184
+ if (Object.hasOwn(obj1, k)) {
186
185
  if (!compareObjects(obj1[k],obj2[k])) {
187
186
  return false;
188
187
  }
@@ -473,7 +472,7 @@ function setObjectProperty(msg,prop,value,createMissing) {
473
472
  for (var i=0;i<length-1;i++) {
474
473
  key = msgPropParts[i];
475
474
  if (typeof key === 'string' || (typeof key === 'number' && !Array.isArray(obj))) {
476
- if (hasOwnProperty.call(obj, key)) {
475
+ if (Object.hasOwn(obj, key)) {
477
476
  if (length > 1 && ((typeof obj[key] !== "object" && typeof obj[key] !== "function") || obj[key] === null)) {
478
477
  // Break out early as we cannot create a property beneath
479
478
  // this type of value
@@ -570,7 +569,7 @@ function getSetting(node, name, flow_) {
570
569
  * @memberof @node-red/util_util
571
570
  */
572
571
  function evaluateEnvProperty(value, node) {
573
- var flow = (node && hasOwnProperty.call(node, "_flow")) ? node._flow : null;
572
+ var flow = (node && Object.hasOwn(node, "_flow")) ? node._flow : null;
574
573
  var result;
575
574
  if (/^\${[^}]+}$/.test(value)) {
576
575
  // ${ENV_VAR}
@@ -782,12 +781,59 @@ function evaluateJSONataExpression(expr,msg,callback) {
782
781
  }
783
782
 
784
783
  expr.evaluate(context, bindings).then(result => {
785
- callback(null, result)
784
+ callback(null, normaliseJSONataResult(result, new WeakSet()))
786
785
  }).catch(err => {
787
786
  callback(err)
788
787
  })
789
788
  }
790
789
 
790
+ /**
791
+ * Restore the standard Object prototype on any object created by JSONata.
792
+ *
793
+ * JSONata 2.2.1 hardened itself against prototype pollution by building its
794
+ * internal objects with `Object.create(null)`. Those objects surface in the
795
+ * expression result, so anything downstream calling `obj.hasOwnProperty(...)` -
796
+ * or relying on `toString` - will throw. We cannot assume code outside our
797
+ * control avoids that, so the prototype is restored before the result is
798
+ * handed back.
799
+ *
800
+ * The result must be walked, not just checked at the top level: JSONata leaves
801
+ * null-prototype objects nested inside plain objects and, commonly, as the
802
+ * members of a returned array (`items.{...}`, `$map(...)`, `$append(...)`).
803
+ *
804
+ * Only plain objects and arrays are traversed. Buffers, Dates and class
805
+ * instances are passed through untouched: JSONata never places a constructed
806
+ * object inside one, and walking them would be expensive (a Buffer has one own
807
+ * key per byte) and unsafe (property getters on msg.req/msg.res may have side
808
+ * effects).
809
+ *
810
+ * @param {any} value - the JSONata result to normalise, modified in place
811
+ * @param {WeakSet} seen - objects already visited, to survive circular references
812
+ * @return {any} the value that was passed in
813
+ */
814
+ function normaliseJSONataResult(value, seen) {
815
+ if (value === null || typeof value !== 'object') { return value }
816
+ if (seen.has(value)) { return value }
817
+ seen.add(value)
818
+ if (Array.isArray(value)) {
819
+ for (var i = 0; i < value.length; i++) {
820
+ normaliseJSONataResult(value[i], seen)
821
+ }
822
+ return value
823
+ }
824
+ var proto = Object.getPrototypeOf(value)
825
+ if (proto === null) {
826
+ Object.setPrototypeOf(value, Object.prototype)
827
+ } else if (proto !== Object.prototype) {
828
+ return value
829
+ }
830
+ var keys = Object.keys(value)
831
+ for (var k = 0; k < keys.length; k++) {
832
+ normaliseJSONataResult(value[keys[k]], seen)
833
+ }
834
+ return value
835
+ }
836
+
791
837
  /**
792
838
  * Normalise a node type name to camel case.
793
839
  *
@@ -824,7 +870,7 @@ function normaliseNodeTypeName(name) {
824
870
  function encodeObject(msg,opts) {
825
871
  try {
826
872
  var debuglength = 1000;
827
- if (opts && hasOwnProperty.call(opts, 'maxLength')) {
873
+ if (opts && Object.hasOwn(opts, 'maxLength')) {
828
874
  debuglength = opts.maxLength;
829
875
  }
830
876
  var msgType = typeof msg.msg;
@@ -837,7 +883,7 @@ function encodeObject(msg,opts) {
837
883
  type: 'error',
838
884
  data: {
839
885
  name: msg.msg.name,
840
- message: hasOwnProperty.call(msg.msg, 'message') ? msg.msg.message : msg.msg.toString(),
886
+ message: Object.hasOwn(msg.msg, 'message') ? msg.msg.message : msg.msg.toString(),
841
887
  code: msg.msg.code,
842
888
  cause: cause + "",
843
889
  stack: msg.msg.stack,
@@ -928,7 +974,7 @@ function encodeObject(msg,opts) {
928
974
  type: 'error',
929
975
  data: {
930
976
  name: value.name,
931
- message: hasOwnProperty.call(value, 'message') ? value.message : value.toString(),
977
+ message: Object.hasOwn(value, 'message') ? value.message : value.toString(),
932
978
  code,
933
979
  cause: cause + "",
934
980
  stack: value.stack,
@@ -1079,7 +1125,7 @@ function encodeObject(msg,opts) {
1079
1125
  type: 'error',
1080
1126
  data: {
1081
1127
  name: e.name,
1082
- message: 'encodeObject Error: ' + (hasOwnProperty.call(e, 'message') ? e.message : e.toString()),
1128
+ message: 'encodeObject Error: ' + (Object.hasOwn(e, 'message') ? e.message : e.toString()),
1083
1129
  stack: e.stack,
1084
1130
  }
1085
1131
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node-red/util",
3
- "version": "4.1.13",
3
+ "version": "4.1.14",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
@@ -19,7 +19,7 @@
19
19
  "fs-extra": "11.4.0",
20
20
  "i18next": "25.8.14",
21
21
  "json-stringify-safe": "5.0.1",
22
- "jsonata": "2.0.6",
22
+ "jsonata": "2.2.2",
23
23
  "lodash.clonedeep": "^4.5.0",
24
24
  "moment": "2.30.1",
25
25
  "moment-timezone": "0.5.48"