Skip to content

Commit b40312e

Browse files
qiuzhongMinggang Wang
authored andcommitted
Add security-related tests for rclnodejs (#245)
* Add security-related tests for rclnodejs * Destroy non-existent nodes * Fuzzing API calls * Complete the race condition and performace test cases * [UT] Update test cases for #274 Since #241 and #243 was fixed, update the test cases in #245.
1 parent db9fd05 commit b40312e

File tree

1 file changed

+274
-0
lines changed

1 file changed

+274
-0
lines changed

test/test-security-related.js

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
// Copyright (c) 2017 Intel Corporation. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
'use strict';
16+
17+
const assert = require('assert');
18+
const rclnodejs = require('../index.js');
19+
const assertThrowsError = require('./utils.js').assertThrowsError;
20+
const translator = require('../lib/message_translator.js');
21+
const arrayGen = require('./array_generator.js');
22+
23+
describe('Destroying non-existent objects testing', function() {
24+
this.timeout(60 * 1000);
25+
26+
before(function() {
27+
return rclnodejs.init();
28+
});
29+
30+
after(function() {
31+
rclnodejs.shutdown();
32+
});
33+
34+
it('Destroy a non-existent node', function() {
35+
var node = null;
36+
assertThrowsError(() => {
37+
node.destroy();
38+
}, TypeError, 'Cannot read property', 'Trying to destroy an empty node!');
39+
40+
var node = rclnodejs.createNode('node1', '/non_existent');
41+
node.destroy();
42+
43+
// It's OK to destroy a node multiple times
44+
// so long as the resources are freed in rcl layer.
45+
node.destroy();
46+
});
47+
48+
it('Destroy a non-existent publisher', function() {
49+
var node = rclnodejs.createNode('node2', '/non_existent');
50+
assertThrowsError(() => {
51+
node.destroyPublisher(null);
52+
}, TypeError, 'Invalid argument', 'Trying to destroy an empty publisher!');
53+
54+
const RclString = 'std_msgs/msg/String';
55+
var publisher = node.createPublisher(RclString, 'chatter2');
56+
node.destroyPublisher(publisher);
57+
58+
// OK to destroy a publisher multiple times
59+
node.destroyPublisher(publisher);
60+
});
61+
62+
it('Destroy a non-existent subscription', function() {
63+
var node = rclnodejs.createNode('node3', '/non_existent');
64+
assertThrowsError(() => {
65+
node.destroySubscription(null);
66+
}, TypeError, 'Invalid argument', 'Trying to destroy an empty subscription!');
67+
68+
const RclString = 'std_msgs/msg/String';
69+
var subscription = node.createSubscription(RclString, 'chatter3', () => {});
70+
node.destroySubscription(subscription);
71+
72+
// OK to destroy a subscription multiple times
73+
node.destroySubscription(subscription);
74+
});
75+
76+
it('Destroy a non-existent client', function() {
77+
var node = rclnodejs.createNode('node4', '/non_existent');
78+
assertThrowsError(() => {
79+
node.destroyClient(null);
80+
}, TypeError, 'Invalid argument', 'Trying to destroy an empty client!');
81+
82+
const AddTwoInts = 'example_interfaces/srv/AddTwoInts';
83+
var client = node.createClient(AddTwoInts, 'add_two_ints');
84+
node.destroyClient(client);
85+
86+
// OK to destroy a client multiple times
87+
node.destroyClient(client);
88+
});
89+
90+
it('Destroy a non-existent service', function() {
91+
var node = rclnodejs.createNode('node5', '/non_existent');
92+
assertThrowsError(() => {
93+
node.destroyService(null);
94+
}, TypeError, 'Invalid argument', 'Trying to destroy an empty service!');
95+
96+
const AddTwoInts = 'example_interfaces/srv/AddTwoInts';
97+
var service = node.createService(AddTwoInts, 'add_two_ints',
98+
(request, response) =>{});
99+
node.destroyService(service);
100+
101+
// OK to destroy a service multiple times
102+
node.destroyService(service);
103+
});
104+
105+
it('Destroy a non-existent timer', function() {
106+
var node = rclnodejs.createNode('node6', '/non_existent');
107+
assertThrowsError(() => {
108+
node.destroyTimer(null);
109+
}, TypeError, 'Invalid argument', 'Trying to destroy an empty timer!');
110+
111+
var count = 0;
112+
var timer = node.createTimer(100, () => { count++; });
113+
node.destroyTimer(timer);
114+
115+
// OK to destroy a timer multiple times
116+
node.destroyTimer(timer);
117+
});
118+
});
119+
120+
describe('Fuzzing API calls testing', function() {
121+
this.timeout(60 * 1000);
122+
123+
before(function() {
124+
return rclnodejs.init();
125+
});
126+
127+
after(function() {
128+
rclnodejs.shutdown();
129+
});
130+
131+
it('Unregistered message types', function() {
132+
var node = rclnodejs.createNode('node1', '/unregistered');
133+
const UnknownMsgType = 'std_msgs/msg/Foo';
134+
135+
assertThrowsError(() => {
136+
node.createPublisher(UnknownMsgType, 'chatter');
137+
}, Error, 'does not exist', 'Unrecoginzed message types!');
138+
139+
assertThrowsError(() => {
140+
node.createSubscription(UnknownMsgType, 'chatter', (msg) => {});
141+
}, Error, 'does not exist', 'Unrecoginzed message type!');
142+
143+
node.destroy();
144+
});
145+
146+
it('Unregistered service interfaces', function() {
147+
var node = rclnodejs.createNode('node2', '/unregistered');
148+
const UnknownInterface = 'example_interfaces/srv/Bar';
149+
150+
assertThrowsError(() => {
151+
node.createClient(UnknownInterface, 'chatter');
152+
}, Error, 'does not exist', 'Unrecoginzed service interface!');
153+
154+
assertThrowsError(() => {
155+
node.createService(UnknownInterface, 'chatter', (req, res) => {});
156+
}, Error, 'does not exist', 'Unrecoginzed service interface!');
157+
158+
node.destroy();
159+
});
160+
161+
it('Inconsistent message type for subscription', function() {
162+
var node = rclnodejs.createNode('node1', '/inconsistent');
163+
const RclString = 'std_msgs/msg/String';
164+
165+
var publisher = node.createPublisher(RclString, 'chatter7');
166+
assertThrowsError(() => {
167+
publisher.publish({a: 1});
168+
}, TypeError, 'Invalid argument', `Type should be ${RclString}`);
169+
170+
rclnodejs.spin(node);
171+
node.destroy();
172+
});
173+
174+
it('Inconsistent request data for service', function() {
175+
var node = rclnodejs.createNode('node2', '/inconsistent');
176+
const AddTwoInts = 'example_interfaces/srv/AddTwoInts';
177+
178+
var client = node.createClient(AddTwoInts, 'add_two_ints');
179+
var service = node.createService(AddTwoInts, 'add_two_ints', (request, response) => {
180+
assert.throws(() => {
181+
request.b;
182+
}, Error, 'This should never be reached.');
183+
});
184+
185+
assertThrowsError(() => {
186+
client.sendRequest({a: 1}, (response) => {});
187+
}, TypeError, 'Invalid argument', 'request.b does not exist');
188+
rclnodejs.spin(node);
189+
node.destroy();
190+
});
191+
192+
it('resources will be freed by shutdown', function() {
193+
var node = rclnodejs.createNode('node1', '/unhandled');
194+
const RclString = 'std_msgs/msg/String';
195+
const AddTwoInts = 'example_interfaces/srv/AddTwoInts';
196+
197+
var publisher = node.createPublisher(RclString, 'chatter9');
198+
var subscription = node.createSubscription(RclString, 'chatter9', () => {});
199+
var client = node.createClient(AddTwoInts, 'add_two_ints');
200+
var service = node.createService(AddTwoInts, 'add_two_ints', (request, response) => {});
201+
});
202+
203+
it('timer Creating with inconsistent type', function() {
204+
var node = rclnodejs.createNode('node3', '/inconsistent');
205+
const invalidParams = [
206+
['100', () => {}],
207+
[100, null]
208+
];
209+
invalidParams.forEach((param) => {
210+
assertThrowsError(() => {
211+
node.createTimer(param[0], param[1]);
212+
}, TypeError, 'Invalid argument', 'Failed to createTimer!');
213+
});
214+
215+
node.destroy();
216+
});
217+
218+
it('Race condition 1', function(done) {
219+
var node = rclnodejs.createNode('node4', '/race1');
220+
const RclString = 'std_msgs/msg/String';
221+
222+
var publisher = node.createPublisher(RclString, 'race_channel');
223+
var subscription = node.createSubscription(RclString, 'race_channel', (msg) => {
224+
node.destroy();
225+
done();
226+
});
227+
228+
publisher.publish('hello world!');
229+
rclnodejs.spin(node);
230+
});
231+
232+
it('Race condition 2', function(done) {
233+
var node = rclnodejs.createNode('node5', '/race2');
234+
const AddTwoInts = 'example_interfaces/srv/AddTwoInts';
235+
236+
var client = node.createClient(AddTwoInts, 'race_service');
237+
var service = node.createService(AddTwoInts, 'race_service', (req, res) => {
238+
node.destroy();
239+
done();
240+
});
241+
242+
let request = { a: 1, b: 2};
243+
client.sendRequest(request, (response) => {
244+
throw new Error('never reached');
245+
});
246+
rclnodejs.spin(node);
247+
});
248+
249+
it('Performace with big array data', function(done) {
250+
var node = rclnodejs.createNode('performance');
251+
const Image = 'sensor_msgs/msg/Image';
252+
253+
const dataLength = 320 * 240 * 4 * 4;
254+
let uint8Data = new Array(dataLength);
255+
for (let i = 0; i < dataLength; i++) {
256+
uint8Data[i] = i % 255;
257+
}
258+
/* eslint-disable camelcase */
259+
const value = {
260+
header: {stamp: {sec: 11223, nanosec: 44556}, frame_id: 'f001', },
261+
height: 240, width: 320, encoding: 'rgba', is_bigendian: false, step: 320*16, is_dense: false,
262+
data: Uint8Array.from(uint8Data),
263+
};
264+
265+
var publisher = node.createPublisher(Image, 'performance');
266+
var subscription = node.createSubscription(Image, 'performance', (message) => {
267+
assert.deepStrictEqual(message.data.length, dataLength);
268+
node.destroy();
269+
done();
270+
});
271+
publisher.publish(value);
272+
rclnodejs.spin(node);
273+
});
274+
});

0 commit comments

Comments
 (0)