Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b974dad

Browse files
committed
Python: Add additional taint steps for containers
1 parent b604976 commit b974dad

3 files changed

Lines changed: 59 additions & 22 deletions

File tree

python/ql/src/experimental/dataflow/internal/TaintTrackingPrivate.qll

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeT
3232
stringManipulation(nodeFrom, nodeTo)
3333
or
3434
jsonStep(nodeFrom, nodeTo)
35+
or
36+
containerStep(nodeFrom, nodeTo)
3537
}
3638

3739
/**
@@ -135,3 +137,38 @@ predicate jsonStep(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeTo) {
135137
call.getArg(0) = nodeFrom.getNode()
136138
)
137139
}
140+
141+
/**
142+
* Holds if taint can flow from `nodeFrom` to `nodeTo` with a step related to containers
143+
* (lists/sets/dictionaries): literals, constructor invocation, methods. Note that this
144+
* is currently very imprecise, as an example, since we model `dict.get`, we treat any
145+
* `<tainted object>.get(<arg>)` will be tainted, whether it's true or not.
146+
*/
147+
predicate containerStep(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeTo) {
148+
// construction by literal
149+
// TODO: Not limiting the content argument here feels like a BIG hack, but we currently get nothing for free :|
150+
storeStep(nodeFrom, _, nodeTo)
151+
or
152+
// constructor call
153+
exists(CallNode call | call = nodeTo.getNode() |
154+
call.getFunction().(NameNode).getId() in ["list", "set", "frozenset", "dict", "defaultdict", "tuple"] and
155+
call.getArg(0) = nodeFrom.getNode()
156+
)
157+
or
158+
// functions operating on collections
159+
exists(CallNode call | call = nodeTo.getNode() |
160+
call.getFunction().(NameNode).getId() in ["sorted", "reversed", "iter", "next"] and
161+
call.getArg(0) = nodeFrom.getNode()
162+
)
163+
or
164+
// methods
165+
exists(CallNode call, string name | call = nodeTo.getNode() |
166+
name in [
167+
// general
168+
"copy", "pop",
169+
// dict
170+
"values", "items", "get", "popitem"
171+
] and
172+
call.getFunction().(AttrNode).getObject(name) = nodeFrom.getNode()
173+
)
174+
}

python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/TestTaint.expected

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
| collections.py:16 | fail | test_access | tainted_list.copy() |
1+
| collections.py:16 | ok | test_access | tainted_list.copy() |
22
| string.py:17 | ok | str_methods | ts.casefold() |
33
| string.py:19 | ok | str_methods | ts.format_map(..) |
4-
| string.py:20 | fail | str_methods | "{unsafe}".format_map(..) |
4+
| string.py:20 | ok | str_methods | "{unsafe}".format_map(..) |
55
| string.py:31 | fail | binary_decode_encode | base64.a85encode(..) |
66
| string.py:32 | fail | binary_decode_encode | base64.a85decode(..) |
77
| string.py:35 | fail | binary_decode_encode | base64.b85encode(..) |

python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/TestTaint.expected

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
| collections.py:24 | ok | test_construction | tainted_string |
2-
| collections.py:25 | fail | test_construction | tainted_list |
3-
| collections.py:26 | fail | test_construction | tainted_tuple |
4-
| collections.py:27 | fail | test_construction | tainted_set |
5-
| collections.py:28 | fail | test_construction | tainted_dict |
6-
| collections.py:32 | fail | test_construction | list(..) |
7-
| collections.py:33 | fail | test_construction | list(..) |
8-
| collections.py:34 | fail | test_construction | list(..) |
9-
| collections.py:35 | fail | test_construction | list(..) |
10-
| collections.py:36 | fail | test_construction | list(..) |
11-
| collections.py:38 | fail | test_construction | tuple(..) |
12-
| collections.py:39 | fail | test_construction | set(..) |
13-
| collections.py:40 | fail | test_construction | frozenset(..) |
2+
| collections.py:25 | ok | test_construction | tainted_list |
3+
| collections.py:26 | ok | test_construction | tainted_tuple |
4+
| collections.py:27 | ok | test_construction | tainted_set |
5+
| collections.py:28 | ok | test_construction | tainted_dict |
6+
| collections.py:32 | ok | test_construction | list(..) |
7+
| collections.py:33 | ok | test_construction | list(..) |
8+
| collections.py:34 | ok | test_construction | list(..) |
9+
| collections.py:35 | ok | test_construction | list(..) |
10+
| collections.py:36 | ok | test_construction | list(..) |
11+
| collections.py:38 | ok | test_construction | tuple(..) |
12+
| collections.py:39 | ok | test_construction | set(..) |
13+
| collections.py:40 | ok | test_construction | frozenset(..) |
1414
| collections.py:48 | ok | test_access | tainted_list[0] |
1515
| collections.py:49 | ok | test_access | tainted_list[x] |
1616
| collections.py:50 | ok | test_access | tainted_list[Slice] |
17-
| collections.py:52 | fail | test_access | sorted(..) |
18-
| collections.py:53 | fail | test_access | reversed(..) |
19-
| collections.py:54 | fail | test_access | iter(..) |
20-
| collections.py:55 | fail | test_access | next(..) |
17+
| collections.py:52 | ok | test_access | sorted(..) |
18+
| collections.py:53 | ok | test_access | reversed(..) |
19+
| collections.py:54 | ok | test_access | iter(..) |
20+
| collections.py:55 | ok | test_access | next(..) |
2121
| collections.py:56 | fail | test_access | copy(..) |
2222
| collections.py:57 | ok | test_access | deepcopy(..) |
2323
| collections.py:61 | fail | test_access | a |
@@ -26,9 +26,9 @@
2626
| collections.py:64 | fail | test_access | h |
2727
| collections.py:66 | fail | test_access | i |
2828
| collections.py:73 | ok | test_dict_access | tainted_dict["name"] |
29-
| collections.py:74 | fail | test_dict_access | tainted_dict.get(..) |
29+
| collections.py:74 | ok | test_dict_access | tainted_dict.get(..) |
3030
| collections.py:75 | ok | test_dict_access | tainted_dict[x] |
31-
| collections.py:76 | fail | test_dict_access | tainted_dict.copy() |
31+
| collections.py:76 | ok | test_dict_access | tainted_dict.copy() |
3232
| collections.py:80 | fail | test_dict_access | v |
3333
| collections.py:82 | fail | test_dict_access | v |
3434
| collections.py:90 | fail | test_named_tuple | point[0] |
@@ -70,7 +70,7 @@
7070
| string.py:49 | ok | str_methods | "{}".format(..) |
7171
| string.py:50 | ok | str_methods | "{unsafe}".format(..) |
7272
| string.py:52 | ok | str_methods | ts.join(..) |
73-
| string.py:53 | fail | str_methods | "".join(..) |
73+
| string.py:53 | ok | str_methods | "".join(..) |
7474
| string.py:55 | ok | str_methods | ts.ljust(..) |
7575
| string.py:56 | ok | str_methods | ts.lstrip() |
7676
| string.py:57 | ok | str_methods | ts.lower() |
@@ -99,7 +99,7 @@
9999
| string.py:100 | fail | non_syntactic | _str(..) |
100100
| string.py:109 | ok | percent_fmt | BinaryExpr |
101101
| string.py:110 | ok | percent_fmt | BinaryExpr |
102-
| string.py:111 | fail | percent_fmt | BinaryExpr |
102+
| string.py:111 | ok | percent_fmt | BinaryExpr |
103103
| string.py:121 | fail | binary_decode_encode | base64.b64encode(..) |
104104
| string.py:122 | fail | binary_decode_encode | base64.b64decode(..) |
105105
| string.py:124 | fail | binary_decode_encode | base64.standard_b64encode(..) |

0 commit comments

Comments
 (0)