diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js index 332f41fa9..e53372a52 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js @@ -344,6 +344,16 @@ that.element.val(that.value()); that.element.trigger('change',that.propertyType,that.value()); }); + this.input.on('keyup', function(evt) { + that.validate(); + that.element.val(that.value()); + that.element.trigger('keyup',evt); + }); + this.input.on('paste', function(evt) { + that.validate(); + that.element.val(that.value()); + that.element.trigger('paste',evt); + }); this.input.on('keydown', function(evt) { if (evt.keyCode >= 37 && evt.keyCode <= 40) { evt.stopPropagation(); diff --git a/packages/node_modules/@node-red/nodes/core/function/10-function.html b/packages/node_modules/@node-red/nodes/core/function/10-function.html index 5b0e8da44..27eb588b9 100644 --- a/packages/node_modules/@node-red/nodes/core/function/10-function.html +++ b/packages/node_modules/@node-red/nodes/core/function/10-function.html @@ -23,6 +23,11 @@ border-left: none; border-top: none; border-right: none; + padding-top: 2px; + padding-bottom: 2px; + margin-top: 4px; + margin-bottom: 2px; + height: 26px; } .node-libs-entry > span > i { @@ -89,6 +94,25 @@ (function() { + var invalidModuleVNames = [ + 'console', + 'util', + 'Buffer', + 'Date', + 'RED', + 'node', + '__node__', + 'context', + 'flow', + 'global', + 'env', + 'setTimeout', + 'clearTimeout', + 'setInterval', + 'clearInterval', + 'promisify' + ] + var knownFunctionNodes = {}; RED.events.on("nodes:add", function(n) { if (n.type === "function") { @@ -193,6 +217,16 @@ width: "120px", "margin-left": "5px" }).appendTo(row0).val(opt.var); + var vnameWarning = $('').appendTo(row0); + RED.popover.tooltip(vnameWarning.find("i"),function() { + var val = fvar.val(); + if (invalidModuleVNames.indexOf(val) !== -1) { + return RED._("node-red:function.error.moduleNameReserved",{name:val}) + } else { + return RED._("node-red:function.error.moduleNameError",{name:val}) + } + }) + $(' = require(').appendTo(row0); var fmodule = $("", { class: "node-input-libs-val", @@ -210,29 +244,33 @@ $(')').appendTo(row0); - var warning = $('').appendTo(row0); - RED.popover.tooltip(warning.find("i"),function() { + var moduleWarning = $('').appendTo(row0); + RED.popover.tooltip(moduleWarning.find("i"),function() { var val = fmodule.typedInput("type"); if (val === "_custom_") { val = fmodule.val(); } + var errors = []; + if (!RED.utils.checkModuleAllowed(val,null,installAllowList,installDenyList)) { - return "Module not allowed" + return RED._("node-red:function.error.moduleNotAllowed",{module:val}); } else { - return "Module not installed: "+missingModuleReasons[val] + return RED._("node-red:function.error.moduleLoadError",{module:val,error:missingModuleReasons[val]}); } }) - fvar.on("change", function (e) { + fvar.on("change keyup paste", function (e) { var v = $(this).val().trim(); - if (v === "" || / /.test(v)) { + if (v === "" || / /.test(v) || invalidModuleVNames.indexOf(v) !== -1) { fvar.addClass("input-error"); + vnameWarning.addClass("input-error"); } else { fvar.removeClass("input-error"); + vnameWarning.removeClass("input-error"); } }); - fmodule.on("change", function (e) { + fmodule.on("change keyup paste", function (e) { var val = $(this).typedInput("type"); if (val === "_custom_") { val = $(this).val(); @@ -243,18 +281,18 @@ if (RED.utils.checkModuleAllowed(val,null,installAllowList,installDenyList) && (missingModules.indexOf(val) === -1)) { fmodule.removeClass("input-error"); - warning.removeClass("input-error"); + moduleWarning.removeClass("input-error"); } else { fmodule.addClass("input-error"); - warning.addClass("input-error"); + moduleWarning.addClass("input-error"); } }); if (RED.utils.checkModuleAllowed(opt.module,null,installAllowList,installDenyList) && (missingModules.indexOf(opt.module) === -1)) { fmodule.removeClass("input-error"); - warning.removeClass("input-error"); + moduleWarning.removeClass("input-error"); } else { fmodule.addClass("input-error"); - warning.addClass("input-error"); + moduleWarning.addClass("input-error"); } if (opt.var) { fvar.trigger("change"); @@ -293,6 +331,9 @@ if (missingModules.indexOf(m.module) > -1) { return false; } + if (invalidModuleVNames.indexOf(m.var) !== -1){ + return false; + } } return true; }} @@ -325,15 +366,15 @@ tabs.addTab({ id: "func-tab-init", - label: "On Start", //that._("function.label.initialize") + label: that._("function.label.initialize") }); tabs.addTab({ id: "func-tab-body", - label: "On Message"//that._("function.label.function") + label: that._("function.label.function") }); tabs.addTab({ id: "func-tab-finalize", - label: "On Stop"//that._("function.label.finalize") + label: that._("function.label.finalize") }); tabs.activateTab("func-tab-body"); diff --git a/packages/node_modules/@node-red/nodes/core/function/10-function.js b/packages/node_modules/@node-red/nodes/core/function/10-function.js index b9fae8626..d6abde563 100644 --- a/packages/node_modules/@node-red/nodes/core/function/10-function.js +++ b/packages/node_modules/@node-red/nodes/core/function/10-function.js @@ -273,6 +273,37 @@ module.exports = function(RED) { sandbox.promisify = util.promisify; } + if (node.hasOwnProperty("libs")) { + let moduleErrors = false; + var modules = node.libs; + modules.forEach(module => { + var vname = module.hasOwnProperty("var") ? module.var : null; + if (vname && (vname !== "")) { + if (sandbox.hasOwnProperty(vname) || vname === 'node') { + node.error(RED._("function.error.moduleNameError",{name:vname})) + moduleErrors = true; + return; + } + sandbox[vname] = null; + try { + var spec = module.module; + if (spec && (spec !== "")) { + var lib = RED.require(module.module); + sandbox[vname] = lib; + } + } catch (e) { + //TODO: NLS error message + node.error(RED._("function.error.moduleLoadError",{module:module.spec, error:e.toString()})) + moduleErrors = true; + } + } + }); + if (moduleErrors) { + throw new Error("Function node failed to load external modules"); + } + } + + const RESOLVING = 0; const RESOLVED = 1; const ERROR = 2; @@ -289,26 +320,6 @@ module.exports = function(RED) { } }); - if (node.hasOwnProperty("libs")) { - var modules = node.libs; - modules.forEach(module => { - var vname = module.hasOwnProperty("var") ? module.var : null; - if (vname && (vname !== "")) { - sandbox[vname] = null; - try { - var spec = module.module; - if (spec && (spec !== "")) { - var lib = RED.require(module.module); - sandbox[vname] = lib; - } - } - catch (e) { - console.log(e); - node.warn("failed to load library: "+ module.spec); - } - } - }); - } var context = vm.createContext(sandbox); try { var iniScript = null; diff --git a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json index 20180f33d..5dc17f76d 100755 --- a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json +++ b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json @@ -209,9 +209,9 @@ "function": { "function": "", "label": { - "function": "Function", - "initialize": "Setup", - "finalize": "Close", + "function": "On Message", + "initialize": "On Start", + "finalize": "On Stop", "outputs": "Outputs", "require": "Require" }, @@ -224,6 +224,10 @@ "module": "module" }, "error": { + "moduleNotAllowed": "Module __module__ not allowed", + "moduleLoadError": "Failed to load module __module__: __error__", + "moduleNameError": "Invalid module variable name: __name__", + "moduleNameReserved": "Reserved variable name: __name__", "inputListener":"Cannot add listener to 'input' event within Function", "non-message-returned":"Function tried to send a message of type __type__" } diff --git a/test/nodes/core/function/10-function_spec.js b/test/nodes/core/function/10-function_spec.js index 95bca5fb4..7139c0e75 100644 --- a/test/nodes/core/function/10-function_spec.js +++ b/test/nodes/core/function/10-function_spec.js @@ -1438,7 +1438,6 @@ describe('function node', function() { } },20); }).catch(err => done(err)); - }) it('should require the OS module', function(done) { var flow = [ @@ -1459,9 +1458,18 @@ describe('function node', function() { }); n1.receive({payload:"foo",topic: "bar"}); }).catch(err => done(err)); - }) - + it('should fail if module variable name clashes with sandbox builtin', function(done) { + var flow = [ + {id:"n1",type:"function",wires:[["n2"]],func:"msg.payload = os.type(); return msg;", "libs": [{var:"flow", module:"os"}]}, + {id:"n2", type:"helper"} + ]; + helper.load(functionNode, flow, function() { + var n1 = helper.getNode("n1"); + should.not.exist(n1); + done(); + }).catch(err => done(err)); + }) }) diff --git a/test/unit/@node-red/registry/lib/externalModules_spec.js b/test/unit/@node-red/registry/lib/externalModules_spec.js index 17f3cc41d..1cd274561 100644 --- a/test/unit/@node-red/registry/lib/externalModules_spec.js +++ b/test/unit/@node-red/registry/lib/externalModules_spec.js @@ -94,7 +94,7 @@ describe("externalModules api", function() { it("installs missing modules", async function() { externalModules.init({userDir: homeDir}); externalModules.register("function", "libs"); - fs.existsSync(path.join(homeDir,"externalModuels")).should.be.false(); + fs.existsSync(path.join(homeDir,"externalModules")).should.be.false(); await externalModules.checkFlowDependencies([ {type: "function", libs:[{module: "foo"}]} ])