Skip to content
This repository was archived by the owner on Dec 4, 2021. It is now read-only.

Commit 0d1accf

Browse files
committed
fixed exporting sinks from one module to another
1 parent f26fb2c commit 0d1accf

File tree

8 files changed

+115
-81
lines changed

8 files changed

+115
-81
lines changed

bin/check

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ check.setFlags({
4444
verbose: argv.v,
4545
recursive: argv.r,
4646
json: !argv.j && !argv.v,
47-
debug: argv.d || true
47+
debug: argv.d || true,
48+
pretty: argv.p
4849
});
4950
var Scope = check.Scope;
5051

@@ -60,9 +61,13 @@ if (ast) {
6061
check.traverse(ast, scope);
6162

6263
if (check.flags.json) {
63-
if (check.reports.length !== 0){
64-
console.log(require('prettyjson').render(check.reports));
65-
} else
66-
console.log(colors.green('No vulneralbities found'));
64+
if (argv.p) {
65+
if (check.reports.length !== 0)
66+
console.log(require('prettyjson').render(check.reports));
67+
else
68+
console.log(colors.green('No vulneralbities found'));
69+
} else {
70+
console.log(check.reports);
71+
}
6772
}
6873
}

check.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ module.exports.setFlags = function(newFlags) {
7777
traverse(ast, newScope);
7878

7979
r = newScope.vars.module.exports;
80+
newScope.sinks.forEach(function (i) {
81+
if (i.indexOf('module.exports.') === 0)
82+
scope.sinks.push(i.replace('module.exports', 'a'))
83+
});
8084

8185
} else
8286
if (flags.verbose && !flags.json)

scope.js

Lines changed: 97 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
CE - Call Expression (functions)
55
SCE - (source) Call Expression
66
SCES - (source) Call Expression Statement
7-
SASSIGN - Assign as a sink
7+
SINK - Assign as a sink
88
*/
99

1010
var fs = require('fs'),
@@ -118,7 +118,7 @@ var returnCB = function (fe, node) {
118118
var arg = scope.resolveExpression(node.argument);
119119
Scope.log = l;
120120

121-
var source = scope.resolveSource(arg);
121+
var source = scope.resolveTree(scope.isSource, arg);
122122
if (source) {
123123
scope.source[fe.name] = fe.name;
124124
Scope.log.call(this, 'RETURN', node, fe.names)
@@ -133,39 +133,54 @@ function Scope (scope) {
133133
if (!this.vars.module) this.vars.module = {exports: {}};
134134
if (!this.vars.exports) this.vars.exports = {};
135135
if (!this.vars.global) this.vars.global = {};
136+
136137
// dynamic list of sources and sinks as variables get set to them
137138
this.sources = scope.sources || JSON.parse(JSON.stringify(Sources)); // clever clone
138139
this.sinks = scope.sinks || Sinks.slice(0); // another clever clone but for arrays
140+
139141
this.file = scope.file;
140142

141143
if (!Scope.baseFile)
142144
Scope.baseFile = scope.file;
143145

144-
this.reports = scope.reports || [{source: {name: 'process.argv', line: path.relative(Scope.baseFile.split('/').reverse().slice(1).reverse().join('/'), this.file)}}];
146+
file = path.relative(Scope.baseFile.split('/').reverse().slice(1).reverse().join('/'), this.file);
147+
this.reports = scope.reports || [{source: {name: 'process.argv', line: file + ':0'}}];
145148
}
146149

147150
Scope.log = function(type, node, name, value) {};
148151

149152
// handles creation of variables.
150153
Scope.prototype.track = function(variable) {
154+
debugger;
151155
var scope = this;
152156
var name = variable.id.name;
153157

158+
var type = "VAR";
154159
var expr = this.resolveExpression(variable.init, function(value) {
155-
if (value) {
156-
157-
var source = scope.resolveSource(value);
158-
if (source) {
159-
scope.sources[name] = source;
160-
Scope.log.call(scope, 'SOURCE', variable, name, source);
161-
}
160+
if (!value) {
161+
console.log('value was undefined for some reason');
162+
throw new Exception('value was undefined');
162163
}
163164

165+
var danger;
166+
if (variable.type == 'Identifier') {
167+
danger = scope.resolveTree(scope.isSink, value);
168+
if (danger) {
169+
scope.sinks.push(name);
170+
type = 'SINK';
171+
}
172+
} else {
173+
danger = scope.resolveTree(scope.isSource, value);
174+
if (danger) {
175+
scope.sources[name] = scope.resolve(name);
176+
type = 'SOURCE';
177+
}
178+
}
164179
});
165180

166181
this.vars[name] = expr;
167182

168-
Scope.log.call(this, 'VAR', variable, name, expr ? (expr.raw || expr.name || expr) : undefined);
183+
Scope.log.call(this, type, variable, name, expr ? (expr.raw || expr.name || expr) : undefined);
169184
};
170185

171186

@@ -226,7 +241,7 @@ Scope.prototype.resolveStatement = function(node) {
226241
if (arg.params && arg.body && arg.scope)
227242
return false; // skips callbacks
228243

229-
var source = scope.resolveSource(arg);
244+
var source = scope.resolveTree(scope.isSource, arg);
230245
if (source) {
231246

232247
// If the function is a sink and there is a source, return as sink;
@@ -246,41 +261,46 @@ Scope.prototype.resolveStatement = function(node) {
246261
case 'AssignmentExpression':
247262
var assign = scope.resolveAssignment(node);
248263
var names = assign.names;
264+
var type = 'ASSIGN';
249265
var value = this.resolveExpression(assign.value, function(value, isSource) {
250-
if (value) {
251-
var resolved = scope.resolve(value);
252-
var source;
253-
if (resolved && typeof resolved == 'string') {
254-
if (node.right.type == 'Identifier' &&
255-
(scope.isSink(value.name || value) || scope.isSink(resolved.name || resolved))) {
256-
scope.sinks.push(names);
257-
Scope.log.call(scope, 'SASSIGN', node, names.length==1?names[0]:names, value);
258-
} else {
259-
var source = scope.resolveSource(value);
260-
if (source) {
261-
scope.sources[names] = source;
262-
Scope.log.call(scope, 'SOURCE', node, names.length==1?names[0]:names, source);
263-
}
264-
}
266+
if (!value) {
267+
console.log('value was undefined for some reason');
268+
throw new Exception('value was undefined');
269+
}
270+
debugger;
271+
272+
var danger;
273+
if (assign.value.type == 'Identifier') {
274+
danger = scope.resolveTree(scope.isSink, value);
275+
if (danger) {
276+
names.forEach(function (name) {
277+
scope.sinks.push(name);
278+
});
279+
type = 'SINK'
280+
}
281+
} else {
282+
debugger;
283+
danger = scope.resolveTree(scope.isSource, value);
284+
if (danger) {
285+
names.forEach(function (name) {
286+
scope.sources[name] = scope.resolve(value);
287+
});
288+
type = 'SOURCE';
265289
}
266290
}
267291
});
268292

269293
names.forEach(function(name) {
270294
try {
271295
if (node.left.type == 'MemberExpression') {
272-
eval('scope.vars.' + name + ' = ' + JSON.stringify(value));
296+
v = typeof value == "object" ? JSON.stringify(value) : String(value);
297+
eval('scope.vars.' + name + ' = "' + v + '"');
273298
}
274-
} catch (e) {
275-
// if (flags.debug) {
276-
// console.error('Error reading line:'.red, scope.file + ':' + pos(node));
277-
// console.error(e.stack);
278-
// }
279-
}
299+
} catch (e) {}
280300
});
281301

282302
if (value)
283-
Scope.log.call(this, 'ASSIGN', node, names.length==1?names[0]:names, util.inspect(value.raw || value, {depth: 1}));
303+
Scope.log.call(this, type, node, names.length==1?names[0]:names, util.inspect(value.raw || value, {depth: 1}));
284304
break;
285305
case 'FunctionDeclaration':
286306
if (Scope.createNewScope)
@@ -343,7 +363,9 @@ Scope.prototype.resolveExpression = function(right, isSourceCB) {
343363
var scope = this;
344364
switch (right.type) {
345365
case 'Literal': // string, number, etc..
346-
return "'" + eval(right.raw) + "'";
366+
return typeof right.value == 'string' ?
367+
"'" + right.value + "'" : right.value;
368+
// return "'" + eval(right.raw) + "'";
347369
case 'Identifier': // variables, etc...
348370
// if variable is being set to a bad variable, mark it too as bad
349371
if (isSourceCB) {
@@ -385,8 +407,8 @@ Scope.prototype.resolveExpression = function(right, isSourceCB) {
385407
ce.arguments.some(function (arg) {
386408
if (arg.params && arg.body && arg.scope)
387409
return false; // skips callbacks
388-
var source = scope.resolveSource(arg);
389-
if (source) { // I do want to set source to resolveSource(arg)
410+
var source = scope.resolveTree(scope.isSource, arg);
411+
if (source) { // I do want to set source to resolveTree(scope.isSource, arg)
390412
// If the function is a sink and there is a source, return as sink;
391413
// If not a sink but still has source, return as a Source CES (possible taint)
392414

@@ -425,39 +447,46 @@ Scope.prototype.resolveExpression = function(right, isSourceCB) {
425447
case 'AssignmentExpression': // a = b
426448
var assign = scope.resolveAssignment(right);
427449
var names = assign.names;
450+
var type = 'ASSIGN';
428451
var value = this.resolveExpression(assign.value, function(value, isSource) {
429-
if (value) {
430-
var resolved = scope.resolve(value);
431-
if (resolved && typeof resolved == 'string') {
432-
if (scope.isSink(value.name || value) || scope.isSink(resolved.name || resolved)) {
433-
scope.sinks.push(names);
434-
Scope.log.call(scope, 'SASSIGN', right, names.length==1?names[0]:names, value);
435-
} else {
436-
var source = scope.resolveSource(value);
437-
if (source) {
438-
scope.sources[names] = source;
439-
Scope.log.call(scope, 'SOURCE', right, names.length==1?names[0]:names, source);
440-
}
441-
}
452+
if (!value) {
453+
console.log('value was undefined for some reason');
454+
throw new Exception('value was undefined');
455+
}
456+
457+
var danger;
458+
if (assign.value.type == 'Identifier') {
459+
danger = scope.resolveTree(scope.isSink, value);
460+
if (danger) {
461+
names.forEach(function (name) {
462+
scope.sinks.push(name);
463+
});
464+
type = 'SINK'
465+
}
466+
} else {
467+
danger = scope.resolveTree(scope.isSource, value);
468+
if (danger) {
469+
names.forEach(function (name) {
470+
scope.sources.push(name);
471+
});
472+
type = 'SOURCE';
442473
}
443474
}
444475
});
445476

446477
names.forEach(function(name) {
447478
try {
448-
if (right.left.type == 'MemberExpression') {
449-
if (scope.vars[name] || scope.vars[name.split('.').slice(-1).join('.')])
450-
eval('scope.vars.' + name + ' = ' + JSON.stringify(value));
451-
else
452-
eval('scope.vars.' + name + ' = ' + JSON.stringify(value));
479+
if (node.left.type == 'MemberExpression') {
480+
v = typeof value == "object" ? JSON.stringify(value) : String(value);
481+
eval('scope.vars.' + name + ' = "' + v + '"');
453482
}
454483
} catch (e) {
455484

456485
}
457486
});
458487

459488
if (value)
460-
Scope.log.call(this, 'ASSIGN', right, names.length==1?names[0]:names, util.inspect(value.raw || value, {depth: 1}));
489+
Scope.log.call(this, type, right, names.length==1?names[0]:names, util.inspect(value.raw || value, {depth: 1}));
461490

462491
return value;
463492
}
@@ -570,7 +599,7 @@ Scope.prototype.resolveFunctionExpression = function(node, newScope) {
570599
Scope.log = l;
571600

572601
if (fe.name) {
573-
var source = scope.resolveSource(arg);
602+
var source = scope.resolveTree(scope.isSource, arg);
574603
if (source) {
575604

576605
scope.sources[fe.name] = source;
@@ -584,33 +613,33 @@ Scope.prototype.resolveFunctionExpression = function(node, newScope) {
584613

585614
// complicated code to check if the argument is a source
586615
// and returns the part of it that is the source
587-
Scope.prototype.resolveSource = function(expr) {
616+
Scope.prototype.resolveTree = function(func, expr) {
588617
var scope = this;
589618

590619
// specifically handles call expressions
591620
if (expr.name && expr.arguments && expr.raw) {
592-
var source = false;
621+
var danger = false;
593622
expr.arguments.some(function (i) {
594-
source = scope.resolveSource(i);
595-
return !!source;
623+
danger = scope.resolveTree(func, i);
624+
return !!danger;
596625
});
597-
return source;
626+
return danger;
598627
} else if (expr.body && expr.params && expr.scope) {
599628
return false;
600629
}
601630

602631
var resolved = this.resolve(expr);
603632

604633
if (typeof expr == 'object' || typeof resolved == 'object') {
605-
var source;
634+
var danger;
606635
(traverseJSON(expr, function (a) {
607636
if (!a) return false;
608-
source = scope.resolveSource(a);
609-
return source;
637+
danger = scope.resolveTree(func, a);
638+
return danger;
610639
}));
611-
return source;
640+
return danger;
612641
} else {
613-
if (scope.isSource(expr.name || expr) || scope.isSource(resolved.name || resolved))
642+
if (func.call(scope, expr.name || expr) || func.call(scope, resolved.name || resolved))
614643
return resolved;
615644
}
616645

tests/lib/a.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
1-
var b = require('./b.js');
2-
b.e = eval;
3-
module.exports.b = b;
1+
module.exports.e = eval;
2+
module.exports.args = process.argv;

tests/lib/b.js

Lines changed: 0 additions & 1 deletion
This file was deleted.

tests/lib/c.js

Lines changed: 0 additions & 2 deletions
This file was deleted.

tests/lib/d.js

Whitespace-only changes.

tests/require.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
var a = require('./lib/a.js');
2-
console.log(a.b);
3-
a.b.e(process.argv[2]); //a.b.e == eval
2+
3+
a.e(a.args); //a.b.e == eval

0 commit comments

Comments
 (0)