Skip to content

Commit 44683f2

Browse files
committed
Python: Identify route handlers for django
Not including class based handlers
1 parent c0d71f7 commit 44683f2

File tree

3 files changed

+55
-24
lines changed

3 files changed

+55
-24
lines changed

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

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,42 @@ private module Django {
133133
}
134134
}
135135

136-
private class DjangoPathRouteSetup extends HTTP::Server::RouteSetup::Range, DataFlow::CfgNode {
136+
/**
137+
* Gets a reference to the Function `func`.
138+
*
139+
* The idea is that this function should be used as a route handler when setting up a
140+
* route, but currently it just tracks all functions, since we can't do type-tracking
141+
* backwards yet (TODO).
142+
*/
143+
private DataFlow::Node djangoRouteHandlerFunctionTracker(DataFlow::TypeTracker t, Function func) {
144+
t.start() and
145+
result = DataFlow::exprNode(func.getDefinition())
146+
or
147+
exists(DataFlow::TypeTracker t2 |
148+
result = djangoRouteHandlerFunctionTracker(t2, func).track(t2, t)
149+
)
150+
}
151+
152+
/**
153+
* Gets a reference to the Function `func`.
154+
*
155+
* The idea is that this function should be used as a route handler when setting up a
156+
* route, but currently it just tracks all functions, since we can't do type-tracking
157+
* backwards yet (TODO).
158+
*/
159+
private DataFlow::Node djangoRouteHandlerFunctionTracker(Function func) {
160+
result = djangoRouteHandlerFunctionTracker(DataFlow::TypeTracker::end(), func)
161+
}
162+
163+
/**
164+
* A call to `django.urls.path`.
165+
*
166+
* See https://docs.djangoproject.com/en/3.0/ref/urls/#path
167+
*/
168+
private class DjangoUrlsPathCall extends HTTP::Server::RouteSetup::Range, DataFlow::CfgNode {
137169
override CallNode node;
138170

139-
DjangoPathRouteSetup() { node.getFunction() = django::urls::path().asCfgNode() }
171+
DjangoUrlsPathCall() { node.getFunction() = django::urls::path().asCfgNode() }
140172

141173
override string getUrlPattern() {
142174
exists(StrConst str, ControlFlowNode urlPatternArg |
@@ -148,15 +180,25 @@ private module Django {
148180
)
149181
}
150182

151-
override Function getARouteHandler() { none() }
183+
override Function getARouteHandler() {
184+
exists(DataFlow::Node viewArg |
185+
viewArg.asCfgNode() in [node.getArg(1), node.getArgByName("view")] and
186+
djangoRouteHandlerFunctionTracker(result) = viewArg
187+
)
188+
}
152189

153190
override Parameter getARoutedParameter() { none() }
154191
}
155192

156-
private class DjangoRePathRouteSetup extends HTTP::Server::RouteSetup::Range, DataFlow::CfgNode {
193+
/**
194+
* A call to `django.urls.re_path`.
195+
*
196+
* See https://docs.djangoproject.com/en/3.0/ref/urls/#re_path
197+
*/
198+
private class DjangoUrlsRePathCall extends HTTP::Server::RouteSetup::Range, DataFlow::CfgNode {
157199
override CallNode node;
158200

159-
DjangoRePathRouteSetup() { node.getFunction() = django::urls::re_path().asCfgNode() }
201+
DjangoUrlsRePathCall() { node.getFunction() = django::urls::re_path().asCfgNode() }
160202

161203
override string getUrlPattern() {
162204
exists(StrConst str, ControlFlowNode urlPatternArg |
@@ -168,7 +210,12 @@ private module Django {
168210
)
169211
}
170212

171-
override Function getARouteHandler() { none() }
213+
override Function getARouteHandler() {
214+
exists(DataFlow::Node viewArg |
215+
viewArg.asCfgNode() in [node.getArg(1), node.getArgByName("view")] and
216+
djangoRouteHandlerFunctionTracker(result) = viewArg
217+
)
218+
}
172219

173220
override Parameter getARoutedParameter() { none() }
174221
}

python/ql/test/experimental/library-tests/frameworks/django-v2-v3/ConceptsTest.expected

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,14 @@
1-
| routing_test.py:7:55:7:111 | Comment # $routeHandler $routedParameter=foo $routedParameter=bar | Missing result:routeHandler= |
21
| routing_test.py:7:55:7:111 | Comment # $routeHandler $routedParameter=foo $routedParameter=bar | Missing result:routedParameter=bar |
32
| routing_test.py:7:55:7:111 | Comment # $routeHandler $routedParameter=foo $routedParameter=bar | Missing result:routedParameter=foo |
4-
| routing_test.py:11:31:11:45 | Comment # $routeHandler | Missing result:routeHandler= |
5-
| routing_test.py:15:32:15:46 | Comment # $routeHandler | Missing result:routeHandler= |
6-
| routing_test.py:19:32:19:46 | Comment # $routeHandler | Missing result:routeHandler= |
7-
| routing_test.py:29:42:29:83 | Comment # $routeHandler $routedParameter=untrusted | Missing result:routeHandler= |
8-
| routing_test.py:29:42:29:83 | Comment # $routeHandler $routedParameter=untrusted | Missing result:routedParameter=untrusted |
9-
| routing_test.py:35:41:35:82 | Comment # $routeHandler $routedParameter=untrusted | Missing result:routeHandler= |
10-
| routing_test.py:35:41:35:82 | Comment # $routeHandler $routedParameter=untrusted | Missing result:routedParameter=untrusted |
11-
| routing_test.py:39:45:39:88 | Comment # $routeHandler $routedParameter=page_number | Missing result:routeHandler= |
123
| routing_test.py:39:45:39:88 | Comment # $routeHandler $routedParameter=page_number | Missing result:routedParameter=page_number |
13-
| routing_test.py:44:62:44:120 | Comment # $routeHandler $routedParameter=arg0 $routedParameter=arg1 | Missing result:routeHandler= |
144
| routing_test.py:44:62:44:120 | Comment # $routeHandler $routedParameter=arg0 $routedParameter=arg1 | Missing result:routedParameter=arg0 |
155
| routing_test.py:44:62:44:120 | Comment # $routeHandler $routedParameter=arg0 $routedParameter=arg1 | Missing result:routedParameter=arg1 |
16-
| routing_test.py:65:31:65:45 | Comment # $routeHandler | Missing result:routeHandler= |
17-
| routing_test.py:78:43:78:86 | Comment # $routeHandler $routedParameter=page_number | Missing result:routeHandler= |
186
| routing_test.py:78:43:78:86 | Comment # $routeHandler $routedParameter=page_number | Missing result:routedParameter=page_number |
19-
| routing_test.py:81:43:81:120 | Comment # $routeHandler $routedParameter=foo $routedParameter=bar $routedParameter=baz | Missing result:routeHandler= |
207
| routing_test.py:81:43:81:120 | Comment # $routeHandler $routedParameter=foo $routedParameter=bar $routedParameter=baz | Missing result:routedParameter=bar |
218
| routing_test.py:81:43:81:120 | Comment # $routeHandler $routedParameter=foo $routedParameter=bar $routedParameter=baz | Missing result:routedParameter=baz |
229
| routing_test.py:81:43:81:120 | Comment # $routeHandler $routedParameter=foo $routedParameter=bar $routedParameter=baz | Missing result:routedParameter=foo |
23-
| routing_test.py:84:38:84:94 | Comment # $routeHandler $routedParameter=foo $routedParameter=bar | Missing result:routeHandler= |
2410
| routing_test.py:84:38:84:94 | Comment # $routeHandler $routedParameter=foo $routedParameter=bar | Missing result:routedParameter=bar |
2511
| routing_test.py:84:38:84:94 | Comment # $routeHandler $routedParameter=foo $routedParameter=bar | Missing result:routedParameter=foo |
26-
| routing_test.py:87:37:87:51 | Comment # $routeHandler | Missing result:routeHandler= |
27-
| taint_test.py:6:60:6:116 | Comment # $routeHandler $routedParameter=foo $routedParameter=bar | Missing result:routeHandler= |
2812
| taint_test.py:6:60:6:116 | Comment # $routeHandler $routedParameter=foo $routedParameter=bar | Missing result:routedParameter=bar |
2913
| taint_test.py:6:60:6:116 | Comment # $routeHandler $routedParameter=foo $routedParameter=bar | Missing result:routedParameter=foo |
3014
| testapp/views.py:3:33:3:47 | Comment # $routeHandler | Missing result:routeHandler= |

python/ql/test/experimental/library-tests/frameworks/django-v2-v3/routing_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ class Foo(object):
2626
# Note: since Foo is used as the super type in a class view, it will be able to handle requests.
2727

2828

29-
def post(self, request, untrusted): # $routeHandler $routedParameter=untrusted
29+
def post(self, request, untrusted): # $f-:routeHandler $f-:routedParameter=untrusted
3030
return HttpResponse('Foo post: {}'.format(untrusted))
3131

3232

3333
class ClassView(View, Foo):
3434

35-
def get(self, request, untrusted): # $routeHandler $routedParameter=untrusted
35+
def get(self, request, untrusted): # $f-:routeHandler $f-:routedParameter=untrusted
3636
return HttpResponse('ClassView get: {}'.format(untrusted))
3737

3838

0 commit comments

Comments
 (0)