Skip to content

Commit e8f6331

Browse files
committed
Python: Model abspath and realpath (for Path Injection)
1 parent bd5cf80 commit e8f6331

File tree

3 files changed

+69
-1
lines changed

3 files changed

+69
-1
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm,codescanning
2+
* Added modeling of `os.path.abspath` and `os.path.realpath` for Path Injection (py/path-injection).

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

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ private module Stdlib {
9191
* For example, using `attr_name = "join"` will get all uses of `os.path.join`.
9292
*/
9393
private DataFlow::Node path_attr(DataFlow::TypeTracker t, string attr_name) {
94-
attr_name in ["join", "normpath"] and
94+
attr_name in ["join", "normpath", "realpath", "abspath"] and
9595
(
9696
t.start() and
9797
result = DataFlow::importNode("os.path." + attr_name)
@@ -157,6 +157,54 @@ private module Stdlib {
157157
}
158158
}
159159

160+
/**
161+
* A call to `os.path.abspath`.
162+
* See https://docs.python.org/3/library/os.path.html#os.path.abspath
163+
*/
164+
private class OsPathAbspathCall extends Path::PathNormalization::Range, DataFlow::CfgNode {
165+
override CallNode node;
166+
167+
OsPathAbspathCall() { node.getFunction() = os::path::path_attr("abspath").asCfgNode() }
168+
169+
DataFlow::Node getPathArg() {
170+
result.asCfgNode() in [node.getArg(0), node.getArgByName("path")]
171+
}
172+
}
173+
174+
/** An additional taint step for calls to `os.path.abspath` */
175+
private class OsPathAbspathCallAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
176+
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
177+
exists(OsPathAbspathCall call |
178+
nodeTo = call and
179+
nodeFrom = call.getPathArg()
180+
)
181+
}
182+
}
183+
184+
/**
185+
* A call to `os.path.realpath`.
186+
* See https://docs.python.org/3/library/os.path.html#os.path.realpath
187+
*/
188+
private class OsPathRealpathCall extends Path::PathNormalization::Range, DataFlow::CfgNode {
189+
override CallNode node;
190+
191+
OsPathRealpathCall() { node.getFunction() = os::path::path_attr("realpath").asCfgNode() }
192+
193+
DataFlow::Node getPathArg() {
194+
result.asCfgNode() in [node.getArg(0), node.getArgByName("path")]
195+
}
196+
}
197+
198+
/** An additional taint step for calls to `os.path.realpath` */
199+
private class OsPathRealpathCallAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
200+
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
201+
exists(OsPathRealpathCall call |
202+
nodeTo = call and
203+
nodeFrom = call.getPathArg()
204+
)
205+
}
206+
}
207+
160208
/**
161209
* A call to `os.system`.
162210
* See https://docs.python.org/3/library/os.html#os.system

python/ql/test/query-tests/Security/CWE-022-PathInjection/PathInjection.expected

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ edges
55
| path_injection.py:27:16:27:27 | ControlFlowNode for Attribute | path_injection.py:28:13:28:64 | ControlFlowNode for Attribute() |
66
| path_injection.py:28:13:28:64 | ControlFlowNode for Attribute() | path_injection.py:31:14:31:18 | ControlFlowNode for npath |
77
| path_injection.py:37:16:37:27 | ControlFlowNode for Attribute | path_injection.py:38:13:38:64 | ControlFlowNode for Attribute() |
8+
| path_injection.py:46:16:46:27 | ControlFlowNode for Attribute | path_injection.py:47:13:47:64 | ControlFlowNode for Attribute() |
9+
| path_injection.py:47:13:47:64 | ControlFlowNode for Attribute() | path_injection.py:48:14:48:18 | ControlFlowNode for npath |
10+
| path_injection.py:54:16:54:27 | ControlFlowNode for Attribute | path_injection.py:55:13:55:64 | ControlFlowNode for Attribute() |
11+
| path_injection.py:63:16:63:27 | ControlFlowNode for Attribute | path_injection.py:64:13:64:63 | ControlFlowNode for Attribute() |
12+
| path_injection.py:64:13:64:63 | ControlFlowNode for Attribute() | path_injection.py:65:14:65:18 | ControlFlowNode for npath |
13+
| path_injection.py:71:16:71:27 | ControlFlowNode for Attribute | path_injection.py:72:13:72:63 | ControlFlowNode for Attribute() |
814
| test.py:9:12:9:23 | ControlFlowNode for Attribute | test.py:9:12:9:39 | ControlFlowNode for Attribute() |
915
| test.py:9:12:9:23 | ControlFlowNode for Attribute | test.py:9:12:9:39 | ControlFlowNode for Attribute() |
1016
| test.py:9:12:9:39 | ControlFlowNode for Attribute() | test.py:18:9:18:16 | ControlFlowNode for source() |
@@ -49,6 +55,16 @@ nodes
4955
| path_injection.py:31:14:31:18 | ControlFlowNode for npath | semmle.label | ControlFlowNode for npath |
5056
| path_injection.py:37:16:37:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
5157
| path_injection.py:38:13:38:64 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
58+
| path_injection.py:46:16:46:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
59+
| path_injection.py:47:13:47:64 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
60+
| path_injection.py:48:14:48:18 | ControlFlowNode for npath | semmle.label | ControlFlowNode for npath |
61+
| path_injection.py:54:16:54:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
62+
| path_injection.py:55:13:55:64 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
63+
| path_injection.py:63:16:63:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
64+
| path_injection.py:64:13:64:63 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
65+
| path_injection.py:65:14:65:18 | ControlFlowNode for npath | semmle.label | ControlFlowNode for npath |
66+
| path_injection.py:71:16:71:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
67+
| path_injection.py:72:13:72:63 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
5268
| test.py:9:12:9:23 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
5369
| test.py:9:12:9:23 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
5470
| test.py:9:12:9:39 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
@@ -87,6 +103,8 @@ nodes
87103
| path_injection.py:13:14:13:47 | ControlFlowNode for Attribute() | path_injection.py:12:16:12:27 | ControlFlowNode for Attribute | path_injection.py:13:14:13:47 | ControlFlowNode for Attribute() | This path depends on $@. | path_injection.py:12:16:12:27 | ControlFlowNode for Attribute | a user-provided value |
88104
| path_injection.py:21:14:21:18 | ControlFlowNode for npath | path_injection.py:19:16:19:27 | ControlFlowNode for Attribute | path_injection.py:21:14:21:18 | ControlFlowNode for npath | This path depends on $@. | path_injection.py:19:16:19:27 | ControlFlowNode for Attribute | a user-provided value |
89105
| path_injection.py:31:14:31:18 | ControlFlowNode for npath | path_injection.py:27:16:27:27 | ControlFlowNode for Attribute | path_injection.py:31:14:31:18 | ControlFlowNode for npath | This path depends on $@. | path_injection.py:27:16:27:27 | ControlFlowNode for Attribute | a user-provided value |
106+
| path_injection.py:48:14:48:18 | ControlFlowNode for npath | path_injection.py:46:16:46:27 | ControlFlowNode for Attribute | path_injection.py:48:14:48:18 | ControlFlowNode for npath | This path depends on $@. | path_injection.py:46:16:46:27 | ControlFlowNode for Attribute | a user-provided value |
107+
| path_injection.py:65:14:65:18 | ControlFlowNode for npath | path_injection.py:63:16:63:27 | ControlFlowNode for Attribute | path_injection.py:65:14:65:18 | ControlFlowNode for npath | This path depends on $@. | path_injection.py:63:16:63:27 | ControlFlowNode for Attribute | a user-provided value |
90108
| test.py:19:10:19:10 | ControlFlowNode for x | test.py:9:12:9:23 | ControlFlowNode for Attribute | test.py:19:10:19:10 | ControlFlowNode for x | This path depends on $@. | test.py:9:12:9:23 | ControlFlowNode for Attribute | a user-provided value |
91109
| test.py:26:10:26:10 | ControlFlowNode for y | test.py:9:12:9:23 | ControlFlowNode for Attribute | test.py:26:10:26:10 | ControlFlowNode for y | This path depends on $@. | test.py:9:12:9:23 | ControlFlowNode for Attribute | a user-provided value |
92110
| test.py:33:14:33:14 | ControlFlowNode for x | test.py:9:12:9:23 | ControlFlowNode for Attribute | test.py:33:14:33:14 | ControlFlowNode for x | This path depends on $@. | test.py:9:12:9:23 | ControlFlowNode for Attribute | a user-provided value |

0 commit comments

Comments
 (0)