Skip to content

Commit 7f4b23b

Browse files
committed
for #4, command whitelist. Breaking constructor change on ProcessProxy
1 parent b10f81e commit 7f4b23b

File tree

4 files changed

+169
-14
lines changed

4 files changed

+169
-14
lines changed

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,18 @@ To use StatefulProcessCommandProxy the constructor takes one parameter which is
8383
8484
processCmdBlacklistRegex: optional config array regex patterns who if match the
8585
command requested to be executed will be rejected
86-
with an error
86+
with an error. Blacklists run before whitelists
87+
8788
[ {regex:'regex1',flags:'ig'},
8889
{regex:'regex2',flags:'ig'}...]
8990
91+
processCmdWhitelistRegex: optional config array regex patterns defining commands
92+
that are permitted to execute, if no match, the command
93+
will be rejected. Whitelists run after blacklists
94+
95+
[ {regex:'regex1',flags:'ig'},
96+
{regex:'regex2',flags:'ig'}...]
97+
9098
processCwd: optional current working directory for the processes to be spawned
9199
92100
processEnvMap: optional hash/object of key-value pairs for environment variables

processProxy.js

Lines changed: 104 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,16 @@ fifo.prototype.toArray = function () {
6666
*
6767
* @param processCmdBlacklistRegex optional config array regex patterns who if match the
6868
* command requested to be executed will be rejected
69-
* with an error.
69+
* with an error. Blacklisted commands are checked
70+
* before whitelisted commands below
71+
*
72+
* [ '{regex:'regex1',flags:'ig'},
73+
* {regex:'regex2',flags:'m'}...]
74+
*
75+
* @param processCmdWhitelistRegex optional config array regex patterns who must match
76+
* the command requested to be executed otherwise
77+
* will be rejected with an error. Whitelisted commands
78+
* are checked AFTER blacklisted commands above...
7079
*
7180
* [ '{regex:'regex1',flags:'ig'},
7281
* {regex:'regex2',flags:'m'}...]
@@ -106,11 +115,13 @@ fifo.prototype.toArray = function () {
106115
* }
107116
*
108117
*
118+
*
109119
*/
110120
function ProcessProxy(processToSpawn, arguments,
111121
retainMaxCmdHistory, invalidateOnRegex,
112122
cwd, envMap, uid, gid, logFunction,
113123
processCmdBlacklistRegex,
124+
processCmdWhitelistRegex,
114125
autoInvalidationConfig) {
115126

116127
this._createdAt = new Date();
@@ -138,6 +149,15 @@ function ProcessProxy(processToSpawn, arguments,
138149
this._parseRegexes(processCmdBlacklistRegex,[this._cmdBlacklistRegexes]);
139150
}
140151

152+
this._cmdWhitelistRegexes = []; // holds RegExp objs
153+
this._cmdWhitelistRegexesConfs = processCmdWhitelistRegex; // retains orig configs
154+
if (typeof(processCmdWhitelistRegex) == 'undefined') {
155+
// nothing to do
156+
} else {
157+
// parse them
158+
this._parseRegexes(processCmdWhitelistRegex,[this._cmdWhitelistRegexes]);
159+
}
160+
141161

142162
// auto invalidation config build
143163
this._buildAutoInvalidationConfig(autoInvalidationConfig);
@@ -412,6 +432,13 @@ ProcessProxy.prototype._handleCommandFinished = function(command) {
412432
* Returns true if the command is blacklisted due to a match, false on no matches
413433
*/
414434
ProcessProxy.prototype._commandIsBlacklisted = function(command) {
435+
436+
// no blacklist? then its not blacklisted
437+
if (this._cmdBlacklistRegexes.length == 0) {
438+
return false;
439+
}
440+
441+
415442
for (var i=0; i<this._cmdBlacklistRegexes.length; i++) {
416443
var regexp = this._cmdBlacklistRegexes[i];
417444
var result = regexp.exec(command);
@@ -426,6 +453,35 @@ ProcessProxy.prototype._commandIsBlacklisted = function(command) {
426453
return false;
427454
}
428455

456+
/**
457+
* _commandIsWhitelisted(command)
458+
*
459+
* Checks to see if current command matches any of the command
460+
* whitelist regexes
461+
*
462+
* Returns true if the command is whitelisted due to a match, false on no matches
463+
*/
464+
ProcessProxy.prototype._commandIsWhitelisted = function(command) {
465+
466+
// no whitelist? then its whitelisted
467+
if (this._cmdWhitelistRegexes.length == 0) {
468+
return true;
469+
}
470+
471+
for (var i=0; i<this._cmdWhitelistRegexes.length; i++) {
472+
var regexp = this._cmdWhitelistRegexes[i];
473+
var result = regexp.exec(command);
474+
475+
if (result) {
476+
return; // exit! command is whitelisted
477+
}
478+
}
479+
480+
this._log('error',"ProcessProxy: command does not match any configured " +
481+
"whitelist regexes, command: " + command);
482+
return false;
483+
}
484+
429485

430486
/**
431487
* onData()
@@ -581,7 +637,7 @@ ProcessProxy.prototype.initialize = function(initCommands) {
581637
// run all initCommands if provided
582638
if (initCommands) {
583639

584-
self.executeCommands(initCommands)
640+
self._executeCommands(initCommands,false) // skip black/whitelists
585641

586642
.then(function(cmdResults) {
587643

@@ -769,23 +825,61 @@ ProcessProxy.prototype.executeCommand = function(command) {
769825
*
770826
**/
771827
ProcessProxy.prototype.executeCommands = function(commands) {
828+
return this._executeCommands(commands,true);
829+
}
830+
831+
832+
/**
833+
* Internal method only:
834+
*
835+
* executeCommands - takes an array of raw command strings and returns promise
836+
* to be fulfilled with a an array of
837+
* of [
838+
* {command:cmd1, stdout:xxxx, stderr:xxxxx},
839+
* {command:cmd2, stdout:xxxx, stderr:xxxxx}
840+
* ]
841+
*
842+
* @commands Array of raw command/shell statements to be executed
843+
* @enforceBlackWhitelists enforce white and blacklists
844+
*
845+
* @return Promise, on fulfill returns promise to be fulfilled with a
846+
* array of command results as described above, on reject
847+
* and Error object
848+
*
849+
**/
850+
ProcessProxy.prototype._executeCommands = function(commands, enforceBlackWhitelists) {
772851

773852
self = this;
774853

775854
return new Promise(function(fulfill, reject) {
776855

777856
try {
778857

779-
// scan for blacklisted, and fail fast
780-
for (var i=0; i<commands.length; i++) {
781-
var cmd = commands[i];
782-
if (self._commandIsBlacklisted(cmd)) {
858+
if (enforceBlackWhitelists) {
859+
860+
// scan for blacklisted, and fail fast
861+
for (var i=0; i<commands.length; i++) {
862+
var cmd = commands[i];
863+
if (self._commandIsBlacklisted(cmd)) {
783864
reject(new Error("Command cannot be executed as it matches a " +
784-
"blacklist regex pattern, see logs: command: " + cmd));
865+
"blacklist regex pattern, see logs: command: " + cmd));
785866
return; // exit!
867+
}
786868
}
869+
870+
// scan for whitelisted, and fail fast
871+
for (var i=0; i<commands.length; i++) {
872+
var cmd = commands[i];
873+
if (!self._commandIsWhitelisted(cmd)) {
874+
reject(new Error("Command cannot be executed it does not match " +
875+
"our set of whitelisted commands, see logs: command: " + cmd));
876+
return; // exit!
877+
}
878+
}
879+
787880
}
788881

882+
789883
var cmdResults = [];
790884

791885
for (var i = 0; i < commands.length; i++) {
@@ -849,7 +943,7 @@ ProcessProxy.prototype.shutdown = function(shutdownCommands) {
849943
// run all shutdownCommands if provided
850944
if (shutdownCommands) {
851945

852-
self.executeCommands(shutdownCommands)
946+
self._executeCommands(shutdownCommands,false) // skip black/whitelists
853947

854948
.then(function(cmdResults) {
855949

@@ -902,7 +996,8 @@ ProcessProxy.prototype.getStatus = function() {
902996
'options':this._processOptions,
903997
'isValid':this._isValid,
904998
'createdAt':(this._createdAt ? this._createdAt.toISOString() : null),
905-
'cmdBlacklistRegexes':this._cmdBlacklistRegexesConfs,
999+
'cmdBlacklistRegexesConfs':this._cmdBlacklistRegexesConfs,
1000+
'cmdWhitelistRegexesConfs':this._cmdWhitelistRegexesConfs,
9061001
'invalidateOnRegexConfig':this._invalidateOnRegexConfig,
9071002
'autoInvalidationConfig':this._autoInvalidationConfig,
9081003
'activeCommandStack':[],

statefulProcessCommandProxy.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ var Promise = require('promise');
4343
[ {regex:'regex1',flags:'ig'},
4444
{regex:'regex2',flags:'m'}...]
4545
46+
processCmdWhitelistRegex: optional config array regex patterns who if do not match
47+
the command requested it will be rejected
48+
with an error
49+
50+
[ {regex:'regex1',flags:'ig'},
51+
{regex:'regex2',flags:'m'}...]
52+
4653
4754
processCwd: optional current working directory for the processes to be spawned
4855
@@ -137,6 +144,7 @@ function StatefulProcessCommandProxy(config) {
137144
config.processGid,
138145
config.logFunction,
139146
config.processCmdBlacklistRegex,
147+
config.processCmdWhitelistRegex,
140148
config.autoInvalidationConfig);
141149

142150

test/all.js

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,9 @@ var doFinalTestRoutine = function(done,statefulProcessCommandProxy) {
9191
}
9292

9393

94-
var getStatefulProcessCommandProxyForTests = function(config,max,min,setAutoValidationConfig) {
94+
var getStatefulProcessCommandProxyForTests = function(config,max,min,
95+
setAutoValidationConfig,
96+
setWhitelistConfig) {
9597

9698
var Promise = require('promise');
9799
var StatefulProcessCommandProxy = require("..");
@@ -122,6 +124,8 @@ var getStatefulProcessCommandProxyForTests = function(config,max,min,setAutoVali
122124

123125
processCmdBlacklistRegex: [ {'regex':'.*blacklisted.*'} ],
124126

127+
processCmdWhitelistRegex: (setWhitelistConfig ? [ {'regex':'.*whitelisted.*'} ] : null),
128+
125129
processCwd : null,
126130
processEnvMap : {"testenvvar":"value1"},
127131
processUid : null,
@@ -156,7 +160,7 @@ describe('core-test', function() {
156160
// chose the right config based on platform
157161
var config = (isWin ? configs['windows'] : configs['nix']);
158162

159-
var statefulProcessCommandProxy = getStatefulProcessCommandProxyForTests(config,1,1,false);
163+
var statefulProcessCommandProxy = getStatefulProcessCommandProxyForTests(config,1,1,false,false);
160164

161165
// #1 invoke all test commands
162166
var promise = statefulProcessCommandProxy.executeCommands(Object.keys(config.testCommands));
@@ -198,7 +202,7 @@ describe('blacklist-test', function() {
198202
// chose the right config based on platform
199203
var config = (isWin ? configs['windows'] : configs['nix']);
200204

201-
var statefulProcessCommandProxy = getStatefulProcessCommandProxyForTests(config,1,1,false);
205+
var statefulProcessCommandProxy = getStatefulProcessCommandProxyForTests(config,1,1,false,false);
202206

203207
var promise = statefulProcessCommandProxy.executeCommand("echo 'some blacklisted command'")
204208

@@ -226,6 +230,46 @@ describe('blacklist-test', function() {
226230

227231
});
228232

233+
describe('whitelist-test', function() {
234+
235+
it('Spawn a pool of shells, fail invoking non-whitelisted command, then shutdown', function(done) {
236+
237+
this.timeout(10000);
238+
239+
var isWin = /^win/.test(process.platform);
240+
241+
// chose the right config based on platform
242+
var config = (isWin ? configs['windows'] : configs['nix']);
243+
244+
var statefulProcessCommandProxy = getStatefulProcessCommandProxyForTests(config,1,1,false,true);
245+
246+
var promise = statefulProcessCommandProxy
247+
.executeCommand("echo 'some non-white listed command'")
248+
249+
// when all commands are executed
250+
// lets assert them all
251+
promise.then(function(cmdResults) {
252+
253+
// should NOT get here!
254+
assert.equal(true,false);
255+
256+
}).catch(function(error) {
257+
258+
// should get here!
259+
assert(error.message.indexOf("whitelisted") != -1);
260+
261+
doFinalTestRoutine(done,statefulProcessCommandProxy);
262+
263+
}).catch(function(exception) {
264+
statefulProcessCommandProxy.shutdown();
265+
done(exception);
266+
});
267+
268+
269+
});
270+
271+
});
272+
229273

230274
describe('auto-invalidation-test', function() {
231275

@@ -238,7 +282,7 @@ describe('auto-invalidation-test', function() {
238282
// chose the right config based on platform
239283
var config = (isWin ? configs['windows'] : configs['nix']);
240284

241-
var statefulProcessCommandProxy = getStatefulProcessCommandProxyForTests(config,2,2,true);
285+
var statefulProcessCommandProxy = getStatefulProcessCommandProxyForTests(config,2,2,true,false);
242286

243287
// do some commands
244288
statefulProcessCommandProxy.executeCommand("echo 'hello'");

0 commit comments

Comments
 (0)