mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Merge branch 'master' into dev
This commit is contained in:
		
							
								
								
									
										20
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,3 +1,23 @@ | |||||||
|  | ### 1.2.8: Maintenance Release | ||||||
|  |  | ||||||
|  | Editor | ||||||
|  |  | ||||||
|  |  - Ensure subflow help is picked up for palette tooltip Fixes #2834 | ||||||
|  |  - Improve Ru locale (#2826) @alexk111 | ||||||
|  |  - Fix scrollbars (#2825) @alexk111 | ||||||
|  |  | ||||||
|  | Runtime | ||||||
|  |  | ||||||
|  |  - Restrict project file access to inside the project directory | ||||||
|  |  - Validate user-provided language parameter before passing to i18n | ||||||
|  |  - Fix grunt release mkdir issue on Node.js 14 (#2827) @alexk111 | ||||||
|  |  - Prevent crash when coreNodesDir is empty (#2831) @hardillb | ||||||
|  |  | ||||||
|  | Nodes | ||||||
|  |  | ||||||
|  |  - Batch node: Fixing minor typo in node's documentation (#2848) @matthiasradde | ||||||
|  |  - Split node: Handle out of order messages as long as one of the messages has msg.parts.count set to the proper value (#2748) @s4ke | ||||||
|  |  | ||||||
| ### 1.2.7: Maintenance Release | ### 1.2.7: Maintenance Release | ||||||
|  |  | ||||||
| Editor | Editor | ||||||
|   | |||||||
| @@ -106,7 +106,7 @@ | |||||||
|         "marked": "1.2.7", |         "marked": "1.2.7", | ||||||
|         "minami": "1.2.3", |         "minami": "1.2.3", | ||||||
|         "mocha": "^5.2.0", |         "mocha": "^5.2.0", | ||||||
|         "node-red-node-test-helper": "^0.2.5", |         "node-red-node-test-helper": "^0.2.6", | ||||||
|         "node-sass": "^4.14.1", |         "node-sass": "^4.14.1", | ||||||
|         "nodemon": "2.0.6", |         "nodemon": "2.0.6", | ||||||
|         "should": "13.2.3", |         "should": "13.2.3", | ||||||
|   | |||||||
| @@ -20,19 +20,18 @@ | |||||||
|     <p>Es gibt drei Modi für die Erstellung von Nachrichtensequenzen:</p> |     <p>Es gibt drei Modi für die Erstellung von Nachrichtensequenzen:</p> | ||||||
|     <dl> |     <dl> | ||||||
|         <dt>Anzahl der Nachrichten</dt> |         <dt>Anzahl der Nachrichten</dt> | ||||||
|         <dd>Die Nachrichten werden zu Sequenzen einer bestimmten Länge gruppiert. Die Option <b>Überlappung</b>> |         <dd>Die Nachrichten werden zu Sequenzen einer bestimmten Länge gruppiert. Die Option <b>Überlappung</b> | ||||||
|             gibt an, wie viele Nachrichten vom Ende einer Sequenz am Anfang der nächsten Sequenz wiederholt werden sollen.</dd> |             gibt an, wie viele Nachrichten vom Ende einer Sequenz am Anfang der nächsten Sequenz wiederholt werden sollen.</dd> | ||||||
|  |  | ||||||
|         <dt>Zeitintervall</dt> |         <dt>Zeitintervall</dt> | ||||||
|         <dd>Gruppiert Nachrichten, die innerhalb des angegebenen Intervalls eingehen. Wenn keine Nachrichten  |         <dd>Gruppiert Nachrichten, die innerhalb des angegebenen Intervalls eingehen. Wenn keine Nachrichten  | ||||||
|             innerhalb des Intervalls ankommen, kann der Node optional eine leere Nachricht senden.</dd> |             innerhalb des Intervalls ankommen, kann der Node optional eine leere Nachricht senden.</dd> | ||||||
|  |  | ||||||
|         <dt>Sequenzesn verketten/dt> |         <dt>Sequenzen verketten/dt> | ||||||
|         <dd>Erzeugt eine Nachrichtensequenz durch die Verkettung eingehender Sequenzen. Jede Nachricht muss eine <code>msg.topic</code>  |         <dd>Erzeugt eine Nachrichtensequenz durch die Verkettung eingehender Sequenzen. Jede Nachricht muss eine <code>msg.topic</code>  | ||||||
|             und eine <code>msg.parts</code> Eigenschaft haben, um die Sequenz identifizieren zu können.  |             und eine <code>msg.parts</code> Eigenschaft haben, um die Sequenz identifizieren zu können.  | ||||||
|             Der Node ist mit einer Liste von <code>topic</code> konfiguriert, |             Der Node ist mit einer Liste von <code>topic</code> konfiguriert, | ||||||
|              mit der die Reihnmefolge der Verkettung der Sequenzen definiert wird. |              mit der die Reihenfolge der Verkettung der Sequenzen definiert wird.</dd> | ||||||
|             </dd> |  | ||||||
|      |      | ||||||
|     </dl> |     </dl> | ||||||
|     <h4>Speichern der Nachrichten</h4> |     <h4>Speichern der Nachrichten</h4> | ||||||
|   | |||||||
| @@ -305,6 +305,9 @@ Project.prototype.update = async function (user, data) { | |||||||
|                 return new Error("Invalid package file: "+data.files.package) |                 return new Error("Invalid package file: "+data.files.package) | ||||||
|             } |             } | ||||||
|             var root = data.files.package.substring(0,data.files.package.length-12); |             var root = data.files.package.substring(0,data.files.package.length-12); | ||||||
|  |             if (/^\.\./.test(fspath.relative(this.path,fspath.join(this.path,data.files.package)))) { | ||||||
|  |                 return Promise.reject("Invalid package file: "+data.files.package) | ||||||
|  |             } | ||||||
|             this.paths.root = root; |             this.paths.root = root; | ||||||
|             this.paths['package.json'] = data.files.package; |             this.paths['package.json'] = data.files.package; | ||||||
|             globalProjectSettings.projects[this.name].rootPath = root; |             globalProjectSettings.projects[this.name].rootPath = root; | ||||||
| @@ -322,12 +325,18 @@ Project.prototype.update = async function (user, data) { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (data.files.hasOwnProperty('flow') && this.package['node-red'].settings.flowFile !== data.files.flow.substring(this.paths.root.length)) { |         if (data.files.hasOwnProperty('flow') && this.package['node-red'].settings.flowFile !== data.files.flow.substring(this.paths.root.length)) { | ||||||
|  |             if (/^\.\./.test(fspath.relative(this.path,fspath.join(this.path,data.files.flow)))) { | ||||||
|  |                 return Promise.reject("Invalid flow file: "+data.files.flow) | ||||||
|  |             } | ||||||
|             this.paths.flowFile = data.files.flow; |             this.paths.flowFile = data.files.flow; | ||||||
|             this.package['node-red'].settings.flowFile = data.files.flow.substring(this.paths.root.length); |             this.package['node-red'].settings.flowFile = data.files.flow.substring(this.paths.root.length); | ||||||
|             savePackage = true; |             savePackage = true; | ||||||
|             flowFilesChanged = true; |             flowFilesChanged = true; | ||||||
|         } |         } | ||||||
|         if (data.files.hasOwnProperty('credentials') && this.package['node-red'].settings.credentialsFile !== data.files.credentials.substring(this.paths.root.length)) { |         if (data.files.hasOwnProperty('credentials') && this.package['node-red'].settings.credentialsFile !== data.files.credentials.substring(this.paths.root.length)) { | ||||||
|  |             if (/^\.\./.test(fspath.relative(this.path,fspath.join(this.path,data.files.credentials)))) { | ||||||
|  |                 return Promise.reject("Invalid credentials file: "+data.files.credentials) | ||||||
|  |             } | ||||||
|             this.paths.credentialsFile = data.files.credentials; |             this.paths.credentialsFile = data.files.credentials; | ||||||
|             this.package['node-red'].settings.credentialsFile = data.files.credentials.substring(this.paths.root.length); |             this.package['node-red'].settings.credentialsFile = data.files.credentials.substring(this.paths.root.length); | ||||||
|             // Don't know if the credSecret is invalid or not so clear the flag |             // Don't know if the credSecret is invalid or not so clear the flag | ||||||
| @@ -490,6 +499,10 @@ Project.prototype.getFile = function (filePath,treeish) { | |||||||
|     if (treeish !== "_") { |     if (treeish !== "_") { | ||||||
|         return gitTools.getFile(this.path, filePath, treeish); |         return gitTools.getFile(this.path, filePath, treeish); | ||||||
|     } else { |     } else { | ||||||
|  |         let fullPath = fspath.join(this.path,filePath); | ||||||
|  |         if (/^\.\./.test(fspath.relative(this.path,fullPath))) { | ||||||
|  |             throw new Error("Invalid file name") | ||||||
|  |         } | ||||||
|         return fs.readFile(fspath.join(this.path,filePath),"utf8"); |         return fs.readFile(fspath.join(this.path,filePath),"utf8"); | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| @@ -639,6 +652,11 @@ Project.prototype.pull = function (user,remoteBranchName,setRemote,allowUnrelate | |||||||
|  |  | ||||||
| Project.prototype.resolveMerge = function (file,resolutions) { | Project.prototype.resolveMerge = function (file,resolutions) { | ||||||
|     var filePath = fspath.join(this.path,file); |     var filePath = fspath.join(this.path,file); | ||||||
|  |  | ||||||
|  |     if (/^\.\./.test(fspath.relative(this.path,filePath))) { | ||||||
|  |         throw new Error("Invalid file name") | ||||||
|  |     } | ||||||
|  |  | ||||||
|     var self = this; |     var self = this; | ||||||
|     if (typeof resolutions === 'string') { |     if (typeof resolutions === 'string') { | ||||||
|         return util.writeFile(filePath, resolutions).then(function() { |         return util.writeFile(filePath, resolutions).then(function() { | ||||||
| @@ -1047,12 +1065,10 @@ function loadProject(projectPath) { | |||||||
| function init(_settings, _runtime) { | function init(_settings, _runtime) { | ||||||
|     settings = _settings; |     settings = _settings; | ||||||
|     runtime = _runtime; |     runtime = _runtime; | ||||||
|     projectsDir = fspath.join(settings.userDir,"projects"); |     projectsDir = fspath.resolve(fspath.join(settings.userDir,"projects")); | ||||||
|  |  | ||||||
|     if(settings.editorTheme.projects.path) { |     if(settings.editorTheme.projects.path) { | ||||||
|         projectsDir = settings.editorTheme.projects.path; |         projectsDir = settings.editorTheme.projects.path; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     authCache.init(); |     authCache.init(); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -113,7 +113,8 @@ function init(_settings, _runtime) { | |||||||
|                     globalGitUser = gitConfig.user; |                     globalGitUser = gitConfig.user; | ||||||
|                     Projects.init(settings,runtime); |                     Projects.init(settings,runtime); | ||||||
|                     sshTools.init(settings); |                     sshTools.init(settings); | ||||||
|                     projectsDir = fspath.join(settings.userDir,"projects"); |  | ||||||
|  |                     projectsDir = fspath.resolve(fspath.join(settings.userDir,"projects")); | ||||||
|  |  | ||||||
|                     if(settings.editorTheme.projects.path) { |                     if(settings.editorTheme.projects.path) { | ||||||
|                       projectsDir = settings.editorTheme.projects.path; |                       projectsDir = settings.editorTheme.projects.path; | ||||||
| @@ -215,9 +216,16 @@ function getBackupFilename(filename) { | |||||||
| } | } | ||||||
|  |  | ||||||
| function loadProject(name) { | function loadProject(name) { | ||||||
|  |     let fullPath = fspath.resolve(fspath.join(projectsDir,name)); | ||||||
|     var projectPath = name; |     var projectPath = name; | ||||||
|     if (projectPath.indexOf(fspath.sep) === -1) { |     if (projectPath.indexOf(fspath.sep) === -1) { | ||||||
|         projectPath = fspath.join(projectsDir,name); |         projectPath = fullPath; | ||||||
|  |     } else { | ||||||
|  |         // Ensure this project dir is under projectsDir; | ||||||
|  |         let relativePath = fspath.relative(projectsDir,fullPath); | ||||||
|  |         if (/^\.\./.test(relativePath)) { | ||||||
|  |             throw new Error("Invalid project name") | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|     return Projects.load(projectPath).then(function(project) { |     return Projects.load(projectPath).then(function(project) { | ||||||
|         activeProject = project; |         activeProject = project; | ||||||
| @@ -241,6 +249,10 @@ function deleteProject(user, name) { | |||||||
|         throw e; |         throw e; | ||||||
|     } |     } | ||||||
|     var projectPath = fspath.join(projectsDir,name); |     var projectPath = fspath.join(projectsDir,name); | ||||||
|  |     let relativePath = fspath.relative(projectsDir,projectPath); | ||||||
|  |     if (/^\.\./.test(relativePath)) { | ||||||
|  |         throw new Error("Invalid project name") | ||||||
|  |     } | ||||||
|     return Projects.delete(user, projectPath); |     return Projects.delete(user, projectPath); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -399,6 +411,10 @@ function createProject(user, metadata) { | |||||||
|         metadata.files.credentialSecret = currentEncryptionKey; |         metadata.files.credentialSecret = currentEncryptionKey; | ||||||
|     } |     } | ||||||
|     metadata.path = fspath.join(projectsDir,metadata.name); |     metadata.path = fspath.join(projectsDir,metadata.name); | ||||||
|  |     if (/^\.\./.test(fspath.relative(projectsDir,metadata.path))) { | ||||||
|  |         throw new Error("Invalid project name") | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return Projects.create(user, metadata).then(function(p) { |     return Projects.create(user, metadata).then(function(p) { | ||||||
|         return setActiveProject(user, p.name); |         return setActiveProject(user, p.name); | ||||||
|     }).then(function() { |     }).then(function() { | ||||||
|   | |||||||
| @@ -53,6 +53,49 @@ describe('function node', function() { | |||||||
|         }); |         }); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|  |     it('should send returned message using send()', function(done) { | ||||||
|  |         var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"node.send(msg);"}, | ||||||
|  |         {id:"n2", type:"helper"}]; | ||||||
|  |         helper.load(functionNode, flow, function() { | ||||||
|  |             var n1 = helper.getNode("n1"); | ||||||
|  |             var n2 = helper.getNode("n2"); | ||||||
|  |             n2.on("input", function(msg) { | ||||||
|  |                 msg.should.have.property('topic', 'bar'); | ||||||
|  |                 msg.should.have.property('payload', 'foo'); | ||||||
|  |                 done(); | ||||||
|  |             }); | ||||||
|  |             n1.receive({payload:"foo",topic: "bar"}); | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     it('should do something with the catch node', function(done) { | ||||||
|  |         var flow = [{"id":"funcNode","type":"function","wires":[["goodNode"]],"func":"node.error('This is an error', msg);"},{"id":"goodNode","type":"helper"},{"id":"badNode","type":"helper"},{"id":"catchNode","type":"catch","scope":null,"uncaught":false,"wires":[["badNode"]]}]; | ||||||
|  |         var catchNodeModule = require("nr-test-utils").require("@node-red/nodes/core/common/25-catch.js") | ||||||
|  |         helper.load([catchNodeModule, functionNode], flow, function() { | ||||||
|  |             var funcNode = helper.getNode("funcNode"); | ||||||
|  |             var catchNode = helper.getNode("catchNode"); | ||||||
|  |             var goodNode = helper.getNode("goodNode"); | ||||||
|  |             var badNode = helper.getNode("badNode"); | ||||||
|  |  | ||||||
|  |             badNode.on("input", function(msg) { | ||||||
|  |                 msg.should.have.property('topic', 'bar'); | ||||||
|  |                 msg.should.have.property('payload', 'foo'); | ||||||
|  |                 msg.should.have.property('error'); | ||||||
|  |                 msg.error.should.have.property('message',"This is an error"); | ||||||
|  |                 msg.error.should.have.property('source'); | ||||||
|  |                 msg.error.source.should.have.property('id', "funcNode"); | ||||||
|  |                 done(); | ||||||
|  |             }); | ||||||
|  |             funcNode.receive({payload:"foo",topic: "bar"}); | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  | ||||||
|     it('should be loaded', function(done) { |     it('should be loaded', function(done) { | ||||||
|         var flow = [{id:"n1", type:"function", name: "function" }]; |         var flow = [{id:"n1", type:"function", name: "function" }]; | ||||||
|         helper.load(functionNode, flow, function() { |         helper.load(functionNode, flow, function() { | ||||||
| @@ -1560,4 +1603,5 @@ describe('function node', function() { | |||||||
|         }); |         }); | ||||||
|  |  | ||||||
|     }) |     }) | ||||||
|  |     */ | ||||||
| }); | }); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user