Python: Fix iterable-unpacking taint CP

When running ql/python/ql/src/Security/CWE-079/ReflectedXss.ql against the
database for flask.

Iitially there were 10 million result-tuples for iterable_unpacking_descent.

With this change, we're down to roughly 2100,
This commit is contained in:
Rasmus Wriedt Larsen
2020-03-26 16:42:48 +01:00
parent 0f70da2258
commit 96d1fc8c0b

View File

@@ -712,32 +712,16 @@ private class EssaTaintTracking extends string {
TaintTrackingNode src, MultiAssignmentDefinition defn, TaintTrackingContext context,
AttributePath path, TaintKind kind
) {
exists(DataFlow::Node srcnode, TaintKind srckind, Assign assign |
exists(DataFlow::Node srcnode, TaintKind srckind, Assign assign, int depth |
src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and
path.noAttribute()
|
assign.getValue().getAFlowNode() = srcnode.asCfgNode() and
kind = iterable_unpacking_descent(assign.getATarget().getAFlowNode(), defn.getDefiningNode(),
srckind)
depth = iterable_unpacking_descent(assign.getATarget().getAFlowNode(), defn.getDefiningNode()) and
kind = taint_at_depth(srckind, depth)
)
}
/** `((x,y), ...) = value` with any nesting on LHS */
private TaintKind iterable_unpacking_descent(
SequenceNode left_parent, ControlFlowNode left_defn, CollectionKind parent_kind
) {
//TODO: Fix the cartesian product in this predicate
none() and
left_parent.getAnElement() = left_defn and
// Handle `a, *b = some_iterable`
if left_defn instanceof StarredNode
then result = parent_kind
else result = parent_kind.getMember()
or
result = iterable_unpacking_descent(left_parent.getAnElement(), left_defn,
parent_kind.getMember())
}
pragma[noinline]
private predicate taintedAttributeAssignment(
TaintTrackingNode src, AttributeAssignment defn, TaintTrackingContext context,
@@ -967,6 +951,45 @@ private predicate piNodeTestAndUse(PyEdgeRefinement defn, ControlFlowNode test,
test = defn.getTest() and use = defn.getInput().getASourceUse() and test.getAChild*() = use
}
/** Helper predicate for taintedMultiAssignment */
private TaintKind taint_at_depth(SequenceKind parent_kind, int depth) {
depth >= 0 and
(
// base-case #0
depth = 0 and
result = parent_kind
or
// base-case #1
depth = 1 and
result = parent_kind.getMember()
or
// recursive case
result = taint_at_depth(parent_kind.getMember(), depth-1)
)
}
/** Helper predicate for taintedMultiAssignment
*
* Returns the `depth` the elements that are assigned to `left_defn` with iterable unpacking has,
* compared to `left_parent`. Special care is taken for `StarredNode` that is assigned a sequence of items.
*
* For example, `((x, *y), ...) = value` with any nesting on LHS
* - with `left_defn` = `x`, `left_parent` = `(x, *y)`, result = 1
* - with `left_defn` = `x`, `left_parent` = `((x, *y), ...)`, result = 2
* - with `left_defn` = `*y`, `left_parent` = `(x, *y)`, result = 0
* - with `left_defn` = `*y`, `left_parent` = `((x, *y), ...)`, result = 1
*/
int iterable_unpacking_descent(SequenceNode left_parent, ControlFlowNode left_defn) {
exists(Assign a | a.getATarget().getASubExpression*().getAFlowNode() = left_parent) and
left_parent.getAnElement() = left_defn and
// Handle `a, *b = some_iterable`
if left_defn instanceof StarredNode
then result = 0
else result = 1
or
result = 1 + iterable_unpacking_descent(left_parent.getAnElement(), left_defn)
}
module Implementation {
/* A call that returns a copy (or similar) of the argument */
predicate copyCall(ControlFlowNode fromnode, CallNode tonode) {