Skip to content

Commit 14740d4

Browse files
committed
move existing array taint stracking into Arrays.qll
1 parent 3ae1aad commit 14740d4

4 files changed

Lines changed: 96 additions & 88 deletions

File tree

javascript/ql/src/javascript.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import Customizations
66
import semmle.javascript.Aliases
77
import semmle.javascript.AMD
8+
import semmle.javascript.Arrays
89
import semmle.javascript.AST
910
import semmle.javascript.BasicBlocks
1011
import semmle.javascript.Base64
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import javascript
2+
private import semmle.javascript.dataflow.InferredTypes
3+
4+
/**
5+
* Classes and predicates for modelling TaintTracking steps for arrays.
6+
*/
7+
module ArrayTaintTracking {
8+
/**
9+
* A taint propagating data flow edge caused by the builtin array functions.
10+
*/
11+
private class ArrayFunctionTaintStep extends TaintTracking::AdditionalTaintStep {
12+
DataFlow::CallNode call;
13+
14+
ArrayFunctionTaintStep() { this = call }
15+
16+
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
17+
arrayFunctionTaintStep(pred, succ, call)
18+
}
19+
}
20+
21+
/**
22+
* A taint propagating data flow edge from `pred` to `succ` caused by a call `call` to a builtin array functions.
23+
*/
24+
predicate arrayFunctionTaintStep(DataFlow::Node pred, DataFlow::Node succ, DataFlow::CallNode call) {
25+
// `array.map(function (elt, i, ary) { ... })`: if `array` is tainted, then so are
26+
// `elt` and `ary`; similar for `forEach`
27+
exists(string name, Function f, int i |
28+
(name = "map" or name = "forEach") and
29+
(i = 0 or i = 2) and
30+
call.getArgument(0).analyze().getAValue().(AbstractFunction).getFunction() = f and
31+
call.(DataFlow::MethodCallNode).getMethodName() = name and
32+
pred = call.getReceiver() and
33+
succ = DataFlow::parameterNode(f.getParameter(i))
34+
)
35+
or
36+
// `array.map` with tainted return value in callback
37+
exists(DataFlow::FunctionNode f |
38+
call.(DataFlow::MethodCallNode).getMethodName() = "map" and
39+
call.getArgument(0) = f and // Require the argument to be a closure to avoid spurious call/return flow
40+
pred = f.getAReturn() and
41+
succ = call
42+
)
43+
or
44+
// `array.push(e)`, `array.unshift(e)`: if `e` is tainted, then so is `array`.
45+
exists(string name |
46+
name = "push" or
47+
name = "unshift"
48+
|
49+
pred = call.getAnArgument() and
50+
succ.(DataFlow::SourceNode).getAMethodCall(name) = call
51+
)
52+
or
53+
// `array.push(...e)`, `array.unshift(...e)`: if `e` is tainted, then so is `array`.
54+
exists(string name |
55+
name = "push" or
56+
name = "unshift"
57+
|
58+
pred = call.getASpreadArgument() and
59+
// Make sure we handle reflective calls
60+
succ = call.getReceiver().getALocalSource() and
61+
call.getCalleeName() = name
62+
)
63+
or
64+
// `array.splice(i, del, e)`: if `e` is tainted, then so is `array`.
65+
exists(string name | name = "splice" |
66+
pred = call.getArgument(2) and
67+
succ.(DataFlow::SourceNode).getAMethodCall(name) = call
68+
)
69+
or
70+
// `e = array.pop()`, `e = array.shift()`, or similar: if `array` is tainted, then so is `e`.
71+
exists(string name |
72+
name = "pop" or
73+
name = "shift" or
74+
name = "slice" or
75+
name = "splice"
76+
|
77+
call.(DataFlow::MethodCallNode).calls(pred, name) and
78+
succ = call
79+
)
80+
or
81+
// `e = Array.from(x)`: if `x` is tainted, then so is `e`.
82+
call = DataFlow::globalVarRef("Array").getAPropertyRead("from").getACall() and
83+
pred = call.getAnArgument() and
84+
succ = call
85+
or
86+
// `e = arr1.concat(arr2, arr3)`: if any of the `arr` is tainted, then so is `e`.
87+
call.(DataFlow::MethodCallNode).calls(pred, "concat") and
88+
succ = call
89+
or
90+
call.(DataFlow::MethodCallNode).getMethodName() = "concat" and
91+
succ = call and
92+
pred = call.getAnArgument()
93+
}
94+
}

javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll

Lines changed: 0 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -263,93 +263,6 @@ module TaintTracking {
263263
}
264264
}
265265

266-
/**
267-
* A taint propagating data flow edge caused by the builtin array functions.
268-
*/
269-
private class ArrayFunctionTaintStep extends AdditionalTaintStep {
270-
DataFlow::CallNode call;
271-
272-
ArrayFunctionTaintStep() { this = call }
273-
274-
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
275-
arrayFunctionTaintStep(pred, succ, call)
276-
}
277-
}
278-
279-
/**
280-
* A taint propagating data flow edge from `pred` to `succ` caused by a call `call` to a builtin array functions.
281-
*/
282-
predicate arrayFunctionTaintStep(DataFlow::Node pred, DataFlow::Node succ, DataFlow::CallNode call) {
283-
// `array.map(function (elt, i, ary) { ... })`: if `array` is tainted, then so are
284-
// `elt` and `ary`; similar for `forEach`
285-
exists(string name, Function f, int i |
286-
(name = "map" or name = "forEach") and
287-
(i = 0 or i = 2) and
288-
call.getArgument(0).analyze().getAValue().(AbstractFunction).getFunction() = f and
289-
call.(DataFlow::MethodCallNode).getMethodName() = name and
290-
pred = call.getReceiver() and
291-
succ = DataFlow::parameterNode(f.getParameter(i))
292-
)
293-
or
294-
// `array.map` with tainted return value in callback
295-
exists(DataFlow::FunctionNode f |
296-
call.(DataFlow::MethodCallNode).getMethodName() = "map" and
297-
call.getArgument(0) = f and // Require the argument to be a closure to avoid spurious call/return flow
298-
pred = f.getAReturn() and
299-
succ = call
300-
)
301-
or
302-
// `array.push(e)`, `array.unshift(e)`: if `e` is tainted, then so is `array`.
303-
exists(string name |
304-
name = "push" or
305-
name = "unshift"
306-
|
307-
pred = call.getAnArgument() and
308-
succ.(DataFlow::SourceNode).getAMethodCall(name) = call
309-
)
310-
or
311-
// `array.push(...e)`, `array.unshift(...e)`: if `e` is tainted, then so is `array`.
312-
exists(string name |
313-
name = "push" or
314-
name = "unshift"
315-
|
316-
pred = call.getASpreadArgument() and
317-
// Make sure we handle reflective calls
318-
succ = call.getReceiver().getALocalSource() and
319-
call.getCalleeName() = name
320-
)
321-
or
322-
// `array.splice(i, del, e)`: if `e` is tainted, then so is `array`.
323-
exists(string name | name = "splice" |
324-
pred = call.getArgument(2) and
325-
succ.(DataFlow::SourceNode).getAMethodCall(name) = call
326-
)
327-
or
328-
// `e = array.pop()`, `e = array.shift()`, or similar: if `array` is tainted, then so is `e`.
329-
exists(string name |
330-
name = "pop" or
331-
name = "shift" or
332-
name = "slice" or
333-
name = "splice"
334-
|
335-
call.(DataFlow::MethodCallNode).calls(pred, name) and
336-
succ = call
337-
)
338-
or
339-
// `e = Array.from(x)`: if `x` is tainted, then so is `e`.
340-
call = DataFlow::globalVarRef("Array").getAPropertyRead("from").getACall() and
341-
pred = call.getAnArgument() and
342-
succ = call
343-
or
344-
// `e = arr1.concat(arr2, arr3)`: if any of the `arr` is tainted, then so is `e`.
345-
call.(DataFlow::MethodCallNode).calls(pred, "concat") and
346-
succ = call
347-
or
348-
call.(DataFlow::MethodCallNode).getMethodName() = "concat" and
349-
succ = call and
350-
pred = call.getAnArgument()
351-
}
352-
353266
/**
354267
* A taint propagating data flow edge for assignments of the form `o[k] = v`, where
355268
* `k` is not a constant and `o` refers to some object literal; in this case, we consider

javascript/ql/src/semmle/javascript/security/dataflow/IndirectCommandArgument.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ private DataFlow::SourceNode argumentList(SystemCommandExecution sys, DataFlow::
5252
result = pred.backtrack(t2, t)
5353
or
5454
t = t2.continue() and
55-
TaintTracking::arrayFunctionTaintStep(result, pred, _)
55+
ArrayTaintTracking::arrayFunctionTaintStep(result, pred, _)
5656
)
5757
}
5858

0 commit comments

Comments
 (0)