Skip to content

Python: Model Django REST framework #7016

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Nov 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f1307b7
Python: Add RequestHandler meta query
RasmusWL Nov 2, 2021
9d84315
Python: Set up test for Django REST framework
RasmusWL Oct 27, 2021
9bbf08d
Python: Add simple Django REST framework code
RasmusWL Oct 27, 2021
095f896
Python: Add examples of class/function based views
RasmusWL Oct 27, 2021
75e2555
Python: Add rest_framework taint tests
RasmusWL Oct 28, 2021
a64e939
Python: Add note about `.method`
RasmusWL Oct 29, 2021
222db37
Python: Add initial rest_framework modeling
RasmusWL Oct 29, 2021
57e13c6
Python: `rest_framework.decorators.api_view` handling
RasmusWL Oct 29, 2021
5d77e62
Python: Add basic rest_framework Request modeling
RasmusWL Oct 29, 2021
62d3063
Python: Add rest_framework Request taint modeling
RasmusWL Oct 29, 2021
13815fe
Python: Model known APIView subclasses
RasmusWL Nov 1, 2021
a7e4e5e
Python: Add rest_framework Response modeling
RasmusWL Nov 1, 2021
fd12b14
Python: Add change-note
RasmusWL Nov 1, 2021
5c2734c
Python: Fix experimental Django.qll
RasmusWL Nov 2, 2021
83389be
Python: Add some missing QLDocs
RasmusWL Nov 2, 2021
cb6bcad
Merge branch 'main' into django-rest-framework
RasmusWL Nov 2, 2021
f48ecb1
Python: Apply suggestions from code review
RasmusWL Nov 12, 2021
62e58b5
Python: SubclassFinder: reorder + comment
RasmusWL Nov 12, 2021
5e4b866
Python: Model `rest_framework.exceptions.APIException`
RasmusWL Nov 12, 2021
f7b5332
Python: Remove copy-pasted comment
RasmusWL Nov 12, 2021
de69e4c
Python: Expand on SubclassFinder implementation note
RasmusWL Nov 12, 2021
491f72b
Python: Adjust generated code to be more familiar
RasmusWL Nov 12, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/codeql/support/reusables/frameworks.rst
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ Python built-in support
Name, Category
aiohttp.web, Web framework
Django, Web framework
djangorestframework, Web framework
FastAPI, Web framework
Flask, Web framework
Tornado, Web framework
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
lgtm,codescanning
* Added modeling of HTTP requests and responses when using the Django REST Framework (`djangorestframework` PyPI package), which leads to additional remote flow sources.
1 change: 1 addition & 0 deletions python/ql/lib/semmle/python/Frameworks.qll
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ private import semmle.python.frameworks.Peewee
private import semmle.python.frameworks.Psycopg2
private import semmle.python.frameworks.Pydantic
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
Expand Down
90 changes: 79 additions & 11 deletions python/ql/lib/semmle/python/frameworks/Django.qll
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
/**
Expand Down Expand Up @@ -367,6 +369,52 @@ private module Django {
}
}

/**
* Provides models for the `django.contrib.auth.models.User` class
*
* See https://docs.djangoproject.com/en/3.2/ref/contrib/auth/#user-model.
*/
module User {
/**
* A source of instances of `django.contrib.auth.models.User`, extend this class to model new instances.
*
* This can include instantiations of the class, return values from function
* calls, or a special parameter that will be set when functions are called by an external
* library.
*
* Use the predicate `User::instance()` to get references to instances of `django.contrib.auth.models.User`.
*/
abstract class InstanceSource extends DataFlow::LocalSourceNode { }

/** Gets a reference to an instance of `django.contrib.auth.models.User`. */
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
t.start() and
result instanceof InstanceSource
or
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
}

/** Gets a reference to an instance of `django.contrib.auth.models.User`. */
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }

/**
* Taint propagation for `django.contrib.auth.models.User`.
*/
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
InstanceTaintSteps() { this = "django.contrib.auth.models.User" }

override DataFlow::Node getInstance() { result = instance() }

override string getAttributeName() {
result in ["username", "first_name", "last_name", "email"]
}

override string getMethodName() { none() }

override string getAsyncMethodName() { none() }
}
}

/**
* Provides models for the `django.core.files.uploadedfile.UploadedFile` class
*
Expand Down Expand Up @@ -466,10 +514,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
// ---------------------------------------------------------------------------
Expand All @@ -496,6 +546,7 @@ private module PrivateDjango {
/** Gets a reference to the `django.db.connection` object. */
API::Node connection() { result = db().getMember("connection") }

/** A `django.db.connection` is a PEP249 compliant DB connection. */
class DjangoDbConnection extends PEP249::Connection::InstanceSource {
DjangoDbConnection() { this = connection().getAUse() }
}
Expand Down Expand Up @@ -692,6 +743,7 @@ private module PrivateDjango {

/** Provides models for the `django.conf` module */
module conf {
/** Provides models for the `django.conf.urls` module */
module conf_urls {
// -------------------------------------------------------------------------
// django.conf.urls
Expand Down Expand Up @@ -890,14 +942,15 @@ private module PrivateDjango {
* See https://docs.djangoproject.com/en/3.1/ref/request-response/#django.http.HttpResponse.
*/
module HttpResponse {
/** Gets a reference to the `django.http.response.HttpResponse` class. */
API::Node baseClassRef() {
result = response().getMember("HttpResponse")
or
// Handle `django.http.HttpResponse` alias
result = http().getMember("HttpResponse")
}

/** Gets a reference to the `django.http.response.HttpResponse` class. */
/** Gets a reference to the `django.http.response.HttpResponse` class or any subclass. */
API::Node classRef() { result = baseClassRef().getASubclass*() }

/**
Expand Down Expand Up @@ -1893,14 +1946,11 @@ private module PrivateDjango {
* with the django framework.
*
* Most functions take a django HttpRequest as a parameter (but not all).
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `DjangoRouteHandler::Range` instead.
*/
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.
Expand All @@ -1920,6 +1970,24 @@ private module PrivateDjango {
Parameter getRequestParam() { result = this.getArg(this.getRequestParamIndex()) }
}

/** Provides a class for modeling new django route handlers. */
module DjangoRouteHandler {
/**
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `DjangoRouteHandler` instead.
*/
abstract class Range extends Function { }

/** Route handlers from normal usage of django. */
private 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.
*
Expand All @@ -1941,7 +2009,7 @@ private 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();

Expand Down
Loading