Skip to content

Commit cffe573

Browse files
committed
add taint-steps for underscore methods
1 parent eb80705 commit cffe573

File tree

3 files changed

+73
-0
lines changed

3 files changed

+73
-0
lines changed

javascript/ql/src/semmle/javascript/frameworks/LodashUnderscore.qll

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,52 @@ module LodashUnderscore {
394394
succ = this.getExceptionalReturn()
395395
}
396396
}
397+
398+
/**
399+
* Holds if there is a taint-step involving a (non-function) underscore method from `pred` to `succ`.
400+
*/
401+
private predicate underscoreTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
402+
exists(string name, DataFlow::CallNode call |
403+
call = any(Member member | member.getName() = name).getACall()
404+
|
405+
name =
406+
["find", "filter", "findWhere", "where", "reject", "pluck", "max", "min", "sortBy",
407+
"shuffle", "sample", "toArray", "partition", "compact", "first", "initial", "last",
408+
"rest", "flatten", "without", "difference", "uniq", "unique", "unzip", "transpose",
409+
"object", "chunk", "values", "mapObject", "pick", "omit", "defaults", "clone", "tap",
410+
"identity"] and
411+
pred = call.getArgument(0) and
412+
succ = call
413+
or
414+
name = ["union", "zip"] and
415+
pred = call.getAnArgument() and
416+
succ = call
417+
or
418+
name =
419+
["each", "map", "every", "some", "max", "min", "sortBy", "partition", "mapObject", "tap"] and
420+
pred = call.getArgument(0) and
421+
succ = call.getABoundCallbackParameter(1, 0)
422+
or
423+
name = ["reduce", "reduceRight"] and
424+
pred = call.getArgument(0) and
425+
succ = call.getABoundCallbackParameter(1, 1)
426+
or
427+
name = ["map", "reduce", "reduceRight"] and
428+
pred = call.getCallback(1).getAReturn() and
429+
succ = call
430+
)
431+
}
432+
433+
/**
434+
* A model for taint-steps involving (non-function) underscore methods.
435+
*/
436+
private class UnderscoreTaintStep extends TaintTracking::AdditionalTaintStep {
437+
UnderscoreTaintStep() { underscoreTaintStep(this, _) }
438+
439+
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
440+
underscoreTaintStep(pred, succ) and pred = this
441+
}
442+
}
397443
}
398444

399445
/**

javascript/ql/test/library-tests/InterProceduralFlow/TaintTracking.expected

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,9 @@
8686
| tst.js:2:17:2:22 | "src1" | tst.js:61:16:61:18 | o.r |
8787
| tst.js:2:17:2:22 | "src1" | tst.js:68:16:68:22 | inner() |
8888
| tst.js:2:17:2:22 | "src1" | tst.js:80:16:80:22 | outer() |
89+
| underscore.js:2:17:2:22 | "src1" | underscore.js:3:15:3:28 | _.max(source1) |
90+
| underscore.js:5:17:5:22 | "src2" | underscore.js:6:15:6:34 | _.union([], source2) |
91+
| underscore.js:5:17:5:22 | "src2" | underscore.js:7:15:7:32 | _.zip(source2, []) |
92+
| underscore.js:9:17:9:22 | "src3" | underscore.js:11:17:11:17 | x |
93+
| underscore.js:14:17:14:22 | "src4" | underscore.js:16:17:16:17 | e |
94+
| underscore.js:19:17:19:22 | "src5" | underscore.js:20:15:20:44 | _.map([ ... ource5) |
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
(function() {
2+
var source1 = "src1";
3+
var sink1 = _.max(source1); // NOT OK
4+
5+
var source2 = "src2";
6+
var sink2 = _.union([], source2); // NOT OK
7+
var sink3 = _.zip(source2, []); // NOT OK
8+
9+
var source3 = "src3";
10+
_.map(source3, (x) => {
11+
let sink4 = x; // NOT OK
12+
});
13+
14+
var source4 = "src4";
15+
_.reduce(source4, (acc, e) => {
16+
let sink5 = e; // NOT OK
17+
});
18+
19+
var source5 = "src5";
20+
var sink6 = _.map([1,2,3], (x) => source5); // NOT OK
21+
})();

0 commit comments

Comments
 (0)