2014-07-17 09:34:26 +02:00
|
|
|
/**
|
|
|
|
* Copyright 2014 IBM Corp.
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
**/
|
2014-07-23 15:51:11 +02:00
|
|
|
|
2014-07-17 09:34:26 +02:00
|
|
|
var should = require("should");
|
2014-07-23 15:51:11 +02:00
|
|
|
var request = require('supertest');
|
|
|
|
var express = require('express');
|
|
|
|
|
|
|
|
var when = require('when');
|
|
|
|
|
|
|
|
var app = express();
|
2014-11-04 18:05:29 +01:00
|
|
|
var RED = require("../../../red/red.js");
|
|
|
|
var storage = require("../../../red/storage");
|
|
|
|
var library = require("../../../red/api/library");
|
2015-02-06 00:43:35 +01:00
|
|
|
var auth = require("../../../red/api/auth");
|
2014-07-23 15:51:11 +02:00
|
|
|
|
2014-11-04 18:05:29 +01:00
|
|
|
describe("library api", function() {
|
|
|
|
|
|
|
|
function initStorage(_flows,_libraryEntries) {
|
|
|
|
var flows = _flows;
|
|
|
|
var libraryEntries = _libraryEntries;
|
|
|
|
storage.init({
|
|
|
|
storageModule: {
|
|
|
|
init: function() {
|
|
|
|
return when.resolve();
|
|
|
|
},
|
|
|
|
getAllFlows: function() {
|
|
|
|
return when.resolve(flows);
|
|
|
|
},
|
|
|
|
getFlow: function(fn) {
|
|
|
|
if (flows[fn]) {
|
|
|
|
return when.resolve(flows[fn]);
|
|
|
|
} else {
|
|
|
|
return when.reject();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
saveFlow: function(fn,data) {
|
|
|
|
flows[fn] = data;
|
|
|
|
return when.resolve();
|
|
|
|
},
|
|
|
|
getLibraryEntry: function(type,path) {
|
|
|
|
if (libraryEntries[type] && libraryEntries[type][path]) {
|
|
|
|
return when.resolve(libraryEntries[type][path]);
|
|
|
|
} else {
|
|
|
|
return when.reject();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
saveLibraryEntry: function(type,path,meta,body) {
|
|
|
|
libraryEntries[type][path] = body;
|
|
|
|
return when.resolve();
|
|
|
|
}
|
|
|
|
}
|
2014-07-23 15:51:11 +02:00
|
|
|
});
|
2014-11-04 18:05:29 +01:00
|
|
|
}
|
2014-07-23 15:51:11 +02:00
|
|
|
|
|
|
|
describe("flows", function() {
|
2014-11-04 18:05:29 +01:00
|
|
|
var app;
|
|
|
|
|
|
|
|
before(function() {
|
|
|
|
app = express();
|
|
|
|
app.use(express.json());
|
|
|
|
app.get("/library/flows",library.getAll);
|
|
|
|
app.post(new RegExp("/library/flows\/(.*)"),library.post);
|
|
|
|
app.get(new RegExp("/library/flows\/(.*)"),library.get);
|
|
|
|
});
|
2014-07-23 15:51:11 +02:00
|
|
|
it('returns empty result', function(done) {
|
2015-04-04 20:33:37 +02:00
|
|
|
initStorage({},{flows:{}});
|
2014-11-04 18:05:29 +01:00
|
|
|
request(app)
|
2014-07-23 15:51:11 +02:00
|
|
|
.get('/library/flows')
|
|
|
|
.expect(200)
|
|
|
|
.end(function(err,res) {
|
|
|
|
if (err) {
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
res.body.should.not.have.property('f');
|
2014-11-04 18:05:29 +01:00
|
|
|
res.body.should.not.have.property('d');
|
2014-07-23 15:51:11 +02:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('returns 404 for non-existent entry', function(done) {
|
2015-04-04 20:33:37 +02:00
|
|
|
initStorage({},{flows:{}});
|
2014-11-04 18:05:29 +01:00
|
|
|
request(app)
|
2014-07-23 15:51:11 +02:00
|
|
|
.get('/library/flows/foo')
|
|
|
|
.expect(404)
|
|
|
|
.end(done);
|
|
|
|
});
|
2014-11-04 18:05:29 +01:00
|
|
|
|
|
|
|
|
2014-07-23 15:51:11 +02:00
|
|
|
it('can store and retrieve item', function(done) {
|
2015-04-04 20:33:37 +02:00
|
|
|
initStorage({},{flows:{}});
|
2014-07-23 15:51:11 +02:00
|
|
|
var flow = '[]';
|
2014-11-04 18:05:29 +01:00
|
|
|
request(app)
|
2014-07-23 15:51:11 +02:00
|
|
|
.post('/library/flows/foo')
|
2014-11-05 23:41:05 +01:00
|
|
|
.set('Content-Type', 'application/json')
|
2014-07-23 15:51:11 +02:00
|
|
|
.send(flow)
|
|
|
|
.expect(204).end(function (err, res) {
|
|
|
|
if (err) {
|
|
|
|
throw err;
|
|
|
|
}
|
2014-11-04 18:05:29 +01:00
|
|
|
request(app)
|
2014-07-23 15:51:11 +02:00
|
|
|
.get('/library/flows/foo')
|
|
|
|
.expect(200)
|
|
|
|
.end(function(err,res) {
|
|
|
|
if (err) {
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
res.text.should.equal(flow);
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2014-11-04 18:05:29 +01:00
|
|
|
|
2014-07-23 15:51:11 +02:00
|
|
|
it('lists a stored item', function(done) {
|
2014-11-04 18:05:29 +01:00
|
|
|
initStorage({f:["bar"]});
|
|
|
|
request(app)
|
|
|
|
.get('/library/flows')
|
|
|
|
.expect(200)
|
|
|
|
.end(function(err,res) {
|
|
|
|
if (err) {
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
res.body.should.have.property('f');
|
|
|
|
should.deepEqual(res.body.f,['bar']);
|
|
|
|
done();
|
2014-07-23 15:51:11 +02:00
|
|
|
});
|
|
|
|
});
|
2014-11-04 18:05:29 +01:00
|
|
|
|
|
|
|
it('returns 403 for malicious get attempt', function(done) {
|
|
|
|
initStorage({});
|
2014-07-29 14:51:21 +02:00
|
|
|
// without the userDir override the malicious url would be
|
|
|
|
// http://127.0.0.1:1880/library/flows/../../package to
|
|
|
|
// obtain package.json from the node-red root.
|
2014-11-04 18:05:29 +01:00
|
|
|
request(app)
|
2014-07-29 14:51:21 +02:00
|
|
|
.get('/library/flows/../../../../../package')
|
|
|
|
.expect(403)
|
|
|
|
.end(done);
|
|
|
|
});
|
2014-11-04 18:05:29 +01:00
|
|
|
it('returns 403 for malicious post attempt', function(done) {
|
|
|
|
initStorage({});
|
2014-07-29 14:51:21 +02:00
|
|
|
// without the userDir override the malicious url would be
|
|
|
|
// http://127.0.0.1:1880/library/flows/../../package to
|
|
|
|
// obtain package.json from the node-red root.
|
2014-11-04 18:05:29 +01:00
|
|
|
request(app)
|
2014-07-29 14:51:21 +02:00
|
|
|
.post('/library/flows/../../../../../package')
|
|
|
|
.expect(403)
|
|
|
|
.end(done);
|
|
|
|
});
|
2014-07-23 15:51:11 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
describe("type", function() {
|
2014-11-04 18:05:29 +01:00
|
|
|
var app;
|
|
|
|
|
2014-07-23 15:51:11 +02:00
|
|
|
before(function() {
|
2014-11-04 18:05:29 +01:00
|
|
|
app = express();
|
|
|
|
app.use(express.json());
|
|
|
|
library.init(app);
|
2015-02-06 00:43:35 +01:00
|
|
|
auth.init({});
|
2014-11-04 18:05:29 +01:00
|
|
|
RED.library.register("test");
|
2014-07-23 15:51:11 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
it('returns empty result', function(done) {
|
2014-11-04 18:05:29 +01:00
|
|
|
initStorage({},{'test':{"":[]}});
|
|
|
|
request(app)
|
2014-07-23 15:51:11 +02:00
|
|
|
.get('/library/test')
|
|
|
|
.expect(200)
|
|
|
|
.end(function(err,res) {
|
|
|
|
if (err) {
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
res.body.should.not.have.property('f');
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
2014-11-04 18:05:29 +01:00
|
|
|
|
2014-07-23 15:51:11 +02:00
|
|
|
it('returns 404 for non-existent entry', function(done) {
|
2014-11-04 18:05:29 +01:00
|
|
|
initStorage({},{});
|
|
|
|
request(app)
|
2014-07-23 15:51:11 +02:00
|
|
|
.get('/library/test/foo')
|
|
|
|
.expect(404)
|
|
|
|
.end(done);
|
|
|
|
});
|
2014-11-04 18:05:29 +01:00
|
|
|
|
2014-07-23 15:51:11 +02:00
|
|
|
it('can store and retrieve item', function(done) {
|
2014-11-04 18:05:29 +01:00
|
|
|
initStorage({},{'test':{}});
|
2015-02-26 18:08:20 +01:00
|
|
|
var flow = {text:"test content"};
|
2014-11-04 18:05:29 +01:00
|
|
|
request(app)
|
2014-07-23 15:51:11 +02:00
|
|
|
.post('/library/test/foo')
|
2015-02-26 18:08:20 +01:00
|
|
|
.set('Content-Type', 'application/json')
|
2014-07-23 15:51:11 +02:00
|
|
|
.send(flow)
|
|
|
|
.expect(204).end(function (err, res) {
|
|
|
|
if (err) {
|
|
|
|
throw err;
|
|
|
|
}
|
2014-11-04 18:05:29 +01:00
|
|
|
request(app)
|
2014-07-23 15:51:11 +02:00
|
|
|
.get('/library/test/foo')
|
|
|
|
.expect(200)
|
|
|
|
.end(function(err,res) {
|
|
|
|
if (err) {
|
|
|
|
throw err;
|
|
|
|
}
|
2015-02-26 18:08:20 +01:00
|
|
|
res.text.should.equal(flow.text);
|
2014-07-23 15:51:11 +02:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2014-11-04 18:05:29 +01:00
|
|
|
|
2014-07-23 15:51:11 +02:00
|
|
|
it('lists a stored item', function(done) {
|
2015-02-26 18:08:20 +01:00
|
|
|
initStorage({},{'test':{'a':['abc','def']}});
|
2014-11-04 18:05:29 +01:00
|
|
|
request(app)
|
2015-02-26 18:08:20 +01:00
|
|
|
.get('/library/test/a')
|
2014-11-04 18:05:29 +01:00
|
|
|
.expect(200)
|
|
|
|
.end(function(err,res) {
|
|
|
|
if (err) {
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
// This response isn't strictly accurate - but it
|
|
|
|
// verifies the api returns what storage gave it
|
|
|
|
should.deepEqual(res.body,['abc','def']);
|
|
|
|
done();
|
|
|
|
});
|
2014-07-23 15:51:11 +02:00
|
|
|
});
|
2014-11-04 18:05:29 +01:00
|
|
|
|
|
|
|
|
2014-07-29 14:51:21 +02:00
|
|
|
it('returns 403 for malicious access attempt', function(done) {
|
2014-11-04 18:05:29 +01:00
|
|
|
request(app)
|
2014-07-29 14:51:21 +02:00
|
|
|
.get('/library/test/../../../../../../../../../../etc/passwd')
|
|
|
|
.expect(403)
|
|
|
|
.end(done);
|
|
|
|
});
|
2014-11-04 18:05:29 +01:00
|
|
|
|
2014-07-29 14:51:21 +02:00
|
|
|
it('returns 403 for malicious access attempt', function(done) {
|
2014-11-04 18:05:29 +01:00
|
|
|
request(app)
|
2014-07-29 14:51:21 +02:00
|
|
|
.get('/library/test/..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\etc\\passwd')
|
|
|
|
.expect(403)
|
|
|
|
.end(done);
|
|
|
|
});
|
2014-11-04 18:05:29 +01:00
|
|
|
|
2014-07-29 14:51:21 +02:00
|
|
|
it('returns 403 for malicious access attempt', function(done) {
|
2014-11-04 18:05:29 +01:00
|
|
|
request(app)
|
2014-07-29 14:51:21 +02:00
|
|
|
.post('/library/test/../../../../../../../../../../etc/passwd')
|
|
|
|
.set('Content-Type', 'text/plain')
|
|
|
|
.send('root:x:0:0:root:/root:/usr/bin/tclsh')
|
|
|
|
.expect(403)
|
|
|
|
.end(done);
|
|
|
|
});
|
2014-11-04 18:05:29 +01:00
|
|
|
|
2014-07-17 09:34:26 +02:00
|
|
|
});
|
|
|
|
});
|