Skip to content

Commit d4d6f0c

Browse files
committed
Python: Model django request handlers without known route
1 parent 004ff38 commit d4d6f0c

File tree

4 files changed

+21
-2
lines changed

4 files changed

+21
-2
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm,codescanning
2+
* Improved modeling of `django` to recognize request handlers on `View` classes without known route, thereby leading to more sources of remote user input (`RemoteFlowSource`).

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1756,6 +1756,23 @@ private module Django {
17561756
}
17571757
}
17581758

1759+
/** A request handler defined in a django view class, that has no known route. */
1760+
private class DjangoViewClassHandlerWithoutKnownRoute extends HTTP::Server::RequestHandler::Range,
1761+
DjangoRouteHandler {
1762+
DjangoViewClassHandlerWithoutKnownRoute() {
1763+
exists(DjangoViewClassDef vc | vc.getARequestHandler() = this) and
1764+
not exists(DjangoRouteSetup setup | setup.getARequestHandler() = this)
1765+
}
1766+
1767+
override Parameter getARoutedParameter() {
1768+
// Since we don't know the URL pattern, we simply mark all parameters as a routed
1769+
// parameter. This should give us more RemoteFlowSources but could also lead to
1770+
// more FPs. If this turns out to be the wrong tradeoff, we can always change our mind.
1771+
result in [this.getArg(_), this.getArgByName(_)] and
1772+
not result = any(int i | i <= this.getRequestParamIndex() | this.getArg(i))
1773+
}
1774+
}
1775+
17591776
/**
17601777
* Gets the regex that is used by django to find routed parameters when using `django.urls.path`.
17611778
*

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,5 +113,5 @@ class PossiblyNotRouted(View):
113113
# Even if our analysis can't find a route-setup for this class, we should still
114114
# consider it to be a handle incoming HTTP requests
115115

116-
def get(self, request, possibly_not_routed=42): # $ MISSING: requestHandler routedParameter=possibly_not_routed
116+
def get(self, request, possibly_not_routed=42): # $ requestHandler routedParameter=possibly_not_routed
117117
return HttpResponse('PossiblyNotRouted get: {}'.format(possibly_not_routed)) # $HttpResponse

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def post(self, request: HttpRequest): # $ requestHandler
2424

2525

2626
class MyCustomViewBaseClass(View):
27-
def post(self, request: HttpRequest): # $ MISSING: requestHandler
27+
def post(self, request: HttpRequest): # $ requestHandler
2828
return HttpResponse("MyCustomViewBaseClass: POST") # $ HttpResponse
2929

3030

0 commit comments

Comments
 (0)