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

Skip to content

Commit 80745e8

Browse files
committed
Python: Model string methods in shared taint tracking library
1 parent a77f118 commit 80745e8

2 files changed

Lines changed: 85 additions & 33 deletions

File tree

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

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeT
2828
concatStep(nodeFrom, nodeTo)
2929
or
3030
subscriptStep(nodeFrom, nodeTo)
31+
or
32+
stringMethods(nodeFrom, nodeTo)
3133
}
3234

3335
/**
@@ -54,3 +56,53 @@ predicate concatStep(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeTo) {
5456
predicate subscriptStep(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeTo) {
5557
nodeTo.getNode().(SubscriptNode).getObject() = nodeFrom.getNode()
5658
}
59+
60+
/**
61+
* Holds if taint can flow from `nodeFrom` to `nodeTo` with a step related to string
62+
* manipulation.
63+
*
64+
* Note that since we cannot easily distinguish when something is a string, this can
65+
* also make taint flow on `<non string>.replace(foo, bar)`.
66+
*/
67+
predicate stringMethods(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeTo) {
68+
// transforming something tainted into a string will make the string tainted
69+
exists(CallNode call | call = nodeTo.getNode() |
70+
call.getFunction().(NameNode).getId() = "str" and
71+
(
72+
nodeFrom.getNode() = call.getArg(0)
73+
or
74+
nodeFrom.getNode() = call.getArgByName("object")
75+
)
76+
)
77+
or
78+
// String methods. Note that this doesn't recognize `meth = "foo".upper; meth()`
79+
exists(CallNode call, string method_name, ControlFlowNode object |
80+
call = nodeTo.getNode() and
81+
object = call.getFunction().(AttrNode).getObject(method_name)
82+
|
83+
nodeFrom.getNode() = object and
84+
method_name in ["capitalize", "casefold", "center", "expandtabs", "format", "format_map",
85+
"join", "ljust", "lstrip", "lower", "replace", "rjust", "rstrip", "strip", "swapcase",
86+
"title", "upper", "zfill", "encode", "decode"]
87+
or
88+
method_name = "replace" and
89+
nodeFrom.getNode() = call.getArg(1)
90+
or
91+
method_name = "format" and
92+
nodeFrom.getNode() = call.getAnArg()
93+
or
94+
// str -> List[str]
95+
// TODO: check if these should be handled differently in regards to content
96+
nodeFrom.getNode() = object and
97+
method_name in ["partition", "rpartition", "rsplit", "split", "splitlines"]
98+
or
99+
// List[str] -> str
100+
// TODO: check if these should be handled differently in regards to content
101+
method_name = "join" and
102+
nodeFrom.getNode() = call.getArg(0)
103+
or
104+
// Mapping[str, Any] -> str
105+
method_name = "format_map" and
106+
nodeFrom.getNode() = call.getArg(0)
107+
)
108+
}

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

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,41 +5,41 @@
55
| test.py:28 | ok | str_operations | ts[Slice] |
66
| test.py:29 | ok | str_operations | ts[Slice] |
77
| test.py:30 | ok | str_operations | ts[0] |
8-
| test.py:31 | fail | str_operations | str(..) |
9-
| test.py:40 | fail | str_methods | ts.capitalize() |
10-
| test.py:41 | fail | str_methods | ts.casefold() |
11-
| test.py:42 | fail | str_methods | ts.center(..) |
12-
| test.py:43 | fail | str_methods | ts.expandtabs() |
13-
| test.py:45 | fail | str_methods | ts.format() |
14-
| test.py:46 | fail | str_methods | "{}".format(..) |
15-
| test.py:47 | fail | str_methods | "{unsafe}".format(..) |
16-
| test.py:49 | fail | str_methods | ts.format_map(..) |
8+
| test.py:31 | ok | str_operations | str(..) |
9+
| test.py:40 | ok | str_methods | ts.capitalize() |
10+
| test.py:41 | ok | str_methods | ts.casefold() |
11+
| test.py:42 | ok | str_methods | ts.center(..) |
12+
| test.py:43 | ok | str_methods | ts.expandtabs() |
13+
| test.py:45 | ok | str_methods | ts.format() |
14+
| test.py:46 | ok | str_methods | "{}".format(..) |
15+
| test.py:47 | ok | str_methods | "{unsafe}".format(..) |
16+
| test.py:49 | ok | str_methods | ts.format_map(..) |
1717
| test.py:50 | fail | str_methods | "{unsafe}".format_map(..) |
18-
| test.py:52 | fail | str_methods | ts.join(..) |
18+
| test.py:52 | ok | str_methods | ts.join(..) |
1919
| test.py:53 | fail | str_methods | "".join(..) |
20-
| test.py:55 | fail | str_methods | ts.ljust(..) |
21-
| test.py:56 | fail | str_methods | ts.lstrip() |
22-
| test.py:57 | fail | str_methods | ts.lower() |
23-
| test.py:59 | fail | str_methods | ts.replace(..) |
24-
| test.py:60 | fail | str_methods | "safe".replace(..) |
25-
| test.py:62 | fail | str_methods | ts.rjust(..) |
26-
| test.py:63 | fail | str_methods | ts.rstrip() |
27-
| test.py:64 | fail | str_methods | ts.strip() |
28-
| test.py:65 | fail | str_methods | ts.swapcase() |
29-
| test.py:66 | fail | str_methods | ts.title() |
30-
| test.py:67 | fail | str_methods | ts.upper() |
31-
| test.py:68 | fail | str_methods | ts.zfill(..) |
32-
| test.py:70 | fail | str_methods | ts.encode(..) |
33-
| test.py:71 | fail | str_methods | ts.encode(..).decode(..) |
34-
| test.py:73 | fail | str_methods | tb.decode(..) |
35-
| test.py:74 | fail | str_methods | tb.decode(..).encode(..) |
36-
| test.py:77 | fail | str_methods | ts.partition(..) |
37-
| test.py:78 | fail | str_methods | ts.rpartition(..) |
38-
| test.py:79 | fail | str_methods | ts.rsplit(..) |
39-
| test.py:80 | fail | str_methods | ts.split(..) |
40-
| test.py:81 | fail | str_methods | ts.splitlines() |
20+
| test.py:55 | ok | str_methods | ts.ljust(..) |
21+
| test.py:56 | ok | str_methods | ts.lstrip() |
22+
| test.py:57 | ok | str_methods | ts.lower() |
23+
| test.py:59 | ok | str_methods | ts.replace(..) |
24+
| test.py:60 | ok | str_methods | "safe".replace(..) |
25+
| test.py:62 | ok | str_methods | ts.rjust(..) |
26+
| test.py:63 | ok | str_methods | ts.rstrip() |
27+
| test.py:64 | ok | str_methods | ts.strip() |
28+
| test.py:65 | ok | str_methods | ts.swapcase() |
29+
| test.py:66 | ok | str_methods | ts.title() |
30+
| test.py:67 | ok | str_methods | ts.upper() |
31+
| test.py:68 | ok | str_methods | ts.zfill(..) |
32+
| test.py:70 | ok | str_methods | ts.encode(..) |
33+
| test.py:71 | ok | str_methods | ts.encode(..).decode(..) |
34+
| test.py:73 | ok | str_methods | tb.decode(..) |
35+
| test.py:74 | ok | str_methods | tb.decode(..).encode(..) |
36+
| test.py:77 | ok | str_methods | ts.partition(..) |
37+
| test.py:78 | ok | str_methods | ts.rpartition(..) |
38+
| test.py:79 | ok | str_methods | ts.rsplit(..) |
39+
| test.py:80 | ok | str_methods | ts.split(..) |
40+
| test.py:81 | ok | str_methods | ts.splitlines() |
4141
| test.py:86 | ok | str_methods | "safe".replace(..) |
42-
| test.py:88 | ok | str_methods | ts.join(..) |
43-
| test.py:89 | ok | str_methods | ts.join(..) |
42+
| test.py:88 | fail | str_methods | ts.join(..) |
43+
| test.py:89 | fail | str_methods | ts.join(..) |
4444
| test.py:99 | fail | non_syntactic | meth() |
4545
| test.py:100 | fail | non_syntactic | _str(..) |

0 commit comments

Comments
 (0)