diff --git a/packages/node_modules/@node-red/runtime/lib/nodes/context/index.js b/packages/node_modules/@node-red/runtime/lib/nodes/context/index.js index fda3852ff..967fae295 100644 --- a/packages/node_modules/@node-red/runtime/lib/nodes/context/index.js +++ b/packages/node_modules/@node-red/runtime/lib/nodes/context/index.js @@ -215,6 +215,22 @@ function followParentContext(parent, key) { return null; } +function validateContextKey(key) { + try { + const keys = Array.isArray(key) ? key : [key]; + if(!keys.length) { return false }; //no key to get/set + for (let index = 0; index < keys.length; index++) { + const k = keys[index]; + if (typeof k !== "string" || !k.length) { + return false; //not string or zero-length + } + } + } catch (error) { + return false; + } + return true; +} + function createContext(id,seed,parent) { // Seed is only set for global context - sourced from functionGlobalContext var scope = id; @@ -251,11 +267,11 @@ function createContext(id,seed,parent) { } } } + Object.defineProperties(obj, { get: { value: function(key, storage, callback) { var context; - if (!callback && typeof storage === 'function') { callback = storage; storage = undefined; @@ -263,7 +279,14 @@ function createContext(id,seed,parent) { if (callback && typeof callback !== 'function'){ throw new Error("Callback must be a function"); } - + if (!validateContextKey(key)) { + var err = Error("Invalid context key"); + if(callback) { + return callback(err); + } else { + throw err; + } + } if (!Array.isArray(key)) { var keyParts = util.parseContextStore(key); key = keyParts.key; @@ -337,7 +360,6 @@ function createContext(id,seed,parent) { set: { value: function(key, value, storage, callback) { var context; - if (!callback && typeof storage === 'function') { callback = storage; storage = undefined; @@ -345,7 +367,14 @@ function createContext(id,seed,parent) { if (callback && typeof callback !== 'function'){ throw new Error("Callback must be a function"); } - + if (!validateContextKey(key)) { + var err = Error("Invalid context key"); + if(callback) { + return callback(err); + } else { + throw err; + } + } if (!Array.isArray(key)) { var keyParts = util.parseContextStore(key); key = keyParts.key; diff --git a/test/unit/@node-red/runtime/lib/nodes/context/index_spec.js b/test/unit/@node-red/runtime/lib/nodes/context/index_spec.js index 6fd76a421..ee0089008 100644 --- a/test/unit/@node-red/runtime/lib/nodes/context/index_spec.js +++ b/test/unit/@node-red/runtime/lib/nodes/context/index_spec.js @@ -1006,7 +1006,155 @@ describe('context', function() { done(); }).catch(done); }); + it('should throw an error in context.get if key is empty string', function (done) { + Context.init({ contextStorage: memoryStorage }); + Context.load().then(function () { + var context = Context.get("1", "flow"); + context.get(""); + done("should throw an error."); + }).catch(function () { + done(); + }); + }); + it('should throw an error in context.get if key is an object', function (done) { + Context.init({ contextStorage: memoryStorage }); + Context.load().then(function () { + var context = Context.get("1", "flow"); + context.get({}); + done("should throw an error."); + }).catch(function () { + done(); + }); + }); + it('should throw an error in context.get if key is a number', function (done) { + Context.init({ contextStorage: memoryStorage }); + Context.load().then(function () { + var context = Context.get("1", "flow"); + context.get(1); + done("should throw an error."); + }).catch(function () { + done(); + }); + }); + it('should throw an error in context.get if key array contains an empty string', function (done) { + Context.init({ contextStorage: memoryStorage }); + Context.load().then(function () { + var context = Context.get("1", "flow"); + context.get(["ok1", "", "ok2"]); + done("should throw an error."); + }).catch(function () { + done(); + }); + }); + it('should throw an error in context.get if key array contains an object', function (done) { + Context.init({ contextStorage: memoryStorage }); + Context.load().then(function () { + var context = Context.get("1", "flow"); + context.get(["ok1", {}, "ok2"]); + done("should throw an error."); + }).catch(function () { + done(); + }); + }); + it('should throw an error in context.get if key array contains a number', function (done) { + Context.init({ contextStorage: memoryStorage }); + Context.load().then(function () { + var context = Context.get("1", "flow"); + context.get(["ok1", 1, "ok2"]); + done("should throw an error."); + }).catch(function () { + done(); + }); + }); + it('should throw an error in context.set if key is empty string', function (done) { + Context.init({ contextStorage: memoryStorage }); + Context.load().then(function () { + var context = Context.get("1", "flow"); + context.set("", 1); + done("should throw an error."); + }).catch(function () { + done(); + }); + }); + it('should throw an error in context.set if key is an object', function (done) { + Context.init({ contextStorage: memoryStorage }); + Context.load().then(function () { + var context = Context.get("1", "flow"); + context.set({}, 1); + done("should throw an error."); + }).catch(function () { + done(); + }); + }); + it('should throw an error in context.set if key is a number', function (done) { + Context.init({ contextStorage: memoryStorage }); + Context.load().then(function () { + var context = Context.get("1", "flow"); + context.set(1, 1); + done("should throw an error."); + }).catch(function () { + done(); + }); + }); + it('should throw an error in context.set if key array contains an empty string', function (done) { + Context.init({ contextStorage: memoryStorage }); + Context.load().then(function () { + var context = Context.get("1", "flow"); + context.set(["ok1", "", "ok2"], 1); + done("should throw an error."); + }).catch(function () { + done(); + }); + }); + it('should throw an error in context.set if key array contains an object', function (done) { + Context.init({ contextStorage: memoryStorage }); + Context.load().then(function () { + var context = Context.get("1", "flow"); + context.set(["ok1", {}, "ok2"], 1); + done("should throw an error."); + }).catch(function () { + done(); + }); + }); + it('should throw an error in context.set if key array contains a number', function (done) { + Context.init({ contextStorage: memoryStorage }); + Context.load().then(function () { + var context = Context.get("1", "flow"); + context.set(["ok1", 1, "ok2"], 1); + done("should throw an error."); + }).catch(function () { + done(); + }); + }); + + it('should have an err set in callback for invalid key in context.get', function (done) { + Context.init({ contextStorage: memoryStorage }); + Context.load().then(function () { + var context = Context.get("1", "flow"); + context.get("", function(err) { + if(err) { + done(); + } else { + done("should throw an error."); + } + }); + }).catch(done); + }); + + it('should have an err set in callback for invalid key in context.set', function (done) { + Context.init({ contextStorage: memoryStorage }); + Context.load().then(function () { + var context = Context.get("1", "flow"); + context.set("", "value", function(err) { + if(err) { + done(); + } else { + done("should throw an error."); + } + }); + }).catch(done); + }); }); describe('listStores', function () {