mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
Python: Django: Handle Class-based views
This commit is contained in:
@@ -6,10 +6,10 @@ import semmle.python.web.Http
|
||||
// a FunctionValue, so we can't use `FunctionValue.getArgumentForCall`
|
||||
// https://github.com/django/django/blob/master/django/urls/conf.py#L76
|
||||
abstract class DjangoRoute extends CallNode {
|
||||
FunctionValue getViewFunction() {
|
||||
result = this.getArg(1).pointsTo()
|
||||
DjangoViewHandler getViewHandler() {
|
||||
result = view_handler_from_view_arg(this.getArg(1))
|
||||
or
|
||||
result = this.getArgByName("view").pointsTo()
|
||||
result = view_handler_from_view_arg(this.getArgByName("view"))
|
||||
}
|
||||
|
||||
abstract string getANamedArgument();
|
||||
@@ -21,6 +21,60 @@ abstract class DjangoRoute extends CallNode {
|
||||
abstract int getNumPositionalArguments();
|
||||
}
|
||||
|
||||
/**
|
||||
* For function based views -- also see `DjangoClassBasedViewHandler`
|
||||
* https://docs.djangoproject.com/en/1.11/topics/http/views/
|
||||
* https://docs.djangoproject.com/en/3.0/topics/http/views/
|
||||
*/
|
||||
class DjangoViewHandler extends PythonFunctionValue {
|
||||
|
||||
/** Gets the index of the 'request' argument */
|
||||
int getRequestArgIndex() {
|
||||
result = 0
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class based views
|
||||
* https://docs.djangoproject.com/en/1.11/topics/class-based-views/
|
||||
* https://docs.djangoproject.com/en/3.0/topics/class-based-views/
|
||||
*/
|
||||
private class DjangoViewClass extends ClassValue {
|
||||
DjangoViewClass() {
|
||||
Value::named("django.views.generic.View") = this.getASuperType()
|
||||
or
|
||||
Value::named("django.views.View") = this.getASuperType()
|
||||
}
|
||||
}
|
||||
|
||||
class DjangoClassBasedViewHandler extends DjangoViewHandler {
|
||||
DjangoClassBasedViewHandler() {
|
||||
exists(DjangoViewClass cls |
|
||||
cls.lookup(httpVerbLower()) = this
|
||||
)
|
||||
}
|
||||
|
||||
override int getRequestArgIndex() {
|
||||
// due to `self` being the first parameter
|
||||
result = 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the function that will handle requests when `view_arg` is used as the view argument to a
|
||||
* django route. That is, this methods handles Class-based Views and its `as_view()` function.
|
||||
*/
|
||||
private DjangoViewHandler view_handler_from_view_arg(ControlFlowNode view_arg) {
|
||||
// Function-based view
|
||||
result = view_arg.pointsTo()
|
||||
or
|
||||
// Class-based view
|
||||
exists(ClassValue cls |
|
||||
cls = view_arg.(CallNode).getFunction().(AttrNode).getObject("as_view").pointsTo() and
|
||||
result = cls.lookup(httpVerbLower())
|
||||
)
|
||||
}
|
||||
|
||||
// We need this "dummy" class, since otherwise the regex argument would not be considered
|
||||
// a regex (RegexString is abstract)
|
||||
class DjangoRouteRegex extends RegexString {
|
||||
|
||||
@@ -39,60 +39,35 @@ class DjangoQueryDict extends TaintKind {
|
||||
}
|
||||
}
|
||||
|
||||
abstract class DjangoRequestSource extends HttpRequestTaintSource {
|
||||
/** A Django request parameter */
|
||||
class DjangoRequestSource extends HttpRequestTaintSource {
|
||||
DjangoRequestSource() {
|
||||
exists(DjangoRoute route, DjangoViewHandler view, int request_arg_index |
|
||||
route.getViewHandler() = view and
|
||||
request_arg_index = view.getRequestArgIndex() and
|
||||
this = view.getScope().getArg(request_arg_index).asName().getAFlowNode()
|
||||
)
|
||||
}
|
||||
|
||||
override string toString() { result = "Django request source" }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoRequest }
|
||||
}
|
||||
|
||||
/**
|
||||
* Function based views
|
||||
* https://docs.djangoproject.com/en/1.11/topics/http/views/
|
||||
* https://docs.djangoproject.com/en/3.0/topics/http/views/
|
||||
*/
|
||||
private class DjangoFunctionBasedViewRequestArgument extends DjangoRequestSource {
|
||||
DjangoFunctionBasedViewRequestArgument() {
|
||||
exists(DjangoRoute route, FunctionValue view |
|
||||
route.getViewFunction() = view and
|
||||
this = view.getScope().getArg(0).asName().getAFlowNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class based views
|
||||
* https://docs.djangoproject.com/en/1.11/topics/class-based-views/
|
||||
* https://docs.djangoproject.com/en/3.0/topics/class-based-views/
|
||||
*/
|
||||
private class DjangoView extends ClassValue {
|
||||
DjangoView() {
|
||||
Value::named("django.views.generic.View") = this.getASuperType()
|
||||
or
|
||||
Value::named("django.views.View") = this.getASuperType()
|
||||
}
|
||||
}
|
||||
|
||||
private FunctionValue djangoViewHttpMethod() {
|
||||
exists(DjangoView view | view.lookup(httpVerbLower()) = result)
|
||||
}
|
||||
|
||||
class DjangoClassBasedViewRequestArgument extends DjangoRequestSource {
|
||||
DjangoClassBasedViewRequestArgument() {
|
||||
this = djangoViewHttpMethod().getScope().getArg(1).asName().getAFlowNode()
|
||||
}
|
||||
}
|
||||
|
||||
/** An argument specified in a url routing table */
|
||||
class DjangoRequestParameter extends HttpRequestTaintSource {
|
||||
DjangoRequestParameter() {
|
||||
exists(DjangoRoute route, Function f |
|
||||
f = route.getViewFunction().getScope() |
|
||||
exists(DjangoRoute route, Function f, DjangoViewHandler view, int request_arg_index |
|
||||
route.getViewHandler() = view and
|
||||
request_arg_index = view.getRequestArgIndex() and
|
||||
f = view.getScope()
|
||||
|
|
||||
this.(ControlFlowNode).getNode() = f.getArgByName(route.getANamedArgument())
|
||||
or
|
||||
exists(int i | i >= 0 |
|
||||
i < route.getNumPositionalArguments() and
|
||||
// +1 because first argument is always the request
|
||||
this.(ControlFlowNode).getNode() = f.getArg(i+1)
|
||||
this.(ControlFlowNode).getNode() = f.getArg(request_arg_index + 1 + i)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user