Skip to content

Commit 781024d

Browse files
committed
Python: Recognize taint for iterable unpacking
1 parent a3f1f4c commit 781024d

File tree

6 files changed

+89
-31
lines changed

6 files changed

+89
-31
lines changed

python/ql/src/semmle/python/dataflow/Implementation.qll

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,8 @@ private class EssaTaintTracking extends string {
658658
or
659659
this.taintedAssignment(src, defn, context, path, kind)
660660
or
661+
this.taintedMultiAssignment(src, defn, context, path, kind)
662+
or
661663
this.taintedAttributeAssignment(src, defn, context, path, kind)
662664
or
663665
this.taintedParameterDefinition(src, defn, context, path, kind)
@@ -705,6 +707,32 @@ private class EssaTaintTracking extends string {
705707
)
706708
}
707709

710+
pragma[noinline]
711+
private predicate taintedMultiAssignment(
712+
TaintTrackingNode src, MultiAssignmentDefinition defn, TaintTrackingContext context,
713+
AttributePath path, TaintKind kind
714+
) {
715+
exists(DataFlow::Node srcnode, TaintKind srckind, Assign assign |
716+
src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and
717+
path.noAttribute()
718+
|
719+
assign.getValue().getAFlowNode() = srcnode.asCfgNode() and
720+
kind = iterable_unpacking_decent(assign.getATarget().getAFlowNode(), defn.getDefiningNode(),
721+
srckind)
722+
)
723+
}
724+
725+
/** `((x,y), ...) = value` with any nesting on LHS */
726+
private TaintKind iterable_unpacking_decent(
727+
SequenceNode left_parent, ControlFlowNode left_defn, CollectionKind parent_kind
728+
) {
729+
left_parent.getAnElement() = left_defn and
730+
result = parent_kind.getMember()
731+
or
732+
result = iterable_unpacking_decent(left_parent.getAnElement(), left_defn,
733+
parent_kind.getMember())
734+
}
735+
708736
pragma[noinline]
709737
private predicate taintedAttributeAssignment(
710738
TaintTrackingNode src, AttributeAssignment defn, TaintTrackingContext context,

python/ql/test/library-tests/taint/collections/TestStep.expected

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
| Taint [externally controlled string] | test.py:29 | test.py:29:9:29:25 | Subscript | | --> | Taint [externally controlled string] | test.py:32 | test.py:32:16:32:16 | c | |
2727
| Taint [externally controlled string] | test.py:30 | test.py:30:9:30:20 | tainted_list | | --> | Taint [externally controlled string] | test.py:30 | test.py:30:9:30:27 | Attribute() | |
2828
| Taint [externally controlled string] | test.py:30 | test.py:30:9:30:27 | Attribute() | | --> | Taint [externally controlled string] | test.py:32 | test.py:32:19:32:19 | d | |
29+
| Taint [externally controlled string] | test.py:31 | test.py:31:15:31:26 | tainted_list | | --> | Taint externally controlled string | test.py:32 | test.py:32:22:32:22 | e | |
30+
| Taint [externally controlled string] | test.py:31 | test.py:31:15:31:26 | tainted_list | | --> | Taint externally controlled string | test.py:32 | test.py:32:25:32:25 | f | |
31+
| Taint [externally controlled string] | test.py:31 | test.py:31:15:31:26 | tainted_list | | --> | Taint externally controlled string | test.py:32 | test.py:32:28:32:28 | g | |
2932
| Taint [externally controlled string] | test.py:33 | test.py:33:14:33:25 | tainted_list | | --> | Taint externally controlled string | test.py:33 | test.py:33:5:33:26 | For | |
3033
| Taint [externally controlled string] | test.py:35 | test.py:35:14:35:35 | reversed() | | --> | Taint externally controlled string | test.py:35 | test.py:35:5:35:36 | For | |
3134
| Taint [externally controlled string] | test.py:35 | test.py:35:23:35:34 | tainted_list | | --> | Taint [externally controlled string] | test.py:35 | test.py:35:14:35:35 | reversed() | |

python/ql/test/library-tests/taint/collections/TestTaint.expected

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
| test.py:32 | test_access | b | externally controlled string |
1111
| test.py:32 | test_access | c | [externally controlled string] |
1212
| test.py:32 | test_access | d | [externally controlled string] |
13-
| test.py:32 | test_access | e | NO TAINT |
14-
| test.py:32 | test_access | f | NO TAINT |
15-
| test.py:32 | test_access | g | NO TAINT |
13+
| test.py:32 | test_access | e | externally controlled string |
14+
| test.py:32 | test_access | f | externally controlled string |
15+
| test.py:32 | test_access | g | externally controlled string |
1616
| test.py:34 | test_access | h | externally controlled string |
1717
| test.py:36 | test_access | i | externally controlled string |
1818
| test.py:43 | test_dict_access | a | externally controlled string |

python/ql/test/library-tests/taint/collections/test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def test_access():
2828
b = tainted_list[x]
2929
c = tainted_list[y:z]
3030
d = tainted_list.copy()
31-
e, f, g = tainted_list # TODO: currently not handled
31+
e, f, g = tainted_list
3232
test(a, b, c, d, e, f, g)
3333
for h in tainted_list:
3434
test(h)

python/ql/test/library-tests/taint/unpacking/TestStep.expected

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,35 @@
11
| Taint [[externally controlled string]] | test.py:19 | test.py:19:10:19:18 | List | | --> | Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | |
22
| Taint [[externally controlled string]] | test.py:19 | test.py:19:10:19:18 | List | | --> | Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | |
33
| Taint [[externally controlled string]] | test.py:19 | test.py:19:10:19:18 | List | | --> | Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | |
4+
| Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | | --> | Taint [externally controlled string] | test.py:23 | test.py:23:22:23:22 | b | |
5+
| Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | | --> | Taint [externally controlled string] | test.py:23 | test.py:23:25:23:25 | c | |
6+
| Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | | --> | Taint externally controlled string | test.py:23 | test.py:23:10:23:11 | a1 | |
7+
| Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | | --> | Taint externally controlled string | test.py:23 | test.py:23:14:23:15 | a2 | |
8+
| Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | | --> | Taint externally controlled string | test.py:23 | test.py:23:18:23:19 | a3 | |
9+
| Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | | --> | Taint [externally controlled string] | test.py:27 | test.py:27:22:27:22 | b | |
10+
| Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | | --> | Taint [externally controlled string] | test.py:27 | test.py:27:25:27:25 | c | |
11+
| Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | | --> | Taint externally controlled string | test.py:27 | test.py:27:10:27:11 | a1 | |
12+
| Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | | --> | Taint externally controlled string | test.py:27 | test.py:27:14:27:15 | a2 | |
13+
| Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | | --> | Taint externally controlled string | test.py:27 | test.py:27:18:27:19 | a3 | |
14+
| Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | | --> | Taint [externally controlled string] | test.py:31 | test.py:31:22:31:22 | b | |
15+
| Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | | --> | Taint [externally controlled string] | test.py:31 | test.py:31:25:31:25 | c | |
16+
| Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | | --> | Taint externally controlled string | test.py:31 | test.py:31:10:31:11 | a1 | |
17+
| Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | | --> | Taint externally controlled string | test.py:31 | test.py:31:14:31:15 | a2 | |
18+
| Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | | --> | Taint externally controlled string | test.py:31 | test.py:31:18:31:19 | a3 | |
19+
| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:10:48:10 | a | |
20+
| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:13:48:13 | b | |
21+
| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:16:48:16 | c | |
22+
| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:19:48:19 | d | |
23+
| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:22:48:22 | e | |
24+
| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:25:48:25 | f | |
425
| Taint [externally controlled string] | test.py:6 | test.py:6:9:6:20 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:7 | test.py:7:15:7:15 | l | |
26+
| Taint [externally controlled string] | test.py:7 | test.py:7:15:7:15 | l | | --> | Taint externally controlled string | test.py:8 | test.py:8:10:8:10 | a | |
27+
| Taint [externally controlled string] | test.py:7 | test.py:7:15:7:15 | l | | --> | Taint externally controlled string | test.py:8 | test.py:8:13:8:13 | b | |
28+
| Taint [externally controlled string] | test.py:7 | test.py:7:15:7:15 | l | | --> | Taint externally controlled string | test.py:8 | test.py:8:16:8:16 | c | |
529
| Taint [externally controlled string] | test.py:12 | test.py:12:9:12:20 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:13 | test.py:13:17:13:17 | l | |
30+
| Taint [externally controlled string] | test.py:13 | test.py:13:17:13:17 | l | | --> | Taint externally controlled string | test.py:14 | test.py:14:10:14:10 | a | |
31+
| Taint [externally controlled string] | test.py:13 | test.py:13:17:13:17 | l | | --> | Taint externally controlled string | test.py:14 | test.py:14:13:14:13 | b | |
32+
| Taint [externally controlled string] | test.py:13 | test.py:13:17:13:17 | l | | --> | Taint externally controlled string | test.py:14 | test.py:14:16:14:16 | c | |
633
| Taint [externally controlled string] | test.py:18 | test.py:18:9:18:20 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:19 | test.py:19:11:19:11 | l | |
734
| Taint [externally controlled string] | test.py:18 | test.py:18:9:18:20 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:19 | test.py:19:14:19:14 | l | |
835
| Taint [externally controlled string] | test.py:18 | test.py:18:9:18:20 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:19 | test.py:19:17:19:17 | l | |
Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,33 @@
1-
| test.py:8 | unpacking | a | NO TAINT |
2-
| test.py:8 | unpacking | b | NO TAINT |
3-
| test.py:8 | unpacking | c | NO TAINT |
4-
| test.py:14 | unpacking_to_list | a | NO TAINT |
5-
| test.py:14 | unpacking_to_list | b | NO TAINT |
6-
| test.py:14 | unpacking_to_list | c | NO TAINT |
7-
| test.py:23 | nested | a1 | NO TAINT |
8-
| test.py:23 | nested | a2 | NO TAINT |
9-
| test.py:23 | nested | a3 | NO TAINT |
10-
| test.py:23 | nested | b | NO TAINT |
11-
| test.py:23 | nested | c | NO TAINT |
12-
| test.py:27 | nested | a1 | NO TAINT |
13-
| test.py:27 | nested | a2 | NO TAINT |
14-
| test.py:27 | nested | a3 | NO TAINT |
15-
| test.py:27 | nested | b | NO TAINT |
16-
| test.py:27 | nested | c | NO TAINT |
17-
| test.py:31 | nested | a1 | NO TAINT |
18-
| test.py:31 | nested | a2 | NO TAINT |
19-
| test.py:31 | nested | a3 | NO TAINT |
20-
| test.py:31 | nested | b | NO TAINT |
21-
| test.py:31 | nested | c | NO TAINT |
1+
| test.py:8 | unpacking | a | externally controlled string |
2+
| test.py:8 | unpacking | b | externally controlled string |
3+
| test.py:8 | unpacking | c | externally controlled string |
4+
| test.py:14 | unpacking_to_list | a | externally controlled string |
5+
| test.py:14 | unpacking_to_list | b | externally controlled string |
6+
| test.py:14 | unpacking_to_list | c | externally controlled string |
7+
| test.py:23 | nested | a1 | externally controlled string |
8+
| test.py:23 | nested | a2 | externally controlled string |
9+
| test.py:23 | nested | a3 | externally controlled string |
10+
| test.py:23 | nested | b | [externally controlled string] |
11+
| test.py:23 | nested | c | [externally controlled string] |
12+
| test.py:27 | nested | a1 | externally controlled string |
13+
| test.py:27 | nested | a2 | externally controlled string |
14+
| test.py:27 | nested | a3 | externally controlled string |
15+
| test.py:27 | nested | b | [externally controlled string] |
16+
| test.py:27 | nested | c | [externally controlled string] |
17+
| test.py:31 | nested | a1 | externally controlled string |
18+
| test.py:31 | nested | a2 | externally controlled string |
19+
| test.py:31 | nested | a3 | externally controlled string |
20+
| test.py:31 | nested | b | [externally controlled string] |
21+
| test.py:31 | nested | c | [externally controlled string] |
2222
| test.py:38 | unpack_from_set | a | NO TAINT |
2323
| test.py:38 | unpack_from_set | b | NO TAINT |
2424
| test.py:38 | unpack_from_set | c | NO TAINT |
25-
| test.py:48 | contrived_1 | a | NO TAINT |
26-
| test.py:48 | contrived_1 | b | NO TAINT |
27-
| test.py:48 | contrived_1 | c | NO TAINT |
28-
| test.py:48 | contrived_1 | d | NO TAINT |
29-
| test.py:48 | contrived_1 | e | NO TAINT |
30-
| test.py:48 | contrived_1 | f | NO TAINT |
25+
| test.py:48 | contrived_1 | a | externally controlled string |
26+
| test.py:48 | contrived_1 | b | externally controlled string |
27+
| test.py:48 | contrived_1 | c | externally controlled string |
28+
| test.py:48 | contrived_1 | d | externally controlled string |
29+
| test.py:48 | contrived_1 | e | externally controlled string |
30+
| test.py:48 | contrived_1 | f | externally controlled string |
3131
| test.py:56 | contrived_2 | a | NO TAINT |
3232
| test.py:56 | contrived_2 | b | NO TAINT |
3333
| test.py:56 | contrived_2 | c | NO TAINT |

0 commit comments

Comments
 (0)