Python: Add initial rest_framework modeling

I had to make the Django and PrivateDjango modeling non-private :O
This commit is contained in:
Rasmus Wriedt Larsen
2021-10-29 14:18:52 +02:00
parent a64e939d71
commit 222db37c0d
5 changed files with 80 additions and 6 deletions

View File

@@ -155,6 +155,7 @@ Python built-in support
Name, Category
aiohttp.web, Web framework
Django, Web framework
djangorestframework, Web framework
Flask, Web framework
Tornado, Web framework
Twisted, Web framework

View File

@@ -25,6 +25,7 @@ private import semmle.python.frameworks.MySQLdb
private import semmle.python.frameworks.Peewee
private import semmle.python.frameworks.Psycopg2
private import semmle.python.frameworks.PyMySQL
private import semmle.python.frameworks.RestFramework
private import semmle.python.frameworks.Rsa
private import semmle.python.frameworks.RuamelYaml
private import semmle.python.frameworks.Simplejson

View File

@@ -17,10 +17,12 @@ private import semmle.python.frameworks.internal.SelfRefMixin
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
/**
* INTERNAL: Do not use.
*
* Provides models for the `django` PyPI package.
* See https://www.djangoproject.com/.
*/
private module Django {
module Django {
/** Provides models for the `django.views` module */
module Views {
/**
@@ -466,10 +468,12 @@ private module Django {
}
/**
* INTERNAL: Do not use.
*
* Provides models for the `django` PyPI package (that we are not quite ready to publicly expose yet).
* See https://www.djangoproject.com/.
*/
private module PrivateDjango {
module PrivateDjango {
// ---------------------------------------------------------------------------
// django
// ---------------------------------------------------------------------------

View File

@@ -0,0 +1,68 @@
/**
* Provides classes modeling security-relevant aspects of the `djangorestframework` PyPI package
* (imported as `rest_framework`)
*
* See
* - https://www.django-rest-framework.org/
* - https://pypi.org/project/djangorestframework/
*/
private import python
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.dataflow.new.TaintTracking
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
private import semmle.python.frameworks.Django
private import semmle.python.frameworks.Stdlib
/**
* INTERNAL: Do not use.
*
* Provides models for the `djangorestframework` PyPI package
* (imported as `rest_framework`)
*
* See
* - https://www.django-rest-framework.org/
* - https://pypi.org/project/djangorestframework/
*/
private module RestFramework {
/**
* An `API::Node` representing the `rest_framework.views.APIView` class or any subclass
* that has explicitly been modeled in the CodeQL libraries.
*/
private class ModeledApiViewClasses extends Django::Views::View::ModeledSubclass {
ModeledApiViewClasses() {
this = API::moduleImport("rest_framework").getMember("views").getMember("APIView")
// TODO: Need to model all known subclasses
}
}
/**
* A class that has a super-type which is a rest_framework APIView class, therefore also
* becoming a APIView class.
*/
class RestFrameworkApiViewClass extends PrivateDjango::DjangoViewClassFromSuperClass {
RestFrameworkApiViewClass() {
this.getABase() = any(ModeledApiViewClasses c).getASubclass*().getAUse().asExpr()
}
override Function getARequestHandler() {
result = super.getARequestHandler()
or
// TODO: This doesn't handle attribute assignment. Should be OK, but analysis is not as complete as with
// points-to and `.lookup`, which would handle `post = my_post_handler` inside class def
result = this.getAMethod() and
result.getName() in [
// these method names where found by looking through the APIView
// implementation in
// https://github.com/encode/django-rest-framework/blob/master/rest_framework/views.py#L104
"initial", "http_method_not_allowed", "permission_denied", "throttled",
"get_authenticate_header", "perform_content_negotiation", "perform_authentication",
"check_permissions", "check_object_permissions", "check_throttles", "determine_version",
"initialize_request", "finalize_response", "dispatch", "options"
]
}
}
}

View File

@@ -89,9 +89,9 @@ def test_taint(request: Request, routed_param): # $ requestHandler routedParamet
class MyClass(APIView):
def initial(self, request, *args, **kwargs):
def initial(self, request, *args, **kwargs): # $ requestHandler
# this method will be called before processing any request
ensure_tainted(request) # $ MISSING: tainted
ensure_tainted(request) # $ tainted
def get(self, request: Request, routed_param): # $ requestHandler routedParameter=routed_param
ensure_tainted(routed_param) # $ tainted
@@ -124,5 +124,5 @@ def function_based_no_route(request: Request, possible_routed_param):
class ClassBasedNoRoute(APIView):
def get(self, request: Request, possible_routed_param):
ensure_tainted(request, possible_routed_param) # $ MISSING: tainted
def get(self, request: Request, possible_routed_param): # $ requestHandler routedParameter=possible_routed_param
ensure_tainted(request, possible_routed_param) # $ tainted