diff --git a/package.json b/package.json index 8fb7ab560..e1d8cdd29 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,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", "nopt": "5.0.0", "oauth2orize": "1.11.0", "on-headers": "1.0.2", 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": "空", 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; 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 35fdc67fc..9294121e7 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]; 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; + } }); } 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/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]) { diff --git a/packages/node_modules/node-red/package.json b/packages/node_modules/node-red/package.json index eb1fdc3ec..c6f9f92e6 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": "10.0.0", - "node-red-admin": "^0.2.6", + "node-red-admin": "0.2.7", "nopt": "5.0.0", "semver": "7.3.5" }, 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 932d2c72b..7e05eba8f 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 () {