Skip to content

Commit c57a4df

Browse files
committed
Python: Model taint of self.request on django view class
1 parent 9ca738d commit c57a4df

File tree

3 files changed

+70
-6
lines changed

3 files changed

+70
-6
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
lgtm,codescanning
22
* Improved modeling of `django` to recognize request handlers functions that are decorated (for example with `django.views.decorators.http.require_GET`). This leads to more sources of remote user input (`RemoteFlowSource`), since we correctly identify the first parameter as being passed a django request.
33
* Improved modeling of django View classes. We now consider any class using in a routing setup with `<class>.as_view()` as django view class. This leads to more sources of remote user input (`RemoteFlowSource`), since we correctly identify the first parameter as being passed a django request.
4+
* Improved modeling of `django`, so for View classes we now model `self.request`, `self.args`, and `self.kwargs` as sources of remote user input (`RemoteFlowSource`).

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

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2069,6 +2069,29 @@ private module Django {
20692069
result = this.getAMethod() and
20702070
result.getName() = HTTP::httpVerbLower()
20712071
}
2072+
2073+
/**
2074+
* Gets a reference to instances of this class, originating from a self parameter of
2075+
* a method defined on this class.
2076+
*
2077+
* Note: TODO: This doesn't take MRO into account
2078+
* Note: TODO: This doesn't take staticmethod/classmethod into account
2079+
*/
2080+
private DataFlow::Node getASelfRef(DataFlow::TypeTracker t) {
2081+
t.start() and
2082+
result.(DataFlow::ParameterNode).getParameter() = this.getAMethod().getArg(0)
2083+
or
2084+
exists(DataFlow::TypeTracker t2 | result = this.getASelfRef(t2).track(t2, t))
2085+
}
2086+
2087+
/**
2088+
* Gets a reference to instances of this class, originating from a self parameter of
2089+
* a method defined on this class.
2090+
*
2091+
* Note: TODO: This doesn't take MRO into account
2092+
* Note: TODO: This doesn't take staticmethod/classmethod into account
2093+
*/
2094+
DataFlow::Node getASelfRef() { result = this.getASelfRef(DataFlow::TypeTracker::end()) }
20722095
}
20732096

20742097
/**
@@ -2307,6 +2330,46 @@ private module Django {
23072330
override string getSourceType() { result = "django.http.request.HttpRequest" }
23082331
}
23092332

2333+
/**
2334+
* A read of the `request` attribute on a reference to an instance of a View class,
2335+
* which is the request being processed currently.
2336+
*
2337+
* See https://docs.djangoproject.com/en/3.1/topics/class-based-views/generic-display/#dynamic-filtering
2338+
*/
2339+
private class DjangoViewClassRequestAttributeRead extends django::http::request::HttpRequest::InstanceSource,
2340+
RemoteFlowSource::Range, DataFlow::Node {
2341+
DjangoViewClassRequestAttributeRead() {
2342+
exists(DataFlow::AttrRead read | this = read |
2343+
read.getObject() = any(DjangoViewClass vc).getASelfRef() and
2344+
read.getAttributeName() = "request"
2345+
)
2346+
}
2347+
2348+
override string getSourceType() {
2349+
result = "django.http.request.HttpRequest (attribute on self in View class)"
2350+
}
2351+
}
2352+
2353+
/**
2354+
* A read of the `args` or `kwargs` attribute on a reference to an instance of a View class,
2355+
* which contains the routed parameters captured from the URL route.
2356+
*
2357+
* See https://docs.djangoproject.com/en/3.1/topics/class-based-views/generic-display/#dynamic-filtering
2358+
*/
2359+
private class DjangoViewClassRoutedParamsAttributeRead extends RemoteFlowSource::Range,
2360+
DataFlow::Node {
2361+
DjangoViewClassRoutedParamsAttributeRead() {
2362+
exists(DataFlow::AttrRead read | this = read |
2363+
read.getObject() = any(DjangoViewClass vc).getASelfRef() and
2364+
read.getAttributeName() in ["args", "kwargs"]
2365+
)
2366+
}
2367+
2368+
override string getSourceType() {
2369+
result = "django routed param from attribute on self in View class"
2370+
}
2371+
}
2372+
23102373
private class DjangoHttpRequstAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
23112374
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
23122375
nodeFrom = django::http::request::HttpRequest::instance() and

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,9 @@
8080
| taint_test.py:145 | ok | test_taint | request.get_signed_cookie(..) |
8181
| taint_test.py:149 | fail | test_taint | request.get_signed_cookie(..) |
8282
| taint_test.py:150 | fail | test_taint | request.get_signed_cookie(..) |
83-
| taint_test.py:157 | fail | some_method | self.request |
84-
| taint_test.py:158 | fail | some_method | self.request.GET["key"] |
85-
| taint_test.py:160 | fail | some_method | self.args |
86-
| taint_test.py:161 | fail | some_method | self.args[0] |
87-
| taint_test.py:163 | fail | some_method | self.kwargs |
88-
| taint_test.py:164 | fail | some_method | self.kwargs["key"] |
83+
| taint_test.py:157 | ok | some_method | self.request |
84+
| taint_test.py:158 | ok | some_method | self.request.GET["key"] |
85+
| taint_test.py:160 | ok | some_method | self.args |
86+
| taint_test.py:161 | ok | some_method | self.args[0] |
87+
| taint_test.py:163 | ok | some_method | self.kwargs |
88+
| taint_test.py:164 | ok | some_method | self.kwargs["key"] |

0 commit comments

Comments
 (0)