mirror of
https://github.com/github/codeql.git
synced 2026-05-03 04:39:29 +02:00
Python: rest_framework.decorators.api_view handling
Had to expose even more things, and had to make the `DjangoRouteHandler` modeling more flexible so I could extend the char-pred in a different file.
This commit is contained in:
@@ -1898,13 +1898,7 @@ module PrivateDjango {
|
||||
*
|
||||
* Most functions take a django HttpRequest as a parameter (but not all).
|
||||
*/
|
||||
private class DjangoRouteHandler extends Function {
|
||||
DjangoRouteHandler() {
|
||||
exists(DjangoRouteSetup route | route.getViewArg() = poorMansFunctionTracker(this))
|
||||
or
|
||||
any(DjangoViewClass vc).getARequestHandler() = this
|
||||
}
|
||||
|
||||
class DjangoRouteHandler extends Function instanceof DjangoRouteHandler::Range {
|
||||
/**
|
||||
* Gets the index of the parameter where the first routed parameter can be passed --
|
||||
* that is, the one just after any possible `self` or HttpRequest parameters.
|
||||
@@ -1924,6 +1918,18 @@ module PrivateDjango {
|
||||
Parameter getRequestParam() { result = this.getArg(this.getRequestParamIndex()) }
|
||||
}
|
||||
|
||||
module DjangoRouteHandler {
|
||||
abstract class Range extends Function { }
|
||||
|
||||
class StandardDjangoRouteHandlers extends Range {
|
||||
StandardDjangoRouteHandlers() {
|
||||
exists(DjangoRouteSetup route | route.getViewArg() = poorMansFunctionTracker(this))
|
||||
or
|
||||
any(DjangoViewClass vc).getARequestHandler() = this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method named `get_redirect_url` on a django view class.
|
||||
*
|
||||
@@ -1945,7 +1951,7 @@ module PrivateDjango {
|
||||
}
|
||||
|
||||
/** A data-flow node that sets up a route on a server, using the django framework. */
|
||||
abstract private class DjangoRouteSetup extends HTTP::Server::RouteSetup::Range, DataFlow::CfgNode {
|
||||
abstract class DjangoRouteSetup extends HTTP::Server::RouteSetup::Range, DataFlow::CfgNode {
|
||||
/** Gets the data-flow node that is used as the argument for the view handler. */
|
||||
abstract DataFlow::Node getViewArg();
|
||||
|
||||
|
||||
@@ -28,6 +28,9 @@ private import semmle.python.frameworks.Stdlib
|
||||
* - https://pypi.org/project/djangorestframework/
|
||||
*/
|
||||
private module RestFramework {
|
||||
// ---------------------------------------------------------------------------
|
||||
// rest_framework.views.APIView handling
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* An `API::Node` representing the `rest_framework.views.APIView` class or any subclass
|
||||
* that has explicitly been modeled in the CodeQL libraries.
|
||||
@@ -65,4 +68,43 @@ private module RestFramework {
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// rest_framework.decorators.api_view handling
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* A function that is a request handler since it is decorated with `rest_framework.decorators.api_view`
|
||||
*/
|
||||
class RestFrameworkFunctionBasedView extends PrivateDjango::DjangoRouteHandler::Range {
|
||||
RestFrameworkFunctionBasedView() {
|
||||
this.getADecorator() =
|
||||
API::moduleImport("rest_framework")
|
||||
.getMember("decorators")
|
||||
.getMember("api_view")
|
||||
.getACall()
|
||||
.asExpr()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensuring that all `RestFrameworkFunctionBasedView` are also marked as a
|
||||
* `HTTP::Server::RequestHandler`. We only need this for the ones that doesn't have a
|
||||
* known route setup.
|
||||
*/
|
||||
class RestFrameworkFunctionBasedViewWithoutKnownRoute extends HTTP::Server::RequestHandler::Range,
|
||||
PrivateDjango::DjangoRouteHandler instanceof RestFrameworkFunctionBasedView {
|
||||
RestFrameworkFunctionBasedViewWithoutKnownRoute() {
|
||||
not exists(PrivateDjango::DjangoRouteSetup setup | setup.getARequestHandler() = this)
|
||||
}
|
||||
|
||||
override Parameter getARoutedParameter() {
|
||||
// Since we don't know the URL pattern, we simply mark all parameters as a routed
|
||||
// parameter. This should give us more RemoteFlowSources but could also lead to
|
||||
// more FPs. If this turns out to be the wrong tradeoff, we can always change our mind.
|
||||
result in [this.getArg(_), this.getArgByName(_)] and
|
||||
not result = any(int i | i < this.getFirstPossibleRoutedParamIndex() | this.getArg(i))
|
||||
}
|
||||
|
||||
override string getFramework() { result = "Django (rest_framework)" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,8 +119,11 @@ urlpatterns = [
|
||||
# framework
|
||||
|
||||
@api_view(["POST"])
|
||||
def function_based_no_route(request: Request, possible_routed_param):
|
||||
ensure_tainted(request, possible_routed_param) # $ MISSING: tainted
|
||||
def function_based_no_route(request: Request, possible_routed_param): # $ requestHandler routedParameter=possible_routed_param
|
||||
ensure_tainted(
|
||||
request, # $ MISSING: tainted
|
||||
possible_routed_param, # $ tainted
|
||||
)
|
||||
|
||||
|
||||
class ClassBasedNoRoute(APIView):
|
||||
|
||||
Reference in New Issue
Block a user