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"}]}
])