Skip to content

Commit 1d1594e

Browse files
Copilothvitved
andauthored
Add taint flow summaries for list.extend and list.insert
Co-authored-by: hvitved <3667920+hvitved@users.noreply.github.com>
1 parent ebf09cb commit 1d1594e

4 files changed

Lines changed: 112 additions & 0 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Added taint flow through `list.extend` and `list.insert`, matching the existing taint flow through `list.append`.

python/ql/lib/semmle/python/frameworks/Stdlib.qll

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4879,6 +4879,70 @@ module StdlibPrivate {
48794879
}
48804880
}
48814881

4882+
/**
4883+
* A flow summary for `list.extend`.
4884+
*
4885+
* See https://docs.python.org/3.10/library/stdtypes.html#typesseq-mutable
4886+
*/
4887+
class ListExtend extends SummarizedCallable::Range {
4888+
ListExtend() { this = "list.extend" }
4889+
4890+
override DataFlow::CallCfgNode getACall() {
4891+
result.(DataFlow::MethodCallNode).calls(_, "extend")
4892+
}
4893+
4894+
override DataFlow::ArgumentNode getACallback() {
4895+
result.(DataFlow::AttrRead).getAttributeName() = "extend"
4896+
}
4897+
4898+
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
4899+
// elements of the newly added iterable are added to this
4900+
(
4901+
input = "Argument[0].ListElement"
4902+
or
4903+
input = "Argument[0].SetElement"
4904+
or
4905+
input = "Argument[0].AnyTupleElement"
4906+
) and
4907+
output = "Argument[self].ListElement" and
4908+
preservesValue = true
4909+
or
4910+
// transfer taint from new iterable to this (TODO: remove in future when taint-handling is more in line with other languages)
4911+
input = "Argument[0]" and
4912+
output = "Argument[self]" and
4913+
preservesValue = false
4914+
}
4915+
}
4916+
4917+
/**
4918+
* A flow summary for `list.insert`.
4919+
*
4920+
* See https://docs.python.org/3.10/library/stdtypes.html#typesseq-mutable
4921+
*/
4922+
class ListInsert extends SummarizedCallable::Range {
4923+
ListInsert() { this = "list.insert" }
4924+
4925+
override DataFlow::CallCfgNode getACall() {
4926+
result.(DataFlow::MethodCallNode).calls(_, "insert")
4927+
}
4928+
4929+
override DataFlow::ArgumentNode getACallback() {
4930+
result.(DataFlow::AttrRead).getAttributeName() = "insert"
4931+
}
4932+
4933+
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
4934+
// newly added element added to this
4935+
input = "Argument[1]" and
4936+
output = "Argument[self].ListElement" and
4937+
preservesValue = true
4938+
or
4939+
// transfer taint from new element to this (TODO: remove in future when taint-handling is more in line with other languages)
4940+
input = "Argument[1]" and
4941+
output = "Argument[self]" and
4942+
preservesValue = false
4943+
}
4944+
}
4945+
48824946
/**
48834947
* A flow summary for `set.add`.
48844948
*

python/ql/test/library-tests/dataflow/coverage/test_builtins.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,16 @@ def test_list_append():
181181
l.append(SOURCE)
182182
SINK(l[1]) # $ flow="SOURCE, l:-1 -> l[1]"
183183

184+
def test_list_extend():
185+
l = [NONSOURCE]
186+
l.extend([SOURCE])
187+
SINK(l[1]) # $ flow="SOURCE, l:-1 -> l[1]"
188+
189+
def test_list_insert():
190+
l = [NONSOURCE]
191+
l.insert(0, SOURCE)
192+
SINK(l[0]) # $ flow="SOURCE, l:-1 -> l[0]"
193+
184194
### Set
185195

186196
def test_set_pop():

python/ql/test/library-tests/dataflow/tainttracking/defaultAdditionalTaintStep/test_collections.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,37 @@ def list_extend():
232232
ensure_not_tainted(my_list)
233233

234234
my_list.extend(tainted_list)
235+
ensure_tainted(my_list) # $ tainted
236+
237+
238+
def list_extend_iteration():
239+
my_list = ["safe"]
240+
tainted_list = [TAINTED_STRING]
241+
242+
ensure_not_tainted(my_list)
243+
244+
my_list.extend(tainted_list)
245+
for x in my_list:
246+
ensure_tainted(x) # $ tainted
247+
248+
249+
def list_insert():
250+
tainted_string = TAINTED_STRING
251+
my_list = ["safe"]
252+
253+
ensure_not_tainted(my_list)
254+
255+
my_list.insert(0, tainted_string)
256+
ensure_tainted(my_list) # $ tainted
257+
258+
259+
def list_iadd():
260+
my_list = ["safe"]
261+
tainted_list = [TAINTED_STRING]
262+
263+
ensure_not_tainted(my_list)
264+
265+
my_list += tainted_list
235266
ensure_tainted(my_list) # $ MISSING: tainted
236267

237268

@@ -308,6 +339,9 @@ def set_add():
308339
list_index_aug_assign()
309340
list_append()
310341
list_extend()
342+
list_extend_iteration()
343+
list_insert()
344+
list_iadd()
311345

312346
dict_update_dict()
313347
dict_update_kv_list()

0 commit comments

Comments
 (0)