From 4d768fd23677ccfbf4d5b5c67a6395644e6890be Mon Sep 17 00:00:00 2001 From: Steve-Mcl Date: Tue, 25 May 2021 14:53:06 +0100 Subject: [PATCH 1/8] ensure context get/set key is a string --- .../@node-red/runtime/lib/nodes/context/index.js | 6 ++++++ 1 file changed, 6 insertions(+) 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..56b0c7e6f 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 @@ -256,6 +256,9 @@ function createContext(id,seed,parent) { value: function(key, storage, callback) { var context; + if (typeof key !== "string" || !key.length) { + throw new Error("context get() requires 'key' to be a valid string"); + } if (!callback && typeof storage === 'function') { callback = storage; storage = undefined; @@ -338,6 +341,9 @@ function createContext(id,seed,parent) { value: function(key, value, storage, callback) { var context; + if (typeof key !== "string" || !key.length) { + throw new Error("context set() requires 'key' to be a valid string"); + } if (!callback && typeof storage === 'function') { callback = storage; storage = undefined; From a92f0c4c6e60daaed863187b6a9a3a8f18f1bcb0 Mon Sep 17 00:00:00 2001 From: Steve-Mcl Date: Wed, 26 May 2021 13:04:09 +0100 Subject: [PATCH 2/8] fix context key validation + add tests - adds a helper function validateContextKey to keep it DRY - adds tests ensure key of null "" 1 {} [] [""] [1] [{}] all throw error --- .../runtime/lib/nodes/context/index.js | 43 +++-- .../runtime/lib/nodes/context/index_spec.js | 148 ++++++++++++++++++ 2 files changed, 181 insertions(+), 10 deletions(-) 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 56b0c7e6f..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,14 +267,11 @@ function createContext(id,seed,parent) { } } } + Object.defineProperties(obj, { get: { value: function(key, storage, callback) { var context; - - if (typeof key !== "string" || !key.length) { - throw new Error("context get() requires 'key' to be a valid string"); - } if (!callback && typeof storage === 'function') { callback = storage; storage = undefined; @@ -266,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; @@ -340,10 +360,6 @@ function createContext(id,seed,parent) { set: { value: function(key, value, storage, callback) { var context; - - if (typeof key !== "string" || !key.length) { - throw new Error("context set() requires 'key' to be a valid string"); - } if (!callback && typeof storage === 'function') { callback = storage; storage = undefined; @@ -351,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 () { From 7585f14b8928af6538a1bdbfe990c3061837bda9 Mon Sep 17 00:00:00 2001 From: Ben Hardill Date: Fri, 28 May 2021 08:34:08 +0100 Subject: [PATCH 3/8] Watch node throws errors if new files deleted before the node has finished processing them all. Fixes #2996 --- .../@node-red/nodes/core/storage/23-watch.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/storage/23-watch.js b/packages/node_modules/@node-red/nodes/core/storage/23-watch.js index e43317bb4..9b6c4bc62 100644 --- a/packages/node_modules/@node-red/nodes/core/storage/23-watch.js +++ b/packages/node_modules/@node-red/nodes/core/storage/23-watch.js @@ -23,9 +23,13 @@ module.exports = function(RED) { var getAllDirs = function (dir, filelist) { filelist = filelist || []; fs.readdirSync(dir).forEach(file => { - if (fs.statSync(path.join(dir, file)).isDirectory() ) { - filelist.push(path.join(dir, file)); - getAllDirs(path.join(dir, file), filelist); + try { + if (fs.statSync(path.join(dir, file)).isDirectory() ) { + filelist.push(path.join(dir, file)); + getAllDirs(path.join(dir, file), filelist); + } + } catch (error) { + //should we raise an error? } }); return filelist; From 4140ff03d752fcaf35ed9d924c49ae495f5adfb2 Mon Sep 17 00:00:00 2001 From: Jiye Yu Date: Fri, 4 Jun 2021 03:03:00 +0900 Subject: [PATCH 4/8] fix typo in zh-CN translation (#3003) --- .../@node-red/editor-client/locales/zh-CN/editor.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/locales/zh-CN/editor.json b/packages/node_modules/@node-red/editor-client/locales/zh-CN/editor.json index e123aef1e..792f6471a 100644 --- a/packages/node_modules/@node-red/editor-client/locales/zh-CN/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/zh-CN/editor.json @@ -648,7 +648,7 @@ }, "context": { "name": "上下文数据", - "label": "上下午", + "label": "上下文", "none": "未选择", "refresh": "刷新以加载", "empty": "空", From a7b8adb0e1bdc450e98dec0e381643b23b21dca5 Mon Sep 17 00:00:00 2001 From: Kazuhiro Ito Date: Fri, 4 Jun 2021 15:08:03 +0900 Subject: [PATCH 5/8] Fix allow Flow.getNode to return subflowInstance nodes --- packages/node_modules/@node-red/runtime/lib/flows/Flow.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/node_modules/@node-red/runtime/lib/flows/Flow.js b/packages/node_modules/@node-red/runtime/lib/flows/Flow.js index c0eb122c9..95a44e2fb 100644 --- a/packages/node_modules/@node-red/runtime/lib/flows/Flow.js +++ b/packages/node_modules/@node-red/runtime/lib/flows/Flow.js @@ -369,12 +369,12 @@ class Flow { return undefined; } // console.log((new Error().stack).toString().split("\n").slice(1,3).join("\n")) - if ((this.flow.configs && this.flow.configs[id]) || (this.flow.nodes && this.flow.nodes[id])) { + if ((this.flow.configs && this.flow.configs[id]) || (this.flow.nodes && this.flow.nodes[id] && this.flow.nodes[id].type.substring(0,8) != "subflow:")) { // This is a node owned by this flow, so return whatever we have got // During a stop/restart, activeNodes could be null for this id return this.activeNodes[id]; } else if (this.activeNodes[id]) { - // TEMP: this is a subflow internal node within this flow + // TEMP: this is a subflow internal node within this flow or subflow instance node return this.activeNodes[id]; } else if (this.subflowInstanceNodes[id]) { return this.subflowInstanceNodes[id]; From 5b1bf35a2311ed14e900da0d3d34d66c9f303238 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 7 Jun 2021 18:06:35 +0100 Subject: [PATCH 6/8] Fix over-greeding matching whilst parsing commit history Fixes #3006 --- .../runtime/lib/storage/localfilesystem/projects/git/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/git/index.js b/packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/git/index.js index e1ded4337..f9d809231 100644 --- a/packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/git/index.js +++ b/packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/git/index.js @@ -291,7 +291,7 @@ function parseLog(log) { currentCommit = {} return; } - var m = /^(.*): (.*)$/.exec(l); + var m = /^(.*?): (.*)$/.exec(l); if (m) { // git 2.1.4 (Debian Stable) doesn't support %D for refs - so filter out if (m[1] === 'refs' && m[2]) { From 7651941722b8a736aa1176ff973e5fdb38b251f4 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Tue, 8 Jun 2021 11:35:10 +0100 Subject: [PATCH 7/8] Do not assign z property to tab node when updating flow Fixes #3010 --- packages/node_modules/@node-red/runtime/lib/flows/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/runtime/lib/flows/index.js b/packages/node_modules/@node-red/runtime/lib/flows/index.js index fbf339d03..92c3e5b1e 100644 --- a/packages/node_modules/@node-red/runtime/lib/flows/index.js +++ b/packages/node_modules/@node-red/runtime/lib/flows/index.js @@ -697,7 +697,9 @@ async function updateFlow(id,newFlow, user) { nodes = [tabNode].concat(newFlow.nodes||[]).concat(newFlow.configs||[]); nodes.forEach(function(n) { - n.z = id; + if (n.type !== 'tab') { + n.z = id; + } }); } From 3104c17fb30c1977b4fd55e83e8687cf85669b98 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Tue, 8 Jun 2021 11:36:16 +0100 Subject: [PATCH 8/8] Update to latest node-red-admin --- package.json | 2 +- packages/node_modules/node-red/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 856120695..a0f038721 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "mqtt": "4.2.6", "multer": "1.4.2", "mustache": "4.2.0", - "node-red-admin": "^0.2.6", + "node-red-admin": "0.2.7", "node-red-node-rbe": "^0.5.0", "node-red-node-sentiment": "^0.1.6", "node-red-node-tail": "^0.3.0", diff --git a/packages/node_modules/node-red/package.json b/packages/node_modules/node-red/package.json index 20c183c19..8ecc40227 100644 --- a/packages/node_modules/node-red/package.json +++ b/packages/node_modules/node-red/package.json @@ -39,7 +39,7 @@ "bcryptjs": "2.4.3", "express": "4.17.1", "fs-extra": "8.1.0", - "node-red-admin": "^0.2.6", + "node-red-admin": "0.2.7", "node-red-node-rbe": "^0.5.0", "node-red-node-tail": "^0.3.0", "nopt": "5.0.0",