Skip to content

Commit 0542c3b

Browse files
committed
Python: Model os.path.join and add taint-step
1 parent efa2484 commit 0542c3b

File tree

3 files changed

+51
-2
lines changed

3 files changed

+51
-2
lines changed

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,6 @@ predicate stringManipulation(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeT
130130
// f-strings
131131
nodeTo.asExpr().(Fstring).getAValue() = nodeFrom.asExpr()
132132
// TODO: Handle encode/decode from base64/quopri
133-
// TODO: Handle os.path.join
134133
// TODO: Handle functions in https://docs.python.org/3/library/binascii.html
135134
}
136135

python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55

66
private import python
77
private import experimental.dataflow.DataFlow
8+
private import experimental.dataflow.TaintTracking
89
private import experimental.dataflow.RemoteFlowSources
910
private import experimental.semmle.python.Concepts
1011

12+
/** Provides models for the Python standard library. */
1113
private module Stdlib {
1214
/** Gets a reference to the `os` module. */
1315
DataFlow::Node os(DataFlow::TypeTracker t) {
@@ -20,6 +22,7 @@ private module Stdlib {
2022
/** Gets a reference to the `os` module. */
2123
DataFlow::Node os() { result = os(DataFlow::TypeTracker::end()) }
2224

25+
/** Provides models for the `os` module. */
2326
module os {
2427
/** Gets a reference to the `os.system` function. */
2528
DataFlow::Node system(DataFlow::TypeTracker t) {
@@ -48,6 +51,41 @@ private module Stdlib {
4851

4952
/** Gets a reference to the `os.popen` function. */
5053
DataFlow::Node popen() { result = os::popen(DataFlow::TypeTracker::end()) }
54+
55+
/** Gets a reference to the `os.path` module. */
56+
private DataFlow::Node path(DataFlow::TypeTracker t) {
57+
t.start() and
58+
(
59+
result = DataFlow::importMember("os", "path")
60+
or
61+
result = DataFlow::importModule("os.path")
62+
)
63+
or
64+
t.startInAttr("path") and
65+
result = os()
66+
or
67+
exists(DataFlow::TypeTracker t2 | result = path(t2).track(t2, t))
68+
}
69+
70+
/** Gets a reference to the `os.path` module. */
71+
DataFlow::Node path() { result = path(DataFlow::TypeTracker::end()) }
72+
73+
/** Provides models for the `os.path` module */
74+
module path {
75+
/** Gets a reference to the `os.path.join` function. */
76+
private DataFlow::Node join(DataFlow::TypeTracker t) {
77+
t.start() and
78+
result = DataFlow::importMember("os.path", "join")
79+
or
80+
t.startInAttr("join") and
81+
result = os::path()
82+
or
83+
exists(DataFlow::TypeTracker t2 | result = join(t2).track(t2, t))
84+
}
85+
86+
/** Gets a reference to the `os.join` module. */
87+
DataFlow::Node join() { result = join(DataFlow::TypeTracker::end()) }
88+
}
5189
}
5290

5391
/**
@@ -73,4 +111,16 @@ private module Stdlib {
73111
result.asCfgNode() = this.asCfgNode().(CallNode).getArg(0)
74112
}
75113
}
114+
115+
/** An additional taint step for calls to `os.path.join` */
116+
private class OsPathJoinCallAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
117+
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
118+
exists(CallNode call |
119+
nodeTo.asCfgNode() = call and
120+
call.getFunction() = os::path::join().asCfgNode() and
121+
call.getAnArg() = nodeFrom.asCfgNode()
122+
)
123+
// TODO: Handle pathlib (like we do for os.path.join)
124+
}
125+
}
76126
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@
139139
| test_string.py:149 | fail | binary_decode_encode | quopri.decodestring(..) |
140140
| test_string.py:158 | ok | test_os_path_join | os.path.join(..) |
141141
| test_string.py:159 | ok | test_os_path_join | os.path.join(..) |
142-
| test_string.py:160 | fail | test_os_path_join | os.path.join(..) |
142+
| test_string.py:160 | ok | test_os_path_join | os.path.join(..) |
143143
| test_unpacking.py:16 | ok | unpacking | a |
144144
| test_unpacking.py:16 | ok | unpacking | b |
145145
| test_unpacking.py:16 | ok | unpacking | c |

0 commit comments

Comments
 (0)