mirror of
https://github.com/github/codeql.git
synced 2025-12-24 04:36:35 +01:00
2833 lines
119 KiB
Plaintext
2833 lines
119 KiB
Plaintext
/**
|
|
* Provides classes modeling security-relevant aspects of the `django` PyPI package.
|
|
* See https://www.djangoproject.com/.
|
|
*/
|
|
|
|
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.PEP249
|
|
private import semmle.python.frameworks.Stdlib
|
|
private import semmle.python.regex
|
|
private import semmle.python.frameworks.internal.PoorMansFunctionResolution
|
|
private import semmle.python.frameworks.internal.SelfRefMixin
|
|
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
|
|
private import semmle.python.security.dataflow.UrlRedirectCustomizations
|
|
|
|
/**
|
|
* INTERNAL: Do not use.
|
|
*
|
|
* Provides models for the `django` PyPI package.
|
|
* See https://www.djangoproject.com/.
|
|
*/
|
|
module Django {
|
|
/** Provides models for the `django.views` module */
|
|
module Views {
|
|
/**
|
|
* Provides models for the `django.views.generic.View` class and subclasses.
|
|
*
|
|
* See
|
|
* - https://docs.djangoproject.com/en/3.1/topics/class-based-views/
|
|
* - https://docs.djangoproject.com/en/3.1/ref/class-based-views/
|
|
*/
|
|
module View {
|
|
/**
|
|
* An `API::Node` representing the `django.views.generic.View` class or any subclass
|
|
* that has explicitly been modeled in the CodeQL libraries.
|
|
*/
|
|
abstract class ModeledSubclass extends API::Node {
|
|
override string toString() { result = this.(API::Node).toString() }
|
|
}
|
|
|
|
/** A Django view subclass in the `django` package. */
|
|
private class DjangoViewSubclassesInDjango extends ModeledSubclass {
|
|
DjangoViewSubclassesInDjango() {
|
|
exists(string moduleName, string className |
|
|
// canonical definition
|
|
this =
|
|
API::moduleImport("django")
|
|
.getMember("views")
|
|
.getMember("generic")
|
|
.getMember(moduleName)
|
|
.getMember(className)
|
|
or
|
|
// aliases from `django.views.generic`
|
|
this =
|
|
API::moduleImport("django")
|
|
.getMember("views")
|
|
.getMember("generic")
|
|
.getMember(className)
|
|
|
|
|
moduleName = "base" and
|
|
className in ["RedirectView", "TemplateView", "View"]
|
|
or
|
|
moduleName = "dates" and
|
|
className in [
|
|
"ArchiveIndexView", "DateDetailView", "DayArchiveView", "MonthArchiveView",
|
|
"TodayArchiveView", "WeekArchiveView", "YearArchiveView"
|
|
]
|
|
or
|
|
moduleName = "detail" and
|
|
className = "DetailView"
|
|
or
|
|
moduleName = "edit" and
|
|
className in ["CreateView", "DeleteView", "FormView", "UpdateView"]
|
|
or
|
|
moduleName = "list" and
|
|
className = "ListView"
|
|
)
|
|
or
|
|
// `django.views.View` alias
|
|
this = API::moduleImport("django").getMember("views").getMember("View")
|
|
}
|
|
}
|
|
|
|
/** Gets a reference to the `django.views.generic.View` class or any subclass. */
|
|
API::Node subclassRef() { result = any(ModeledSubclass subclass).getASubclass*() }
|
|
}
|
|
}
|
|
|
|
/** Provides models for django forms (defined in the `django.forms` module) */
|
|
module Forms {
|
|
/**
|
|
* Provides models for the `django.forms.forms.BaseForm` class and subclasses. This
|
|
* is usually used by the `django.forms.forms.Form` class, which is also available
|
|
* under the more commonly used alias `django.forms.Form`.
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.1/ref/forms/api/
|
|
*/
|
|
module Form {
|
|
/**
|
|
* An `API::Node` representing the `django.forms.forms.BaseForm` class or any subclass
|
|
* that has explicitly been modeled in the CodeQL libraries.
|
|
*/
|
|
abstract class ModeledSubclass extends API::Node {
|
|
override string toString() { result = this.(API::Node).toString() }
|
|
}
|
|
|
|
/** A Django form subclass in the `django` package. */
|
|
private class DjangoFormSubclassesInDjango extends ModeledSubclass {
|
|
DjangoFormSubclassesInDjango() {
|
|
// canonical definition
|
|
this =
|
|
API::moduleImport("django")
|
|
.getMember("forms")
|
|
.getMember("forms")
|
|
.getMember(["BaseForm", "Form"])
|
|
or
|
|
this =
|
|
API::moduleImport("django")
|
|
.getMember("forms")
|
|
.getMember("models")
|
|
.getMember(["BaseModelForm", "ModelForm"])
|
|
or
|
|
// aliases from `django.forms`
|
|
this =
|
|
API::moduleImport("django")
|
|
.getMember("forms")
|
|
.getMember(["BaseForm", "Form", "BaseModelForm", "ModelForm"])
|
|
or
|
|
// other Form subclasses defined in Django
|
|
this =
|
|
API::moduleImport("django")
|
|
.getMember("contrib")
|
|
.getMember("admin")
|
|
.getMember("forms")
|
|
.getMember(["AdminAuthenticationForm", "AdminPasswordChangeForm"])
|
|
or
|
|
this =
|
|
API::moduleImport("django")
|
|
.getMember("contrib")
|
|
.getMember("admin")
|
|
.getMember("helpers")
|
|
.getMember("ActionForm")
|
|
or
|
|
this =
|
|
API::moduleImport("django")
|
|
.getMember("contrib")
|
|
.getMember("admin")
|
|
.getMember("views")
|
|
.getMember("main")
|
|
.getMember("ChangeListSearchForm")
|
|
or
|
|
this =
|
|
API::moduleImport("django")
|
|
.getMember("contrib")
|
|
.getMember("auth")
|
|
.getMember("forms")
|
|
.getMember([
|
|
"PasswordResetForm", "UserChangeForm", "SetPasswordForm",
|
|
"AdminPasswordChangeForm", "PasswordChangeForm", "AuthenticationForm",
|
|
"UserCreationForm"
|
|
])
|
|
or
|
|
this =
|
|
API::moduleImport("django")
|
|
.getMember("contrib")
|
|
.getMember("flatpages")
|
|
.getMember("forms")
|
|
.getMember("FlatpageForm")
|
|
or
|
|
this =
|
|
API::moduleImport("django")
|
|
.getMember("forms")
|
|
.getMember("formsets")
|
|
.getMember("ManagementForm")
|
|
or
|
|
this =
|
|
API::moduleImport("django")
|
|
.getMember("forms")
|
|
.getMember("models")
|
|
.getMember(["ModelForm", "BaseModelForm"])
|
|
}
|
|
}
|
|
|
|
/** Gets a reference to the `django.forms.forms.BaseForm` class or any subclass. */
|
|
API::Node subclassRef() { result = any(ModeledSubclass subclass).getASubclass*() }
|
|
}
|
|
|
|
/**
|
|
* Provides models for the `django.forms.fields.Field` class and subclasses. This is
|
|
* also available under the more commonly used alias `django.forms.Field`.
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.1/ref/forms/fields/
|
|
*/
|
|
module Field {
|
|
/**
|
|
* An `API::Node` representing the `django.forms.fields.Field` class or any subclass
|
|
* that has explicitly been modeled in the CodeQL libraries.
|
|
*/
|
|
abstract class ModeledSubclass extends API::Node {
|
|
override string toString() { result = this.(API::Node).toString() }
|
|
}
|
|
|
|
/** A Django field subclass in the `django` package. */
|
|
private class DjangoFieldSubclassesInDjango extends ModeledSubclass {
|
|
DjangoFieldSubclassesInDjango() {
|
|
exists(string moduleName, string className |
|
|
// canonical definition
|
|
this =
|
|
API::moduleImport("django")
|
|
.getMember("forms")
|
|
.getMember(moduleName)
|
|
.getMember(className)
|
|
or
|
|
// aliases from `django.forms`
|
|
this = API::moduleImport("django").getMember("forms").getMember(className)
|
|
|
|
|
moduleName = "fields" and
|
|
className in [
|
|
"Field",
|
|
// Known subclasses
|
|
"BooleanField", "IntegerField", "CharField", "SlugField", "DateTimeField",
|
|
"EmailField", "DateField", "TimeField", "DurationField", "DecimalField",
|
|
"FloatField", "GenericIPAddressField", "UUIDField", "JSONField", "FilePathField",
|
|
"NullBooleanField", "URLField", "TypedChoiceField", "FileField", "ImageField",
|
|
"RegexField", "ChoiceField", "MultipleChoiceField", "ComboField", "MultiValueField",
|
|
"SplitDateTimeField", "TypedMultipleChoiceField", "BaseTemporalField"
|
|
]
|
|
or
|
|
// Known subclasses from `django.forms.models`
|
|
moduleName = "models" and
|
|
className in ["ModelChoiceField", "ModelMultipleChoiceField", "InlineForeignKeyField"]
|
|
)
|
|
or
|
|
// other Field subclasses defined in Django
|
|
this =
|
|
API::moduleImport("django")
|
|
.getMember("contrib")
|
|
.getMember("auth")
|
|
.getMember("forms")
|
|
.getMember(["ReadOnlyPasswordHashField", "UsernameField"])
|
|
or
|
|
this =
|
|
API::moduleImport("django")
|
|
.getMember("contrib")
|
|
.getMember("gis")
|
|
.getMember("forms")
|
|
.getMember("fields")
|
|
.getMember([
|
|
"GeometryCollectionField", "GeometryField", "LineStringField",
|
|
"MultiLineStringField", "MultiPointField", "MultiPolygonField", "PointField",
|
|
"PolygonField"
|
|
])
|
|
or
|
|
this =
|
|
API::moduleImport("django")
|
|
.getMember("contrib")
|
|
.getMember("postgres")
|
|
.getMember("forms")
|
|
.getMember("array")
|
|
.getMember(["SimpleArrayField", "SplitArrayField"])
|
|
or
|
|
this =
|
|
API::moduleImport("django")
|
|
.getMember("contrib")
|
|
.getMember("postgres")
|
|
.getMember("forms")
|
|
.getMember("hstore")
|
|
.getMember("HStoreField")
|
|
or
|
|
this =
|
|
API::moduleImport("django")
|
|
.getMember("contrib")
|
|
.getMember("postgres")
|
|
.getMember("forms")
|
|
.getMember("ranges")
|
|
.getMember([
|
|
"BaseRangeField", "DateRangeField", "DateTimeRangeField", "DecimalRangeField",
|
|
"IntegerRangeField"
|
|
])
|
|
or
|
|
this =
|
|
API::moduleImport("django")
|
|
.getMember("forms")
|
|
.getMember("models")
|
|
.getMember(["InlineForeignKeyField", "ModelChoiceField", "ModelMultipleChoiceField"])
|
|
}
|
|
}
|
|
|
|
/** Gets a reference to the `django.forms.fields.Field` class or any subclass. */
|
|
API::Node subclassRef() { result = any(ModeledSubclass subclass).getASubclass*() }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Provides models for the `django.utils.datastructures.MultiValueDict` class
|
|
*
|
|
* See
|
|
* - https://docs.djangoproject.com/en/3.0/ref/request-response/#django.http.QueryDict (subclass that has proper docs)
|
|
* - https://www.kite.com/python/docs/django.utils.datastructures.MultiValueDict
|
|
*/
|
|
module MultiValueDict {
|
|
/** Gets a reference to the `django.utils.datastructures.MultiValueDict` class. */
|
|
private API::Node classRef() {
|
|
result =
|
|
API::moduleImport("django")
|
|
.getMember("utils")
|
|
.getMember("datastructures")
|
|
.getMember("MultiValueDict")
|
|
}
|
|
|
|
/**
|
|
* A source of instances of `django.utils.datastructures.MultiValueDict`, 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 `MultiValueDict::instance()` to get references to instances of `django.utils.datastructures.MultiValueDict`.
|
|
*/
|
|
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
|
|
|
|
/** A direct instantiation of `django.utils.datastructures.MultiValueDict`. */
|
|
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
|
|
ClassInstantiation() { this = classRef().getACall() }
|
|
}
|
|
|
|
/** Gets a reference to an instance of `django.utils.datastructures.MultiValueDict`. */
|
|
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.utils.datastructures.MultiValueDict`. */
|
|
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
|
|
|
/**
|
|
* Taint propagation for `django.utils.datastructures.MultiValueDict`.
|
|
*/
|
|
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
|
InstanceTaintSteps() { this = "django.utils.datastructures.MultiValueDict" }
|
|
|
|
override DataFlow::Node getInstance() { result = instance() }
|
|
|
|
override string getAttributeName() { none() }
|
|
|
|
override string getMethodName() {
|
|
result in ["getlist", "lists", "popitem", "dict", "urlencode"]
|
|
}
|
|
|
|
override string getAsyncMethodName() { none() }
|
|
}
|
|
|
|
/**
|
|
* Extra taint propagation for `django.utils.datastructures.MultiValueDict`, not covered by `InstanceTaintSteps`.
|
|
*/
|
|
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
|
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
|
// class instantiation
|
|
exists(ClassInstantiation call |
|
|
nodeFrom = call.getArg(0) and
|
|
nodeTo = call
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.0/ref/files/uploads/#django.core.files.uploadedfile.UploadedFile.
|
|
*/
|
|
module UploadedFile {
|
|
/**
|
|
* A source of instances of `django.core.files.uploadedfile.UploadedFile`, 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 `UploadedFile::instance()` to get references to instances of `django.core.files.uploadedfile.UploadedFile`.
|
|
*/
|
|
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
|
|
|
|
/** Gets a reference to an instance of `django.core.files.uploadedfile.UploadedFile`. */
|
|
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.core.files.uploadedfile.UploadedFile`. */
|
|
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
|
|
|
/**
|
|
* Taint propagation for `django.core.files.uploadedfile.UploadedFile`.
|
|
*/
|
|
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
|
InstanceTaintSteps() { this = "django.core.files.uploadedfile.UploadedFile" }
|
|
|
|
override DataFlow::Node getInstance() { result = instance() }
|
|
|
|
override string getAttributeName() {
|
|
result in [
|
|
"content_type", "content_type_extra", "content_type_extra", "charset", "name", "file"
|
|
]
|
|
}
|
|
|
|
override string getMethodName() { none() }
|
|
|
|
override string getAsyncMethodName() { none() }
|
|
}
|
|
|
|
/** A file-like object instance that originates from a `UploadedFile`. */
|
|
class UploadedFileFileLikeInstances extends Stdlib::FileLikeObject::InstanceSource {
|
|
UploadedFileFileLikeInstances() {
|
|
// in the bottom of
|
|
// https://docs.djangoproject.com/en/4.0/ref/files/file/#django.core.files.File
|
|
// it's mentioned that the File object itself has proxy methods for
|
|
// `read`/`write`/... that forwards to the underlying file object.
|
|
this = instance()
|
|
or
|
|
this.(DataFlow::AttrRead).accesses(instance(), "file")
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Provides models for the `django.urls.ResolverMatch` class
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.0/ref/urlresolvers/#django.urls.ResolverMatch.
|
|
*/
|
|
module ResolverMatch {
|
|
/**
|
|
* A source of instances of `django.urls.ResolverMatch`, 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 `ResolverMatch::instance()` to get references to instances of `django.urls.ResolverMatch`.
|
|
*/
|
|
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
|
|
|
|
/** Gets a reference to an instance of `django.urls.ResolverMatch`. */
|
|
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.urls.ResolverMatch`. */
|
|
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
|
|
|
/**
|
|
* Taint propagation for `django.urls.ResolverMatch`.
|
|
*/
|
|
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
|
InstanceTaintSteps() { this = "django.urls.ResolverMatch" }
|
|
|
|
override DataFlow::Node getInstance() { result = instance() }
|
|
|
|
override string getAttributeName() { result in ["args", "kwargs"] }
|
|
|
|
override string getMethodName() { none() }
|
|
|
|
override string getAsyncMethodName() { none() }
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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/.
|
|
*/
|
|
module PrivateDjango {
|
|
// ---------------------------------------------------------------------------
|
|
// django
|
|
// ---------------------------------------------------------------------------
|
|
/** Gets a reference to the `django` module. */
|
|
API::Node django() { result = API::moduleImport("django") }
|
|
|
|
/** Provides models for the `django` module. */
|
|
module DjangoImpl {
|
|
// -------------------------------------------------------------------------
|
|
// django.db
|
|
// -------------------------------------------------------------------------
|
|
/** Gets a reference to the `django.db` module. */
|
|
API::Node db() { result = django().getMember("db") }
|
|
|
|
/**
|
|
* `django.db` implements PEP249, providing ways to execute SQL statements against a database.
|
|
*/
|
|
private class DjangoDb extends PEP249::PEP249ModuleApiNode {
|
|
DjangoDb() { this = API::moduleImport("django").getMember("db") }
|
|
}
|
|
|
|
/** Provides models for the `django.db` module. */
|
|
module DB {
|
|
/** 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::DatabaseConnection {
|
|
DjangoDbConnection() { this = connection() }
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// django.db.models
|
|
// -------------------------------------------------------------------------
|
|
/** Gets a reference to the `django.db.models` module. */
|
|
API::Node models() { result = db().getMember("models") }
|
|
|
|
/** Provides models for the `django.db.models` module. */
|
|
module Models {
|
|
/**
|
|
* Provides models for the `django.db.models.Model` class and subclasses.
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.1/topics/db/models/.
|
|
*/
|
|
module Model {
|
|
/** Gets a reference to the `django.db.models.Model` class or any subclass. */
|
|
API::Node subclassRef() {
|
|
result =
|
|
API::moduleImport("django")
|
|
.getMember("db")
|
|
.getMember("models")
|
|
.getMember("Model")
|
|
.getASubclass*()
|
|
or
|
|
result =
|
|
API::moduleImport("django")
|
|
.getMember("db")
|
|
.getMember("models")
|
|
.getMember("base")
|
|
.getMember("Model")
|
|
.getASubclass*()
|
|
or
|
|
result =
|
|
API::moduleImport("polymorphic")
|
|
.getMember("models")
|
|
.getMember("PolymorphicModel")
|
|
.getASubclass*()
|
|
}
|
|
|
|
/**
|
|
* A source of instances of `django.db.models.Model` class or any subclass, 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 `Model::instance()` to get references to instances of `django.db.models.Model` class or any subclass.
|
|
*/
|
|
abstract class InstanceSource extends DataFlow::LocalSourceNode {
|
|
/** Gets the model class that this is an instance source of. */
|
|
abstract API::Node getModelClass();
|
|
|
|
/** Holds if this instance-source is fetching data from the DB. */
|
|
abstract predicate isDbFetch();
|
|
}
|
|
|
|
/** A direct instantiation of `django.db.models.Model` class or any subclass. */
|
|
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
|
|
API::Node modelClass;
|
|
|
|
ClassInstantiation() {
|
|
modelClass = subclassRef() and
|
|
this = modelClass.getACall()
|
|
}
|
|
|
|
override API::Node getModelClass() { result = modelClass }
|
|
|
|
override predicate isDbFetch() { none() }
|
|
}
|
|
|
|
/** A method call on a query-set or manager that returns an instance of a django model. */
|
|
private class QuerySetMethod extends InstanceSource, DataFlow::CallCfgNode {
|
|
API::Node modelClass;
|
|
string methodName;
|
|
|
|
QuerySetMethod() {
|
|
modelClass = subclassRef() and
|
|
methodName in ["get", "create", "earliest", "latest", "first", "last"] and
|
|
this = [manager(modelClass), querySet(modelClass)].getMember(methodName).getACall()
|
|
}
|
|
|
|
override API::Node getModelClass() { result = modelClass }
|
|
|
|
override predicate isDbFetch() { not methodName = "create" }
|
|
}
|
|
|
|
/**
|
|
* A method call on a query-set or manager that returns a collection
|
|
* containing instances of a django model.
|
|
*/
|
|
class QuerySetMethodInstanceCollection extends DataFlow::CallCfgNode {
|
|
API::Node modelClass;
|
|
string methodName;
|
|
|
|
QuerySetMethodInstanceCollection() {
|
|
modelClass = subclassRef() and
|
|
this = querySetReturningMethod(modelClass, methodName).getACall() and
|
|
not methodName in ["none", "datetimes", "dates", "values", "values_list"]
|
|
or
|
|
// TODO: When we have flow-summaries, we should be able to model `values` and `values_list`
|
|
// Potentially by doing `synthetic ===store of list element==> <Model>.objects`, and then
|
|
// `.all()` just keeps that content, and `.first()` will do a read step (of the list element).
|
|
//
|
|
// So for `Model.objects.filter().exclude().first()` we would have
|
|
// 1: <Synthetic node for Model> ===store of list element==> Model.objects
|
|
// 2: Model.objects ==> Model.objects.filter()
|
|
// 3: Model.objects.filter() ==> Model.objects.filter().exclude()
|
|
// 4: Model.objects.filter().exclude() ===read of list element==> Model.objects.filter().exclude().first()
|
|
//
|
|
// This also means that `.none()` could clear contents. Right now we
|
|
// think that the result of `Model.objects.none().all()` can contain
|
|
// Model objects, but it will be empty due to the `.none()` part. Not
|
|
// that this is important, since no-one would need to write
|
|
// `.none().all()` code anyway, but it would be cool to be able to model it properly :D
|
|
//
|
|
//
|
|
// The big benefit is for how we could then model `values`/`values_list`. For example,
|
|
// `Model.objects.value_list(name, description)` would result in (for the attribute description)
|
|
// 0: <Synthetic node for Model> -- [attr description]
|
|
// 1: ==> Model.objects [ListElement, attr description]
|
|
// 2: ==> .value_list(...) [ListElement, TupleIndex 1]
|
|
//
|
|
// but for now, we just model a store step directly from the synthetic
|
|
// node to the method call.
|
|
//
|
|
// extra method on query-set/manager that does _not_ return a query-set,
|
|
// but a collection of instances.
|
|
modelClass = subclassRef() and
|
|
methodName in ["iterator", "bulk_create"] and
|
|
this = [manager(modelClass), querySet(modelClass)].getMember(methodName).getACall()
|
|
}
|
|
|
|
/** Gets the model class that this is an instance source of. */
|
|
API::Node getModelClass() { result = modelClass }
|
|
|
|
/** Holds if this instance-source is fetching data from the DB. */
|
|
predicate isDbFetch() { not methodName = "bulk_create" }
|
|
}
|
|
|
|
/**
|
|
* A method call on a query-set or manager that returns a dictionary
|
|
* containing instances of a django models as the values.
|
|
*/
|
|
class QuerySetMethodInstanceDictValue extends DataFlow::CallCfgNode {
|
|
API::Node modelClass;
|
|
|
|
QuerySetMethodInstanceDictValue() {
|
|
modelClass = subclassRef() and
|
|
this = [manager(modelClass), querySet(modelClass)].getMember("in_bulk").getACall()
|
|
}
|
|
|
|
/** Gets the model class that this is an instance source of. */
|
|
API::Node getModelClass() { result = modelClass }
|
|
|
|
/** Holds if this instance-source is fetching data from the DB. */
|
|
predicate isDbFetch() { any() }
|
|
}
|
|
|
|
/**
|
|
* Gets a reference to an instance of `django.db.models.Model` class or any subclass,
|
|
* where `modelClass` specifies the class.
|
|
*/
|
|
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t, API::Node modelClass) {
|
|
t.start() and
|
|
modelClass = result.(InstanceSource).getModelClass()
|
|
or
|
|
exists(DataFlow::TypeTracker t2 | result = instance(t2, modelClass).track(t2, t))
|
|
}
|
|
|
|
/**
|
|
* Gets a reference to an instance of `django.db.models.Model` class or any subclass,
|
|
* where `modelClass` specifies the class.
|
|
*/
|
|
DataFlow::Node instance(API::Node modelClass) {
|
|
instance(DataFlow::TypeTracker::end(), modelClass).flowsTo(result)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Provides models for the `django.db.models.FileField` class and `ImageField` subclasses.
|
|
*
|
|
* See
|
|
* - https://docs.djangoproject.com/en/3.1/ref/models/fields/#django.db.models.FileField
|
|
* - https://docs.djangoproject.com/en/3.1/ref/models/fields/#django.db.models.ImageField
|
|
*/
|
|
module FileField {
|
|
/** Gets a reference to the `django.db.models.FileField` or the `django.db.models.ImageField` class or any subclass. */
|
|
API::Node subclassRef() {
|
|
exists(string className | className in ["FileField", "ImageField"] |
|
|
// commonly used alias
|
|
result =
|
|
API::moduleImport("django")
|
|
.getMember("db")
|
|
.getMember("models")
|
|
.getMember(className)
|
|
.getASubclass*()
|
|
or
|
|
// actual class definition
|
|
result =
|
|
API::moduleImport("django")
|
|
.getMember("db")
|
|
.getMember("models")
|
|
.getMember("fields")
|
|
.getMember("files")
|
|
.getMember(className)
|
|
.getASubclass*()
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets a reference to the Manager (django.db.models.Manager) for the django Model `modelClass`,
|
|
* accessed by `<modelClass>.objects`.
|
|
*/
|
|
API::Node manager(API::Node modelClass) {
|
|
modelClass = Model::subclassRef() and
|
|
result = modelClass.getMember("objects")
|
|
}
|
|
|
|
/**
|
|
* Gets a method with `name` that returns a QuerySet.
|
|
* This method can originate on a QuerySet or a Manager.
|
|
* `modelClass` specifies the django Model that this query-set originates from.
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.1/ref/models/querysets/
|
|
*/
|
|
API::Node querySetReturningMethod(API::Node modelClass, string name) {
|
|
name in [
|
|
"none", "all", "filter", "exclude", "complex_filter", "union", "intersection",
|
|
"difference", "select_for_update", "select_related", "prefetch_related", "order_by",
|
|
"distinct", "reverse", "defer", "only", "using", "annotate", "extra", "raw",
|
|
"datetimes", "dates", "values", "values_list", "alias"
|
|
] and
|
|
result = [manager(modelClass), querySet(modelClass)].getMember(name)
|
|
or
|
|
name = "get_queryset" and
|
|
result = manager(modelClass).getMember(name)
|
|
}
|
|
|
|
/**
|
|
* Gets a reference to a QuerySet (django.db.models.query.QuerySet).
|
|
* `modelClass` specifies the django Model that this query-set originates from.
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.1/ref/models/querysets/
|
|
*/
|
|
API::Node querySet(API::Node modelClass) {
|
|
result = querySetReturningMethod(modelClass, _).getReturn()
|
|
}
|
|
|
|
/** Gets a reference to the `django.db.models.expressions` module. */
|
|
API::Node expressions() { result = models().getMember("expressions") }
|
|
|
|
/** Provides models for the `django.db.models.expressions` module. */
|
|
module Expressions {
|
|
/** Provides models for the `django.db.models.expressions.RawSql` class. */
|
|
module RawSql {
|
|
/**
|
|
* Gets an reference to the `django.db.models.expressions.RawSQL` class.
|
|
*/
|
|
API::Node classRef() {
|
|
result = expressions().getMember("RawSQL")
|
|
or
|
|
// Commonly used alias
|
|
result = models().getMember("RawSQL")
|
|
}
|
|
|
|
/**
|
|
* Gets an instance of the `django.db.models.expressions.RawSQL` class,
|
|
* that was initiated with the SQL represented by `sql`.
|
|
*/
|
|
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t, DataFlow::Node sql) {
|
|
t.start() and
|
|
exists(DataFlow::CallCfgNode c | result = c |
|
|
c = classRef().getACall() and
|
|
c.getArg(0) = sql
|
|
)
|
|
or
|
|
exists(DataFlow::TypeTracker t2 | result = instance(t2, sql).track(t2, t))
|
|
}
|
|
|
|
/**
|
|
* Gets an instance of the `django.db.models.expressions.RawSQL` class,
|
|
* that was initiated with the SQL represented by `sql`.
|
|
*/
|
|
DataFlow::Node instance(DataFlow::Node sql) {
|
|
instance(DataFlow::TypeTracker::end(), sql).flowsTo(result)
|
|
}
|
|
}
|
|
}
|
|
|
|
/** This internal module provides data-flow modeling of Django ORM. */
|
|
private module OrmDataflow {
|
|
private import semmle.python.dataflow.new.internal.DataFlowPrivate::Orm
|
|
|
|
/** Gets the (AST) class of the Django model class `modelClass`. */
|
|
Class getModelClassClass(API::Node modelClass) {
|
|
result.getParent() = modelClass.asSource().asExpr() and
|
|
modelClass = Model::subclassRef()
|
|
}
|
|
|
|
/** A synthetic node representing the data for an Django ORM model saved in a DB. */
|
|
class SyntheticDjangoOrmModelNode extends SyntheticOrmModelNode {
|
|
API::Node modelClass;
|
|
|
|
SyntheticDjangoOrmModelNode() { this.getClass() = getModelClassClass(modelClass) }
|
|
|
|
/** Gets the API node for this Django model class. */
|
|
API::Node getModelClass() { result = modelClass }
|
|
}
|
|
|
|
/**
|
|
* Gets a synthetic node where the data in the attribute `fieldName` can flow
|
|
* to, when a DB store is made on `subModel`, taking ORM inheritance into
|
|
* account.
|
|
*
|
|
* If `fieldName` is defined in class `base`, the results will include the
|
|
* synthetic node for `base` itself, the synthetic node for `subModel`, as
|
|
* well as all the classes in-between (in the class hierarchy).
|
|
*/
|
|
SyntheticDjangoOrmModelNode nodeToStoreIn(API::Node subModel, string fieldName) {
|
|
exists(Class base, API::Node baseModel, API::Node resultModel |
|
|
baseModel = Model::subclassRef() and
|
|
resultModel = Model::subclassRef() and
|
|
baseModel.getASubclass*() = subModel and
|
|
base = getModelClassClass(baseModel) and
|
|
exists(Variable v |
|
|
base.getBody().getAnItem().(AssignStmt).defines(v) and
|
|
v.getId() = fieldName
|
|
)
|
|
|
|
|
baseModel.getASubclass*() = resultModel and
|
|
resultModel.getASubclass*() = subModel and
|
|
result.getModelClass() = resultModel
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Gets the synthetic node where data could be loaded from, when a fetch is
|
|
* made on `modelClass`.
|
|
*
|
|
* In vanilla Django inheritance, this is simply the model itself, but if a
|
|
* model is based on `polymorphic.models.PolymorphicModel`, a fetch of the
|
|
* base-class can also yield instances of its subclasses.
|
|
*/
|
|
SyntheticDjangoOrmModelNode nodeToLoadFrom(API::Node modelClass) {
|
|
result.getModelClass() = modelClass
|
|
or
|
|
exists(API::Node polymorphicModel |
|
|
polymorphicModel =
|
|
API::moduleImport("polymorphic").getMember("models").getMember("PolymorphicModel")
|
|
|
|
|
polymorphicModel.getASubclass+() = modelClass and
|
|
modelClass.getASubclass+() = result.getModelClass()
|
|
)
|
|
}
|
|
|
|
/** Additional data-flow steps for Django ORM models. */
|
|
class DjangOrmSteps extends AdditionalOrmSteps {
|
|
override predicate storeStep(
|
|
DataFlow::Node nodeFrom, DataFlow::Content c, DataFlow::Node nodeTo
|
|
) {
|
|
// attribute value from constructor call -> object created
|
|
exists(DataFlow::CallCfgNode call, string fieldName |
|
|
// Note: Currently only supports kwargs, which should by far be the most
|
|
// common way to do things. We _should_ investigate how often
|
|
// positional-args are used.
|
|
call = Model::subclassRef().getACall() and
|
|
nodeFrom = call.getArgByName(fieldName) and
|
|
c.(DataFlow::AttributeContent).getAttribute() = fieldName and
|
|
nodeTo = call
|
|
)
|
|
or
|
|
// attribute store in `<Model>.objects.create`, `get_or_create`, and `update_or_create`
|
|
// see https://docs.djangoproject.com/en/4.0/ref/models/querysets/#create
|
|
// see https://docs.djangoproject.com/en/4.0/ref/models/querysets/#get-or-create
|
|
// see https://docs.djangoproject.com/en/4.0/ref/models/querysets/#update-or-create
|
|
// TODO: This does currently not handle values passed in the `defaults` dictionary
|
|
exists(
|
|
DataFlow::CallCfgNode call, API::Node modelClass, string fieldName,
|
|
string methodName
|
|
|
|
|
modelClass = Model::subclassRef() and
|
|
methodName in ["create", "get_or_create", "update_or_create"] and
|
|
call = modelClass.getMember("objects").getMember(methodName).getACall() and
|
|
nodeFrom = call.getArgByName(fieldName) and
|
|
c.(DataFlow::AttributeContent).getAttribute() = fieldName and
|
|
(
|
|
// -> object created
|
|
(
|
|
methodName = "create" and nodeTo = call
|
|
or
|
|
// TODO: for these two methods, the result is a tuple `(<Model>, bool)`,
|
|
// which we need flow-summaries to model properly
|
|
methodName in ["get_or_create", "update_or_create"] and none()
|
|
)
|
|
or
|
|
// -> DB store on synthetic node
|
|
nodeTo = nodeToStoreIn(modelClass, fieldName)
|
|
)
|
|
)
|
|
or
|
|
// attribute store in `<Model>.objects.[<QuerySet>].update()` -> synthetic
|
|
// see https://docs.djangoproject.com/en/4.0/ref/models/querysets/#update
|
|
exists(DataFlow::CallCfgNode call, API::Node modelClass, string fieldName |
|
|
call = [manager(modelClass), querySet(modelClass)].getMember("update").getACall() and
|
|
nodeFrom = call.getArgByName(fieldName) and
|
|
c.(DataFlow::AttributeContent).getAttribute() = fieldName and
|
|
nodeTo = nodeToStoreIn(modelClass, fieldName)
|
|
)
|
|
or
|
|
// synthetic -> method-call that returns collection of ORM models (all/filter/...)
|
|
exists(API::Node modelClass |
|
|
nodeFrom = nodeToLoadFrom(modelClass) and
|
|
nodeTo.(Model::QuerySetMethodInstanceCollection).getModelClass() = modelClass and
|
|
nodeTo.(Model::QuerySetMethodInstanceCollection).isDbFetch() and
|
|
c instanceof DataFlow::ListElementContent
|
|
)
|
|
or
|
|
// synthetic -> method-call that returns dictionary with ORM models as values
|
|
exists(API::Node modelClass |
|
|
nodeFrom = nodeToLoadFrom(modelClass) and
|
|
nodeTo.(Model::QuerySetMethodInstanceDictValue).getModelClass() = modelClass and
|
|
nodeTo.(Model::QuerySetMethodInstanceDictValue).isDbFetch() and
|
|
c instanceof DataFlow::DictionaryElementAnyContent
|
|
)
|
|
}
|
|
|
|
override predicate jumpStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
|
// save -> synthetic
|
|
exists(API::Node modelClass, DataFlow::MethodCallNode saveCall |
|
|
// TODO: The `nodeTo` should be restricted more, such that flow to
|
|
// base-classes are only for the fields that are defined in the
|
|
// base-class... but only passing on flow for a specific attribute requires flow-summaries,
|
|
// so we can do
|
|
// `obj (in obj.save call) ==read of attr==> synthetic attr on base-class ==store of attr==> synthetic for base-class`
|
|
nodeTo = nodeToStoreIn(modelClass, _) and
|
|
saveCall.calls(Model::instance(modelClass), "save") and
|
|
nodeFrom = saveCall.getObject()
|
|
)
|
|
or
|
|
// synthetic -> method-call that returns single ORM model (get/first/...)
|
|
exists(API::Node modelClass |
|
|
nodeFrom = nodeToLoadFrom(modelClass) and
|
|
nodeTo.(Model::InstanceSource).getModelClass() = modelClass and
|
|
nodeTo.(Model::InstanceSource).isDbFetch()
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A call to the `annotate` function on a model using a `RawSQL` argument.
|
|
*
|
|
* TODO: Consider reworking this to use taint tracking.
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.1/ref/models/querysets/#annotate
|
|
*/
|
|
private class ObjectsAnnotate extends SqlExecution::Range, DataFlow::CallCfgNode {
|
|
DataFlow::Node sql;
|
|
|
|
ObjectsAnnotate() {
|
|
this = DjangoImpl::DB::Models::querySetReturningMethod(_, "annotate").getACall() and
|
|
DjangoImpl::DB::Models::Expressions::RawSql::instance(sql) in [
|
|
this.getArg(_), this.getArgByName(_)
|
|
]
|
|
}
|
|
|
|
override DataFlow::Node getSql() { result = sql }
|
|
}
|
|
|
|
/**
|
|
* A call to the `alias` function on a model using a `RawSQL` argument.
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.2/ref/models/querysets/#alias
|
|
*/
|
|
private class ObjectsAlias extends SqlExecution::Range, DataFlow::CallCfgNode {
|
|
DataFlow::Node sql;
|
|
|
|
ObjectsAlias() {
|
|
this = DjangoImpl::DB::Models::querySetReturningMethod(_, "alias").getACall() and
|
|
DjangoImpl::DB::Models::Expressions::RawSql::instance(sql) in [
|
|
this.getArg(_), this.getArgByName(_)
|
|
]
|
|
}
|
|
|
|
override DataFlow::Node getSql() { result = sql }
|
|
}
|
|
|
|
/**
|
|
* A call to the `raw` function on a model.
|
|
*
|
|
* See
|
|
* - https://docs.djangoproject.com/en/3.1/topics/db/sql/#django.db.models.Manager.raw
|
|
* - https://docs.djangoproject.com/en/3.1/ref/models/querysets/#raw
|
|
*/
|
|
private class ObjectsRaw extends SqlExecution::Range, DataFlow::CallCfgNode {
|
|
ObjectsRaw() { this = DjangoImpl::DB::Models::querySetReturningMethod(_, "raw").getACall() }
|
|
|
|
override DataFlow::Node getSql() { result = this.getArg(0) }
|
|
}
|
|
|
|
/**
|
|
* A call to the `extra` function on a model.
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.1/ref/models/querysets/#extra
|
|
*/
|
|
private class ObjectsExtra extends SqlExecution::Range, DataFlow::CallCfgNode {
|
|
ObjectsExtra() {
|
|
this = DjangoImpl::DB::Models::querySetReturningMethod(_, "extra").getACall()
|
|
}
|
|
|
|
override DataFlow::Node getSql() {
|
|
result in [
|
|
this.getArg([0, 1, 3, 4]), this.getArgByName(["select", "where", "tables", "order_by"])
|
|
]
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// django.urls
|
|
// -------------------------------------------------------------------------
|
|
/** Gets a reference to the `django.urls` module. */
|
|
API::Node urls() { result = django().getMember("urls") }
|
|
|
|
/** Provides models for the `django.urls` module */
|
|
module Urls {
|
|
/**
|
|
* Gets a reference to the `django.urls.path` function.
|
|
* See https://docs.djangoproject.com/en/3.0/ref/urls/#path
|
|
*/
|
|
API::Node path() { result = urls().getMember("path") }
|
|
|
|
/**
|
|
* Gets a reference to the `django.urls.re_path` function.
|
|
* See https://docs.djangoproject.com/en/3.0/ref/urls/#re_path
|
|
*/
|
|
API::Node re_path() { result = urls().getMember("re_path") }
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// django.conf
|
|
// -------------------------------------------------------------------------
|
|
/** Gets a reference to the `django.conf` module. */
|
|
API::Node conf() { result = django().getMember("conf") }
|
|
|
|
/** Provides models for the `django.conf` module */
|
|
module Conf {
|
|
/** Provides models for the `django.conf.urls` module */
|
|
module ConfUrls {
|
|
// -------------------------------------------------------------------------
|
|
// django.conf.urls
|
|
// -------------------------------------------------------------------------
|
|
// NOTE: had to rename due to shadowing rules in QL
|
|
/** Gets a reference to the `django.conf.urls` module. */
|
|
API::Node conf_urls() { result = conf().getMember("urls") }
|
|
|
|
/**
|
|
* Gets a reference to the `django.conf.urls.url` function.
|
|
*
|
|
* See https://docs.djangoproject.com/en/1.11/ref/urls/#django.conf.urls.url
|
|
*/
|
|
API::Node url() { result = conf_urls().getMember("url") }
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// django.http
|
|
// -------------------------------------------------------------------------
|
|
/** Gets a reference to the `django.http` module. */
|
|
API::Node http() { result = django().getMember("http") }
|
|
|
|
/** DEPRECATED: Alias for `DjangoHttp` */
|
|
deprecated module http = DjangoHttp;
|
|
|
|
/** Provides models for the `django.http` module */
|
|
module DjangoHttp {
|
|
// ---------------------------------------------------------------------------
|
|
// django.http.request
|
|
// ---------------------------------------------------------------------------
|
|
/** Gets a reference to the `django.http.request` module. */
|
|
API::Node request() { result = http().getMember("request") }
|
|
|
|
/** Provides models for the `django.http.request` module. */
|
|
module Request {
|
|
/**
|
|
* Provides models for the `django.http.request.HttpRequest` class
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.0/ref/request-response/#httprequest-objects
|
|
*/
|
|
module HttpRequest {
|
|
/** Gets a reference to the `django.http.request.HttpRequest` class. */
|
|
API::Node classRef() {
|
|
result = request().getMember("HttpRequest")
|
|
or
|
|
// handle django.http.HttpRequest alias
|
|
result = http().getMember("HttpRequest")
|
|
}
|
|
|
|
/**
|
|
* A source of instances of `django.http.request.HttpRequest`, 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 `django::http::request::HttpRequest::instance()` predicate to get
|
|
* references to instances of `django.http.request.HttpRequest`.
|
|
*/
|
|
abstract class InstanceSource extends DataFlow::Node { }
|
|
|
|
/** Gets a reference to an instance of `django.http.request.HttpRequest`. */
|
|
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.http.request.HttpRequest`. */
|
|
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
|
|
|
/**
|
|
* Taint propagation for `django.http.request.HttpRequest`.
|
|
*/
|
|
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
|
InstanceTaintSteps() { this = "django.http.request.HttpRequest" }
|
|
|
|
override DataFlow::Node getInstance() { result = instance() }
|
|
|
|
override string getAttributeName() {
|
|
result in [
|
|
// str / bytes
|
|
"body", "path", "path_info", "method", "encoding", "content_type",
|
|
// django.http.QueryDict
|
|
"GET", "POST",
|
|
// dict[str, str]
|
|
"content_params", "COOKIES",
|
|
// dict[str, Any]
|
|
"META",
|
|
// HttpHeaders (case insensitive dict-like)
|
|
"headers",
|
|
// MultiValueDict[str, UploadedFile]
|
|
"FILES",
|
|
// django.urls.ResolverMatch
|
|
"resolver_match"
|
|
]
|
|
// TODO: Handle that a HttpRequest is iterable
|
|
}
|
|
|
|
override string getMethodName() {
|
|
result in ["get_full_path", "get_full_path_info", "read", "readline", "readlines"]
|
|
}
|
|
|
|
override string getAsyncMethodName() { none() }
|
|
}
|
|
|
|
/**
|
|
* Extra taint propagation for `django.http.request.HttpRequest`, not covered by `InstanceTaintSteps`.
|
|
*/
|
|
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
|
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
|
// special handling of the `build_absolute_uri` method, see
|
|
// https://docs.djangoproject.com/en/3.0/ref/request-response/#django.http.HttpRequest.build_absolute_uri
|
|
exists(DataFlow::AttrRead attr, DataFlow::CallCfgNode call, DataFlow::Node instance |
|
|
instance = DjangoImpl::DjangoHttp::Request::HttpRequest::instance() and
|
|
attr.getObject() = instance
|
|
|
|
|
attr.getAttributeName() = "build_absolute_uri" and
|
|
nodeTo.(DataFlow::CallCfgNode).getFunction() = attr and
|
|
call = nodeTo and
|
|
(
|
|
not exists(call.getArg(_)) and
|
|
not exists(call.getArgByName(_)) and
|
|
nodeFrom = instance
|
|
or
|
|
nodeFrom = call.getArg(0)
|
|
or
|
|
nodeFrom = call.getArgByName("location")
|
|
)
|
|
)
|
|
}
|
|
}
|
|
|
|
/** An attribute read on an django request that is a `MultiValueDict` instance. */
|
|
private class DjangoHttpRequestMultiValueDictInstances extends Django::MultiValueDict::InstanceSource
|
|
{
|
|
DjangoHttpRequestMultiValueDictInstances() {
|
|
this.(DataFlow::AttrRead).getObject() = instance() and
|
|
this.(DataFlow::AttrRead).getAttributeName() in ["GET", "POST", "FILES"]
|
|
}
|
|
}
|
|
|
|
/** An attribute read on an django request that is a `ResolverMatch` instance. */
|
|
private class DjangoHttpRequestResolverMatchInstances extends Django::ResolverMatch::InstanceSource
|
|
{
|
|
DjangoHttpRequestResolverMatchInstances() {
|
|
this.(DataFlow::AttrRead).getObject() = instance() and
|
|
this.(DataFlow::AttrRead).getAttributeName() = "resolver_match"
|
|
}
|
|
}
|
|
|
|
/** An `UploadedFile` instance that originates from a django request. */
|
|
private class DjangoHttpRequestUploadedFileInstances extends Django::UploadedFile::InstanceSource
|
|
{
|
|
DjangoHttpRequestUploadedFileInstances() {
|
|
// TODO: this currently only works in local-scope, since writing type-trackers for
|
|
// this is a little too much effort. Once API-graphs are available for more
|
|
// things, we can rewrite this.
|
|
//
|
|
// TODO: This approach for identifying member-access is very adhoc, and we should
|
|
// be able to do something more structured for providing modeling of the members
|
|
// of a container-object.
|
|
//
|
|
// dicts
|
|
exists(DataFlow::AttrRead files, DataFlow::Node dict |
|
|
files.accesses(instance(), "FILES") and
|
|
(
|
|
dict = files
|
|
or
|
|
dict.(DataFlow::MethodCallNode).calls(files, "dict")
|
|
)
|
|
|
|
|
this.asCfgNode().(SubscriptNode).getObject() = dict.asCfgNode()
|
|
or
|
|
this.(DataFlow::MethodCallNode).calls(dict, "get")
|
|
)
|
|
or
|
|
// getlist
|
|
exists(DataFlow::AttrRead files, DataFlow::MethodCallNode getlistCall |
|
|
files.accesses(instance(), "FILES") and
|
|
getlistCall.calls(files, "getlist") and
|
|
this.asCfgNode().(SubscriptNode).getObject() = getlistCall.asCfgNode()
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// django.http.response
|
|
// -------------------------------------------------------------------------
|
|
/** Gets a reference to the `django.http.response` module. */
|
|
API::Node response() { result = http().getMember("response") }
|
|
|
|
/** Provides models for the `django.http.response` module */
|
|
module Response {
|
|
/**
|
|
* Provides models for the `django.http.response.HttpResponse` class
|
|
*
|
|
* 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 or any subclass. */
|
|
API::Node classRef() { result = baseClassRef().getASubclass*() }
|
|
|
|
/**
|
|
* A source of instances of `django.http.response.HttpResponse`, 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 `HttpResponse::instance()` to get references to instances of `django.http.response.HttpResponse`.
|
|
*/
|
|
abstract class InstanceSource extends Http::Server::HttpResponse::Range, DataFlow::Node {
|
|
}
|
|
|
|
/** A direct instantiation of `django.http.response.HttpResponse`. */
|
|
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
|
|
ClassInstantiation() { this = classRef().getACall() }
|
|
|
|
override DataFlow::Node getBody() {
|
|
result in [this.getArg(0), this.getArgByName("content")]
|
|
}
|
|
|
|
// How to support the `headers` argument here?
|
|
override DataFlow::Node getMimetypeOrContentTypeArg() {
|
|
result in [this.getArg(1), this.getArgByName("content_type")]
|
|
}
|
|
|
|
override string getMimetypeDefault() { result = "text/html" }
|
|
}
|
|
|
|
/** Gets a reference to an instance of `django.http.response.HttpResponse`. */
|
|
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.http.response.HttpResponse`. */
|
|
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// HttpResponse subclasses
|
|
// see https://docs.djangoproject.com/en/3.1/ref/request-response/#httpresponse-subclasses
|
|
// ---------------------------------------------------------------------------
|
|
/**
|
|
* Provides models for the `django.http.response.HttpResponseRedirect` class
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.1/ref/request-response/#django.http.HttpResponseRedirect.
|
|
*/
|
|
module HttpResponseRedirect {
|
|
/** Gets a reference to the `django.http.response.HttpResponseRedirect` class. */
|
|
API::Node baseClassRef() {
|
|
result = response().getMember("HttpResponseRedirect")
|
|
or
|
|
// Handle `django.http.HttpResponseRedirect` alias
|
|
result = http().getMember("HttpResponseRedirect")
|
|
}
|
|
|
|
/** Gets a reference to a subclass of the `django.http.response.HttpResponseRedirect` class. */
|
|
API::Node classRef() { result = baseClassRef().getASubclass*() }
|
|
|
|
/**
|
|
* A source of instances of `django.http.response.HttpResponseRedirect`, 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 `HttpResponseRedirect::instance()` to get references to instances of `django.http.response.HttpResponseRedirect`.
|
|
*/
|
|
abstract class InstanceSource extends HttpResponse::InstanceSource,
|
|
Http::Server::HttpRedirectResponse::Range, DataFlow::Node
|
|
{ }
|
|
|
|
/** A direct instantiation of `django.http.response.HttpResponseRedirect`. */
|
|
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
|
|
ClassInstantiation() { this = classRef().getACall() }
|
|
|
|
override DataFlow::Node getBody() {
|
|
// note that even though browsers like Chrome usually doesn't fetch the
|
|
// content of a redirect, it is possible to observe the body (for example,
|
|
// with cURL).
|
|
result in [this.getArg(1), this.getArgByName("content")]
|
|
}
|
|
|
|
override DataFlow::Node getRedirectLocation() {
|
|
result in [this.getArg(0), this.getArgByName("redirect_to")]
|
|
}
|
|
|
|
// How to support the `headers` argument here?
|
|
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
|
|
|
|
override string getMimetypeDefault() { result = "text/html" }
|
|
}
|
|
|
|
/** Gets a reference to an instance of `django.http.response.HttpResponseRedirect`. */
|
|
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.http.response.HttpResponseRedirect`. */
|
|
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
|
}
|
|
|
|
/**
|
|
* Provides models for the `django.http.response.HttpResponsePermanentRedirect` class
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.1/ref/request-response/#django.http.HttpResponsePermanentRedirect.
|
|
*/
|
|
module HttpResponsePermanentRedirect {
|
|
/** Gets a reference to the `django.http.response.HttpResponsePermanentRedirect` class. */
|
|
API::Node baseClassRef() {
|
|
result = response().getMember("HttpResponsePermanentRedirect")
|
|
or
|
|
// Handle `django.http.HttpResponsePermanentRedirect` alias
|
|
result = http().getMember("HttpResponsePermanentRedirect")
|
|
}
|
|
|
|
/** Gets a reference to the `django.http.response.HttpResponsePermanentRedirect` class. */
|
|
API::Node classRef() { result = baseClassRef().getASubclass*() }
|
|
|
|
/**
|
|
* A source of instances of `django.http.response.HttpResponsePermanentRedirect`, 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 `HttpResponsePermanentRedirect::instance()` to get references to instances of `django.http.response.HttpResponsePermanentRedirect`.
|
|
*/
|
|
abstract class InstanceSource extends HttpResponse::InstanceSource,
|
|
Http::Server::HttpRedirectResponse::Range, DataFlow::Node
|
|
{ }
|
|
|
|
/** A direct instantiation of `django.http.response.HttpResponsePermanentRedirect`. */
|
|
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
|
|
ClassInstantiation() { this = classRef().getACall() }
|
|
|
|
override DataFlow::Node getBody() {
|
|
// note that even though browsers like Chrome usually doesn't fetch the
|
|
// content of a redirect, it is possible to observe the body (for example,
|
|
// with cURL).
|
|
result in [this.getArg(1), this.getArgByName("content")]
|
|
}
|
|
|
|
override DataFlow::Node getRedirectLocation() {
|
|
result in [this.getArg(0), this.getArgByName("redirect_to")]
|
|
}
|
|
|
|
// How to support the `headers` argument here?
|
|
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
|
|
|
|
override string getMimetypeDefault() { result = "text/html" }
|
|
}
|
|
|
|
/** Gets a reference to an instance of `django.http.response.HttpResponsePermanentRedirect`. */
|
|
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.http.response.HttpResponsePermanentRedirect`. */
|
|
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
|
}
|
|
|
|
/**
|
|
* Provides models for the `django.http.response.HttpResponseNotModified` class
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.1/ref/request-response/#django.http.HttpResponseNotModified.
|
|
*/
|
|
module HttpResponseNotModified {
|
|
/** Gets a reference to the `django.http.response.HttpResponseNotModified` class. */
|
|
API::Node baseClassRef() {
|
|
result = response().getMember("HttpResponseNotModified")
|
|
or
|
|
// TODO: remove/expand this part of the template as needed
|
|
// Handle `django.http.HttpResponseNotModified` alias
|
|
result = http().getMember("HttpResponseNotModified")
|
|
}
|
|
|
|
/** Gets a reference to the `django.http.response.HttpResponseNotModified` class. */
|
|
API::Node classRef() { result = baseClassRef().getASubclass*() }
|
|
|
|
/**
|
|
* A source of instances of `django.http.response.HttpResponseNotModified`, 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 `HttpResponseNotModified::instance()` to get references to instances of `django.http.response.HttpResponseNotModified`.
|
|
*/
|
|
abstract class InstanceSource extends HttpResponse::InstanceSource, DataFlow::Node { }
|
|
|
|
/** A direct instantiation of `django.http.response.HttpResponseNotModified`. */
|
|
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
|
|
ClassInstantiation() { this = classRef().getACall() }
|
|
|
|
override DataFlow::Node getBody() { none() }
|
|
|
|
// How to support the `headers` argument here?
|
|
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
|
|
|
|
override string getMimetypeDefault() { none() }
|
|
}
|
|
|
|
/** Gets a reference to an instance of `django.http.response.HttpResponseNotModified`. */
|
|
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.http.response.HttpResponseNotModified`. */
|
|
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
|
}
|
|
|
|
/**
|
|
* Provides models for the `django.http.response.HttpResponseBadRequest` class
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.1/ref/request-response/#django.http.HttpResponseBadRequest.
|
|
*/
|
|
module HttpResponseBadRequest {
|
|
/** Gets a reference to the `django.http.response.HttpResponseBadRequest` class. */
|
|
API::Node baseClassRef() {
|
|
result = response().getMember("HttpResponseBadRequest")
|
|
or
|
|
// Handle `django.http.HttpResponseBadRequest` alias
|
|
result = http().getMember("HttpResponseBadRequest")
|
|
}
|
|
|
|
/** Gets a reference to the `django.http.response.HttpResponseBadRequest` class. */
|
|
API::Node classRef() { result = baseClassRef().getASubclass*() }
|
|
|
|
/**
|
|
* A source of instances of `django.http.response.HttpResponseBadRequest`, 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 `HttpResponseBadRequest::instance()` to get references to instances of `django.http.response.HttpResponseBadRequest`.
|
|
*/
|
|
abstract class InstanceSource extends HttpResponse::InstanceSource, DataFlow::Node { }
|
|
|
|
/** A direct instantiation of `django.http.response.HttpResponseBadRequest`. */
|
|
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
|
|
ClassInstantiation() { this = classRef().getACall() }
|
|
|
|
override DataFlow::Node getBody() {
|
|
result in [this.getArg(0), this.getArgByName("content")]
|
|
}
|
|
|
|
// How to support the `headers` argument here?
|
|
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
|
|
|
|
override string getMimetypeDefault() { result = "text/html" }
|
|
}
|
|
|
|
/** Gets a reference to an instance of `django.http.response.HttpResponseBadRequest`. */
|
|
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.http.response.HttpResponseBadRequest`. */
|
|
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
|
}
|
|
|
|
/**
|
|
* Provides models for the `django.http.response.HttpResponseNotFound` class
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.1/ref/request-response/#django.http.HttpResponseNotFound.
|
|
*/
|
|
module HttpResponseNotFound {
|
|
/** Gets a reference to the `django.http.response.HttpResponseNotFound` class. */
|
|
API::Node baseClassRef() {
|
|
result = response().getMember("HttpResponseNotFound")
|
|
or
|
|
// Handle `django.http.HttpResponseNotFound` alias
|
|
result = http().getMember("HttpResponseNotFound")
|
|
}
|
|
|
|
/** Gets a reference to the `django.http.response.HttpResponseNotFound` class. */
|
|
API::Node classRef() { result = baseClassRef().getASubclass*() }
|
|
|
|
/**
|
|
* A source of instances of `django.http.response.HttpResponseNotFound`, 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 `HttpResponseNotFound::instance()` to get references to instances of `django.http.response.HttpResponseNotFound`.
|
|
*/
|
|
abstract class InstanceSource extends HttpResponse::InstanceSource, DataFlow::Node { }
|
|
|
|
/** A direct instantiation of `django.http.response.HttpResponseNotFound`. */
|
|
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
|
|
ClassInstantiation() { this = classRef().getACall() }
|
|
|
|
override DataFlow::Node getBody() {
|
|
result in [this.getArg(0), this.getArgByName("content")]
|
|
}
|
|
|
|
// How to support the `headers` argument here?
|
|
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
|
|
|
|
override string getMimetypeDefault() { result = "text/html" }
|
|
}
|
|
|
|
/** Gets a reference to an instance of `django.http.response.HttpResponseNotFound`. */
|
|
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.http.response.HttpResponseNotFound`. */
|
|
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
|
}
|
|
|
|
/**
|
|
* Provides models for the `django.http.response.HttpResponseForbidden` class
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.1/ref/request-response/#django.http.HttpResponseForbidden.
|
|
*/
|
|
module HttpResponseForbidden {
|
|
/** Gets a reference to the `django.http.response.HttpResponseForbidden` class. */
|
|
API::Node baseClassRef() {
|
|
result = response().getMember("HttpResponseForbidden")
|
|
or
|
|
// Handle `django.http.HttpResponseForbidden` alias
|
|
result = http().getMember("HttpResponseForbidden")
|
|
}
|
|
|
|
/** Gets a reference to the `django.http.response.HttpResponseForbidden` class. */
|
|
API::Node classRef() { result = baseClassRef().getASubclass*() }
|
|
|
|
/**
|
|
* A source of instances of `django.http.response.HttpResponseForbidden`, 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 `HttpResponseForbidden::instance()` to get references to instances of `django.http.response.HttpResponseForbidden`.
|
|
*/
|
|
abstract class InstanceSource extends HttpResponse::InstanceSource, DataFlow::Node { }
|
|
|
|
/** A direct instantiation of `django.http.response.HttpResponseForbidden`. */
|
|
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
|
|
ClassInstantiation() { this = classRef().getACall() }
|
|
|
|
override DataFlow::Node getBody() {
|
|
result in [this.getArg(0), this.getArgByName("content")]
|
|
}
|
|
|
|
// How to support the `headers` argument here?
|
|
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
|
|
|
|
override string getMimetypeDefault() { result = "text/html" }
|
|
}
|
|
|
|
/** Gets a reference to an instance of `django.http.response.HttpResponseForbidden`. */
|
|
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.http.response.HttpResponseForbidden`. */
|
|
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
|
}
|
|
|
|
/**
|
|
* Provides models for the `django.http.response.HttpResponseNotAllowed` class
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.1/ref/request-response/#django.http.HttpResponseNotAllowed.
|
|
*/
|
|
module HttpResponseNotAllowed {
|
|
/** Gets a reference to the `django.http.response.HttpResponseNotAllowed` class. */
|
|
API::Node baseClassRef() {
|
|
result = response().getMember("HttpResponseNotAllowed")
|
|
or
|
|
// Handle `django.http.HttpResponseNotAllowed` alias
|
|
result = http().getMember("HttpResponseNotAllowed")
|
|
}
|
|
|
|
/** Gets a reference to the `django.http.response.HttpResponseNotAllowed` class. */
|
|
API::Node classRef() { result = baseClassRef().getASubclass*() }
|
|
|
|
/**
|
|
* A source of instances of `django.http.response.HttpResponseNotAllowed`, 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 `HttpResponseNotAllowed::instance()` to get references to instances of `django.http.response.HttpResponseNotAllowed`.
|
|
*/
|
|
abstract class InstanceSource extends HttpResponse::InstanceSource, DataFlow::Node { }
|
|
|
|
/** A direct instantiation of `django.http.response.HttpResponseNotAllowed`. */
|
|
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
|
|
ClassInstantiation() { this = classRef().getACall() }
|
|
|
|
override DataFlow::Node getBody() {
|
|
// First argument is permitted methods
|
|
result in [this.getArg(1), this.getArgByName("content")]
|
|
}
|
|
|
|
// How to support the `headers` argument here?
|
|
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
|
|
|
|
override string getMimetypeDefault() { result = "text/html" }
|
|
}
|
|
|
|
/** Gets a reference to an instance of `django.http.response.HttpResponseNotAllowed`. */
|
|
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.http.response.HttpResponseNotAllowed`. */
|
|
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
|
}
|
|
|
|
/**
|
|
* Provides models for the `django.http.response.HttpResponseGone` class
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.1/ref/request-response/#django.http.HttpResponseGone.
|
|
*/
|
|
module HttpResponseGone {
|
|
/** Gets a reference to the `django.http.response.HttpResponseGone` class. */
|
|
API::Node baseClassRef() {
|
|
result = response().getMember("HttpResponseGone")
|
|
or
|
|
// Handle `django.http.HttpResponseGone` alias
|
|
result = http().getMember("HttpResponseGone")
|
|
}
|
|
|
|
/** Gets a reference to the `django.http.response.HttpResponseGone` class. */
|
|
API::Node classRef() { result = baseClassRef().getASubclass*() }
|
|
|
|
/**
|
|
* A source of instances of `django.http.response.HttpResponseGone`, 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 `HttpResponseGone::instance()` to get references to instances of `django.http.response.HttpResponseGone`.
|
|
*/
|
|
abstract class InstanceSource extends HttpResponse::InstanceSource, DataFlow::Node { }
|
|
|
|
/** A direct instantiation of `django.http.response.HttpResponseGone`. */
|
|
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
|
|
ClassInstantiation() { this = classRef().getACall() }
|
|
|
|
override DataFlow::Node getBody() {
|
|
result in [this.getArg(0), this.getArgByName("content")]
|
|
}
|
|
|
|
// How to support the `headers` argument here?
|
|
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
|
|
|
|
override string getMimetypeDefault() { result = "text/html" }
|
|
}
|
|
|
|
/** Gets a reference to an instance of `django.http.response.HttpResponseGone`. */
|
|
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.http.response.HttpResponseGone`. */
|
|
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
|
}
|
|
|
|
/**
|
|
* Provides models for the `django.http.response.HttpResponseServerError` class
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.1/ref/request-response/#django.http.HttpResponseServerError.
|
|
*/
|
|
module HttpResponseServerError {
|
|
/** Gets a reference to the `django.http.response.HttpResponseServerError` class. */
|
|
API::Node baseClassRef() {
|
|
result = response().getMember("HttpResponseServerError")
|
|
or
|
|
// Handle `django.http.HttpResponseServerError` alias
|
|
result = http().getMember("HttpResponseServerError")
|
|
}
|
|
|
|
/** Gets a reference to the `django.http.response.HttpResponseServerError` class. */
|
|
API::Node classRef() { result = baseClassRef().getASubclass*() }
|
|
|
|
/**
|
|
* A source of instances of `django.http.response.HttpResponseServerError`, 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 `HttpResponseServerError::instance()` to get references to instances of `django.http.response.HttpResponseServerError`.
|
|
*/
|
|
abstract class InstanceSource extends HttpResponse::InstanceSource, DataFlow::Node { }
|
|
|
|
/** A direct instantiation of `django.http.response.HttpResponseServerError`. */
|
|
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
|
|
ClassInstantiation() { this = classRef().getACall() }
|
|
|
|
override DataFlow::Node getBody() {
|
|
result in [this.getArg(0), this.getArgByName("content")]
|
|
}
|
|
|
|
// How to support the `headers` argument here?
|
|
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
|
|
|
|
override string getMimetypeDefault() { result = "text/html" }
|
|
}
|
|
|
|
/** Gets a reference to an instance of `django.http.response.HttpResponseServerError`. */
|
|
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.http.response.HttpResponseServerError`. */
|
|
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
|
}
|
|
|
|
/**
|
|
* Provides models for the `django.http.response.JsonResponse` class
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.1/ref/request-response/#jsonresponse-objects.
|
|
*/
|
|
module JsonResponse {
|
|
/** Gets a reference to the `django.http.response.JsonResponse` class. */
|
|
API::Node baseClassRef() {
|
|
result = response().getMember("JsonResponse")
|
|
or
|
|
// Handle `django.http.JsonResponse` alias
|
|
result = http().getMember("JsonResponse")
|
|
}
|
|
|
|
/** Gets a reference to the `django.http.response.JsonResponse` class. */
|
|
API::Node classRef() { result = baseClassRef().getASubclass*() }
|
|
|
|
/**
|
|
* A source of instances of `django.http.response.JsonResponse`, 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 `JsonResponse::instance()` to get references to instances of `django.http.response.JsonResponse`.
|
|
*/
|
|
abstract class InstanceSource extends HttpResponse::InstanceSource, DataFlow::Node { }
|
|
|
|
/** A direct instantiation of `django.http.response.JsonResponse`. */
|
|
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
|
|
ClassInstantiation() { this = classRef().getACall() }
|
|
|
|
override DataFlow::Node getBody() {
|
|
result in [this.getArg(0), this.getArgByName("data")]
|
|
}
|
|
|
|
// How to support the `headers` argument here?
|
|
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
|
|
|
|
override string getMimetypeDefault() { result = "application/json" }
|
|
}
|
|
|
|
/** Gets a reference to an instance of `django.http.response.JsonResponse`. */
|
|
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.http.response.JsonResponse`. */
|
|
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// HttpResponse-like classes
|
|
// ---------------------------------------------------------------------------
|
|
/**
|
|
* Provides models for the `django.http.response.StreamingHttpResponse` class
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.1/ref/request-response/#streaminghttpresponse-objects.
|
|
*/
|
|
module StreamingHttpResponse {
|
|
/** Gets a reference to the `django.http.response.StreamingHttpResponse` class. */
|
|
API::Node baseClassRef() {
|
|
result = response().getMember("StreamingHttpResponse")
|
|
or
|
|
// Handle `django.http.StreamingHttpResponse` alias
|
|
result = http().getMember("StreamingHttpResponse")
|
|
}
|
|
|
|
/** Gets a reference to the `django.http.response.StreamingHttpResponse` class. */
|
|
API::Node classRef() { result = baseClassRef().getASubclass*() }
|
|
|
|
/**
|
|
* A source of instances of `django.http.response.StreamingHttpResponse`, 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 `StreamingHttpResponse::instance()` to get references to instances of `django.http.response.StreamingHttpResponse`.
|
|
*/
|
|
abstract class InstanceSource extends HttpResponse::InstanceSource, DataFlow::Node { }
|
|
|
|
/** A direct instantiation of `django.http.response.StreamingHttpResponse`. */
|
|
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
|
|
ClassInstantiation() { this = classRef().getACall() }
|
|
|
|
override DataFlow::Node getBody() {
|
|
result in [this.getArg(0), this.getArgByName("streaming_content")]
|
|
}
|
|
|
|
// How to support the `headers` argument here?
|
|
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
|
|
|
|
override string getMimetypeDefault() { result = "text/html" }
|
|
}
|
|
|
|
/** Gets a reference to an instance of `django.http.response.StreamingHttpResponse`. */
|
|
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.http.response.StreamingHttpResponse`. */
|
|
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
|
}
|
|
|
|
/**
|
|
* Provides models for the `django.http.response.FileResponse` class
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.1/ref/request-response/#fileresponse-objects.
|
|
*/
|
|
module FileResponse {
|
|
/** Gets a reference to the `django.http.response.FileResponse` class. */
|
|
API::Node baseClassRef() {
|
|
result = response().getMember("FileResponse")
|
|
or
|
|
// Handle `django.http.FileResponse` alias
|
|
result = http().getMember("FileResponse")
|
|
}
|
|
|
|
/** Gets a reference to the `django.http.response.FileResponse` class. */
|
|
API::Node classRef() { result = baseClassRef().getASubclass*() }
|
|
|
|
/**
|
|
* A source of instances of `django.http.response.FileResponse`, 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 `FileResponse::instance()` to get references to instances of `django.http.response.FileResponse`.
|
|
*/
|
|
abstract class InstanceSource extends HttpResponse::InstanceSource, DataFlow::Node { }
|
|
|
|
/** A direct instantiation of `django.http.response.FileResponse`. */
|
|
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
|
|
ClassInstantiation() { this = classRef().getACall() }
|
|
|
|
override DataFlow::Node getBody() {
|
|
result in [this.getArg(0), this.getArgByName("streaming_content")]
|
|
}
|
|
|
|
// How to support the `headers` argument here?
|
|
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
|
|
|
|
override string getMimetypeDefault() {
|
|
// see https://github.com/django/django/blob/ebb08d19424c314c75908bc6048ff57c2f872269/django/http/response.py#L471-L479
|
|
result = "application/octet-stream"
|
|
}
|
|
}
|
|
|
|
/** Gets a reference to an instance of `django.http.response.FileResponse`. */
|
|
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.http.response.FileResponse`. */
|
|
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
|
}
|
|
|
|
/** Gets a reference to the `django.http.response.HttpResponse.write` function. */
|
|
private DataFlow::TypeTrackingNode write(
|
|
DjangoImpl::DjangoHttp::Response::HttpResponse::InstanceSource instance,
|
|
DataFlow::TypeTracker t
|
|
) {
|
|
t.startInAttr("write") and
|
|
instance = DjangoImpl::DjangoHttp::Response::HttpResponse::instance() and
|
|
result = instance
|
|
or
|
|
exists(DataFlow::TypeTracker t2 | result = write(instance, t2).track(t2, t))
|
|
}
|
|
|
|
/** Gets a reference to the `django.http.response.HttpResponse.write` function. */
|
|
DataFlow::Node write(DjangoImpl::DjangoHttp::Response::HttpResponse::InstanceSource instance) {
|
|
write(instance, DataFlow::TypeTracker::end()).flowsTo(result)
|
|
}
|
|
|
|
/**
|
|
* A call to the `django.http.response.HttpResponse.write` function.
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.1/ref/request-response/#django.http.HttpResponse.write
|
|
*/
|
|
class HttpResponseWriteCall extends Http::Server::HttpResponse::Range, DataFlow::CallCfgNode
|
|
{
|
|
DjangoImpl::DjangoHttp::Response::HttpResponse::InstanceSource instance;
|
|
|
|
HttpResponseWriteCall() { this.getFunction() = write(instance) }
|
|
|
|
override DataFlow::Node getBody() {
|
|
result in [this.getArg(0), this.getArgByName("content")]
|
|
}
|
|
|
|
override DataFlow::Node getMimetypeOrContentTypeArg() {
|
|
result = instance.getMimetypeOrContentTypeArg()
|
|
}
|
|
|
|
override string getMimetypeDefault() { result = instance.getMimetypeDefault() }
|
|
}
|
|
|
|
/**
|
|
* A call to `set_cookie` on a HTTP Response.
|
|
*/
|
|
class DjangoResponseSetCookieCall extends Http::Server::CookieWrite::Range,
|
|
DataFlow::MethodCallNode
|
|
{
|
|
DjangoResponseSetCookieCall() {
|
|
this.calls(DjangoImpl::DjangoHttp::Response::HttpResponse::instance(), "set_cookie")
|
|
}
|
|
|
|
override DataFlow::Node getHeaderArg() { none() }
|
|
|
|
override DataFlow::Node getNameArg() {
|
|
result in [this.getArg(0), this.getArgByName("key")]
|
|
}
|
|
|
|
override DataFlow::Node getValueArg() {
|
|
result in [this.getArg(1), this.getArgByName("value")]
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A call to `delete_cookie` on a HTTP Response.
|
|
*/
|
|
class DjangoResponseDeleteCookieCall extends Http::Server::CookieWrite::Range,
|
|
DataFlow::MethodCallNode
|
|
{
|
|
DjangoResponseDeleteCookieCall() {
|
|
this.calls(DjangoImpl::DjangoHttp::Response::HttpResponse::instance(), "delete_cookie")
|
|
}
|
|
|
|
override DataFlow::Node getHeaderArg() { none() }
|
|
|
|
override DataFlow::Node getNameArg() {
|
|
result in [this.getArg(0), this.getArgByName("key")]
|
|
}
|
|
|
|
override DataFlow::Node getValueArg() { none() }
|
|
}
|
|
|
|
/**
|
|
* A dict-like write to an item of the `cookies` attribute on a HTTP response, such as
|
|
* `response.cookies[name] = value`.
|
|
*/
|
|
class DjangoResponseCookieSubscriptWrite extends Http::Server::CookieWrite::Range {
|
|
DataFlow::Node index;
|
|
DataFlow::Node value;
|
|
|
|
DjangoResponseCookieSubscriptWrite() {
|
|
exists(SubscriptNode subscript, DataFlow::AttrRead cookieLookup |
|
|
// To give `this` a value, we need to choose between either LHS or RHS,
|
|
// and just go with the LHS
|
|
this.asCfgNode() = subscript
|
|
|
|
|
cookieLookup.getAttributeName() = "cookies" and
|
|
cookieLookup.getObject() = DjangoImpl::DjangoHttp::Response::HttpResponse::instance() and
|
|
exists(DataFlow::Node subscriptObj |
|
|
subscriptObj.asCfgNode() = subscript.getObject()
|
|
|
|
|
cookieLookup.flowsTo(subscriptObj)
|
|
) and
|
|
value.asCfgNode() = subscript.(DefinitionNode).getValue() and
|
|
index.asCfgNode() = subscript.getIndex()
|
|
)
|
|
}
|
|
|
|
override DataFlow::Node getHeaderArg() { none() }
|
|
|
|
override DataFlow::Node getNameArg() { result = index }
|
|
|
|
override DataFlow::Node getValueArg() { result = value }
|
|
}
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// django.shortcuts
|
|
// -------------------------------------------------------------------------
|
|
/** Gets a reference to the `django.shortcuts` module. */
|
|
API::Node shortcuts() { result = django().getMember("shortcuts") }
|
|
|
|
/** Provides models for the `django.shortcuts` module */
|
|
module Shortcuts {
|
|
/**
|
|
* Gets a reference to the `django.shortcuts.redirect` function
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.1/topics/http/shortcuts/#redirect
|
|
*/
|
|
API::Node redirect() { result = shortcuts().getMember("redirect") }
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Form and form field modeling
|
|
// ---------------------------------------------------------------------------
|
|
/**
|
|
* A class that is a subclass of the `django.forms.Form` class,
|
|
* thereby handling user input.
|
|
*/
|
|
class DjangoFormClass extends Class, SelfRefMixin {
|
|
DjangoFormClass() { this.getParent() = Django::Forms::Form::subclassRef().asSource().asExpr() }
|
|
}
|
|
|
|
/**
|
|
* A source of cleaned_data (either the return value from `super().clean()`, or a reference to `self.cleaned_data`)
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.1/ref/forms/validation/#form-and-field-validation
|
|
*/
|
|
private class DjangoFormCleanedData extends RemoteFlowSource::Range, DataFlow::Node {
|
|
DjangoFormCleanedData() {
|
|
exists(DjangoFormClass cls, Function meth |
|
|
cls.getAMethod() = meth and
|
|
(
|
|
this = API::builtin("super").getReturn().getMember("clean").getACall() and
|
|
this.getScope() = meth
|
|
or
|
|
this.(DataFlow::AttrRead).getAttributeName() = "cleaned_data" and
|
|
this.(DataFlow::AttrRead).getObject() = cls.getASelfRef()
|
|
)
|
|
)
|
|
}
|
|
|
|
override string getSourceType() {
|
|
result = "django.forms.Field subclass, value parameter in method"
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A class that is a subclass of the `django.forms.Field` class,
|
|
* thereby handling user input.
|
|
*/
|
|
class DjangoFormFieldClass extends Class {
|
|
DjangoFormFieldClass() {
|
|
this.getParent() = Django::Forms::Field::subclassRef().asSource().asExpr()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A parameter in a method on a `DjangoFormFieldClass` that receives the user-supplied value for this field.
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.1/ref/forms/validation/#form-and-field-validation
|
|
*/
|
|
private class DjangoFormFieldValueParam extends RemoteFlowSource::Range, DataFlow::ParameterNode {
|
|
DjangoFormFieldValueParam() {
|
|
exists(DjangoFormFieldClass cls, Function meth |
|
|
cls.getAMethod() = meth and
|
|
meth.getName() in ["to_python", "validate", "run_validators", "clean"] and
|
|
this.getParameter() = meth.getArg(1)
|
|
)
|
|
}
|
|
|
|
override string getSourceType() {
|
|
result = "django.forms.Field subclass, value parameter in method"
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// routing modeling
|
|
// ---------------------------------------------------------------------------
|
|
/**
|
|
* A class that may be a django view class. In order to recognize a class as being a django view class,
|
|
* based on the `as_view`
|
|
* call, we need to be able to track such calls on _any_ class. This is provided by
|
|
* the member predicates of this QL class.
|
|
*
|
|
* As such, a Python class being part of `DjangoViewClassHelper` doesn't signify that
|
|
* we model it as a django view class.
|
|
*/
|
|
class DjangoViewClassHelper extends Class {
|
|
/** Gets a reference to this class. */
|
|
private DataFlow::TypeTrackingNode getARef(DataFlow::TypeTracker t) {
|
|
t.start() and
|
|
result.asExpr() = this.getParent()
|
|
or
|
|
exists(DataFlow::TypeTracker t2 | result = this.getARef(t2).track(t2, t))
|
|
}
|
|
|
|
/** Gets a reference to this class. */
|
|
DataFlow::Node getARef() { this.getARef(DataFlow::TypeTracker::end()).flowsTo(result) }
|
|
|
|
/** Gets a reference to the `as_view` classmethod of this class. */
|
|
private DataFlow::TypeTrackingNode asViewRef(DataFlow::TypeTracker t) {
|
|
t.startInAttr("as_view") and
|
|
result = this.getARef()
|
|
or
|
|
exists(DataFlow::TypeTracker t2 | result = this.asViewRef(t2).track(t2, t))
|
|
}
|
|
|
|
/** Gets a reference to the `as_view` classmethod of this class. */
|
|
DataFlow::Node asViewRef() { this.asViewRef(DataFlow::TypeTracker::end()).flowsTo(result) }
|
|
|
|
/** Gets a reference to the result of calling the `as_view` classmethod of this class. */
|
|
private DataFlow::TypeTrackingNode asViewResult(DataFlow::TypeTracker t) {
|
|
t.start() and
|
|
result.asCfgNode().(CallNode).getFunction() = this.asViewRef().asCfgNode()
|
|
or
|
|
exists(DataFlow::TypeTracker t2 | result = this.asViewResult(t2).track(t2, t))
|
|
}
|
|
|
|
/** Gets a reference to the result of calling the `as_view` classmethod of this class. */
|
|
DataFlow::Node asViewResult() {
|
|
this.asViewResult(DataFlow::TypeTracker::end()).flowsTo(result)
|
|
}
|
|
}
|
|
|
|
/** A class that we consider a django View class. */
|
|
abstract class DjangoViewClass extends DjangoViewClassHelper, SelfRefMixin {
|
|
/** Gets a function that could handle incoming requests, if any. */
|
|
Function getARequestHandler() {
|
|
// 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() = Http::httpVerbLower()
|
|
or
|
|
result.getName() = "get_redirect_url"
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A class that is used in a route-setup, with `<class>.as_view()`, therefore being
|
|
* considered a django View class.
|
|
*/
|
|
class DjangoViewClassFromRouteSetup extends DjangoViewClass {
|
|
DjangoViewClassFromRouteSetup() {
|
|
exists(DjangoRouteSetup setup | setup.getViewArg() = this.asViewResult())
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A class that has a super-type which is a django View class, therefore also
|
|
* becoming a django View class.
|
|
*/
|
|
class DjangoViewClassFromSuperClass extends DjangoViewClass {
|
|
DjangoViewClassFromSuperClass() {
|
|
this.getParent() = Django::Views::View::subclassRef().asSource().asExpr()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A function that is a django route handler, meaning it handles incoming requests
|
|
* 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.
|
|
*/
|
|
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.
|
|
*/
|
|
int getFirstPossibleRoutedParamIndex() { result = 1 + this.getRequestParamIndex() }
|
|
|
|
/** Gets the index of the request parameter. */
|
|
int getRequestParamIndex() {
|
|
not this.isMethod() and
|
|
result = 0
|
|
or
|
|
this.isMethod() and
|
|
result = 1
|
|
}
|
|
|
|
/** Gets the request parameter. */
|
|
Parameter getRequestParam() { result = this.getArg(this.getRequestParamIndex()) }
|
|
}
|
|
|
|
/** Provides a class for modeling new django route handlers. */
|
|
module DjangoRouteHandler {
|
|
/**
|
|
* A django route handler. 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.
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.1/ref/class-based-views/base/#django.views.generic.base.RedirectView.get_redirect_url
|
|
*
|
|
* Note: this function only does something on a subclass of `RedirectView`, but since
|
|
* classes can be considered django view classes without us knowing their super-classes,
|
|
* we need to consider _any_ django view class. I don't expect any problems to come from this.
|
|
*/
|
|
private class GetRedirectUrlFunction extends DjangoRouteHandler {
|
|
GetRedirectUrlFunction() {
|
|
this.getName() = "get_redirect_url" and
|
|
any(DjangoViewClass vc).getARequestHandler() = this
|
|
}
|
|
|
|
override int getFirstPossibleRoutedParamIndex() { result = 1 }
|
|
|
|
override int getRequestParamIndex() { none() }
|
|
}
|
|
|
|
/** A data-flow node that sets up a route on a server, using the django framework. */
|
|
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();
|
|
|
|
final override DjangoRouteHandler getARequestHandler() {
|
|
poorMansFunctionTracker(result) = this.getViewArg()
|
|
or
|
|
exists(DjangoViewClass vc |
|
|
this.getViewArg() = vc.asViewResult() and
|
|
result = vc.getARequestHandler()
|
|
)
|
|
}
|
|
|
|
override string getFramework() { result = "Django" }
|
|
}
|
|
|
|
/** A request handler defined in a django view class, that has no known route. */
|
|
private class DjangoViewClassHandlerWithoutKnownRoute extends Http::Server::RequestHandler::Range,
|
|
DjangoRouteHandler
|
|
{
|
|
DjangoViewClassHandlerWithoutKnownRoute() {
|
|
exists(DjangoViewClass vc | vc.getARequestHandler() = this) and
|
|
not exists(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(_), //
|
|
this.getVararg().(Parameter), this.getKwarg().(Parameter), // TODO: These sources should be modeled as storing content!
|
|
] and
|
|
not result = any(int i | i < this.getFirstPossibleRoutedParamIndex() | this.getArg(i))
|
|
}
|
|
|
|
override string getFramework() { result = "Django" }
|
|
}
|
|
|
|
/**
|
|
* Gets the regex that is used by django to find routed parameters when using `django.urls.path`.
|
|
*
|
|
* Taken from https://github.com/django/django/blob/7d1bf29977bb368d7c28e7c6eb146db3b3009ae7/django/urls/resolvers.py#L199
|
|
*/
|
|
private string pathRoutedParameterRegex() {
|
|
result = "<(?:(?<converter>[^>:]+):)?(?<parameter>\\w+)>"
|
|
}
|
|
|
|
/**
|
|
* A call to `django.urls.path`.
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.0/ref/urls/#path
|
|
*/
|
|
private class DjangoUrlsPathCall extends DjangoRouteSetup, DataFlow::CallCfgNode {
|
|
DjangoUrlsPathCall() { this = DjangoImpl::Urls::path().getACall() }
|
|
|
|
override DataFlow::Node getUrlPatternArg() {
|
|
result in [this.getArg(0), this.getArgByName("route")]
|
|
}
|
|
|
|
override DataFlow::Node getViewArg() { result in [this.getArg(1), this.getArgByName("view")] }
|
|
|
|
override Parameter getARoutedParameter() {
|
|
// If 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.
|
|
exists(DjangoRouteHandler routeHandler | routeHandler = this.getARequestHandler() |
|
|
not exists(this.getUrlPattern()) and
|
|
result in [
|
|
routeHandler.getArg(_), routeHandler.getArgByName(_), //
|
|
routeHandler.getVararg().(Parameter), routeHandler.getKwarg().(Parameter), // TODO: These sources should be modeled as storing content!
|
|
] and
|
|
not result =
|
|
any(int i | i < routeHandler.getFirstPossibleRoutedParamIndex() | routeHandler.getArg(i))
|
|
)
|
|
or
|
|
exists(string name |
|
|
(
|
|
result = this.getARequestHandler().getKwarg() // TODO: These sources should be modeled as storing content!
|
|
or
|
|
result = this.getARequestHandler().getArgByName(name)
|
|
) and
|
|
exists(string match |
|
|
match = this.getUrlPattern().regexpFind(pathRoutedParameterRegex(), _, _) and
|
|
name = match.regexpCapture(pathRoutedParameterRegex(), 2)
|
|
)
|
|
)
|
|
}
|
|
}
|
|
|
|
/** A Django route setup that uses a Regex to specify route (and routed parameters). */
|
|
abstract private class DjangoRegexRouteSetup extends DjangoRouteSetup {
|
|
override Parameter getARoutedParameter() {
|
|
// If 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.
|
|
exists(DjangoRouteHandler routeHandler | routeHandler = this.getARequestHandler() |
|
|
not exists(this.getUrlPattern()) and
|
|
result in [
|
|
routeHandler.getArg(_), routeHandler.getArgByName(_), //
|
|
routeHandler.getVararg().(Parameter), routeHandler.getKwarg().(Parameter), // TODO: These sources should be modeled as storing content!
|
|
] and
|
|
not result =
|
|
any(int i | i < routeHandler.getFirstPossibleRoutedParamIndex() | routeHandler.getArg(i))
|
|
)
|
|
or
|
|
exists(DjangoRouteHandler routeHandler, DjangoRouteRegex regexUse, RegExp regex |
|
|
regex.getAUse() = regexUse and
|
|
routeHandler = this.getARequestHandler() and
|
|
regexUse.getRouteSetup() = this
|
|
|
|
|
// either using named capture groups (passed as keyword arguments) or using
|
|
// unnamed capture groups (passed as positional arguments)
|
|
not exists(regex.getGroupName(_, _)) and
|
|
// first group will have group number 1
|
|
result =
|
|
routeHandler
|
|
.getArg(routeHandler.getFirstPossibleRoutedParamIndex() - 1 +
|
|
regex.getGroupNumber(_, _))
|
|
or
|
|
result = routeHandler.getArgByName(regex.getGroupName(_, _))
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A regex that is used to set up a route.
|
|
*
|
|
* Needs this subclass to be considered a RegExpInterpretation.
|
|
*/
|
|
private class DjangoRouteRegex extends RegExpInterpretation::Range {
|
|
DjangoRegexRouteSetup rePathCall;
|
|
|
|
DjangoRouteRegex() { this = rePathCall.getUrlPatternArg() }
|
|
|
|
DjangoRegexRouteSetup getRouteSetup() { result = rePathCall }
|
|
}
|
|
|
|
/**
|
|
* A call to `django.urls.re_path`.
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.0/ref/urls/#re_path
|
|
*/
|
|
private class DjangoUrlsRePathCall extends DjangoRegexRouteSetup, DataFlow::CallCfgNode {
|
|
DjangoUrlsRePathCall() {
|
|
this = DjangoImpl::Urls::re_path().getACall() and
|
|
// `django.conf.urls.url` (which we support directly with
|
|
// `DjangoConfUrlsUrlCall`), is implemented in Django 2+ as backward compatibility
|
|
// using `django.urls.re_path`. See
|
|
// https://github.com/django/django/blob/stable/3.2.x/django/conf/urls/__init__.py#L22
|
|
// Since we're still installing dependencies and analyzing their source code,
|
|
// without explicitly filtering out this call, we would be double-counting such
|
|
// route-setups :( One practical negative side effect of double-counting it, is
|
|
// that since we can't figure out the URL in the library code calling `django.urls.re_path`
|
|
// (because we only consider local flow), we will for all those cases mark ANY parameter
|
|
// as being a routed-parameter, which can lead to FPs.
|
|
not exists(Module mod |
|
|
mod.getName() = "django.conf.urls.__init__" and
|
|
node.getEnclosingModule() = mod
|
|
)
|
|
}
|
|
|
|
override DataFlow::Node getUrlPatternArg() {
|
|
result in [this.getArg(0), this.getArgByName("route")]
|
|
}
|
|
|
|
override DataFlow::Node getViewArg() { result in [this.getArg(1), this.getArgByName("view")] }
|
|
}
|
|
|
|
/**
|
|
* A call to `django.conf.urls.url`.
|
|
*
|
|
* See https://docs.djangoproject.com/en/1.11/ref/urls/#django.conf.urls.url
|
|
*/
|
|
private class DjangoConfUrlsUrlCall extends DjangoRegexRouteSetup, DataFlow::CallCfgNode {
|
|
DjangoConfUrlsUrlCall() { this = DjangoImpl::Conf::ConfUrls::url().getACall() }
|
|
|
|
override DataFlow::Node getUrlPatternArg() {
|
|
result in [this.getArg(0), this.getArgByName("regex")]
|
|
}
|
|
|
|
override DataFlow::Node getViewArg() { result in [this.getArg(1), this.getArgByName("view")] }
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// HttpRequest taint modeling
|
|
// ---------------------------------------------------------------------------
|
|
/** A parameter that will receive the django `HttpRequest` instance when a request handler is invoked. */
|
|
private class DjangoRequestHandlerRequestParam extends DjangoImpl::DjangoHttp::Request::HttpRequest::InstanceSource,
|
|
RemoteFlowSource::Range, DataFlow::ParameterNode
|
|
{
|
|
DjangoRequestHandlerRequestParam() {
|
|
this.getParameter() = any(DjangoRouteSetup setup).getARequestHandler().getRequestParam()
|
|
or
|
|
this.getParameter() = any(DjangoViewClassHandlerWithoutKnownRoute setup).getRequestParam()
|
|
}
|
|
|
|
override string getSourceType() { result = "django.http.request.HttpRequest" }
|
|
}
|
|
|
|
/**
|
|
* A read of the `request` attribute on a reference to an instance of a View class,
|
|
* which is the request being processed currently.
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.1/topics/class-based-views/generic-display/#dynamic-filtering
|
|
*/
|
|
private class DjangoViewClassRequestAttributeRead extends DjangoImpl::DjangoHttp::Request::HttpRequest::InstanceSource,
|
|
RemoteFlowSource::Range, DataFlow::Node
|
|
{
|
|
DjangoViewClassRequestAttributeRead() {
|
|
exists(DataFlow::AttrRead read | this = read |
|
|
read.getObject() = any(DjangoViewClass vc).getASelfRef() and
|
|
read.getAttributeName() = "request"
|
|
)
|
|
}
|
|
|
|
override string getSourceType() {
|
|
result = "django HttpRequest from self.request in View class"
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A read of the `args` or `kwargs` attribute on a reference to an instance of a View class,
|
|
* which contains the routed parameters captured from the URL route.
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.1/topics/class-based-views/generic-display/#dynamic-filtering
|
|
*/
|
|
private class DjangoViewClassRoutedParamsAttributeRead extends RemoteFlowSource::Range,
|
|
DataFlow::Node
|
|
{
|
|
DjangoViewClassRoutedParamsAttributeRead() {
|
|
exists(DataFlow::AttrRead read | this = read |
|
|
read.getObject() = any(DjangoViewClass vc).getASelfRef() and
|
|
read.getAttributeName() in ["args", "kwargs"]
|
|
)
|
|
}
|
|
|
|
override string getSourceType() {
|
|
result = "django routed param from self.args/kwargs in View class"
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A parameter that accepts the filename used to upload a file. This is the second
|
|
* parameter in functions used for the `upload_to` argument to a `FileField`.
|
|
*
|
|
* Note that the value this parameter accepts cannot contain a slash. Even when
|
|
* forcing the filename to contain a slash when sending the request, django does
|
|
* something like `input_filename.split("/")[-1]` (so other special characters still
|
|
* allowed). This also means that although the return value from `upload_to` is used
|
|
* to construct a path, path injection is not possible.
|
|
*
|
|
* See
|
|
* - https://docs.djangoproject.com/en/3.1/ref/models/fields/#django.db.models.FileField.upload_to
|
|
* - https://docs.djangoproject.com/en/3.1/topics/http/file-uploads/#handling-uploaded-files-with-a-model
|
|
*/
|
|
private class DjangoFileFieldUploadToFunctionFilenameParam extends RemoteFlowSource::Range,
|
|
DataFlow::ParameterNode
|
|
{
|
|
DjangoFileFieldUploadToFunctionFilenameParam() {
|
|
exists(DataFlow::CallCfgNode call, DataFlow::Node uploadToArg, Function func |
|
|
this.getParameter() = func.getArg(1) and
|
|
call = DjangoImpl::DB::Models::FileField::subclassRef().getACall() and
|
|
uploadToArg in [call.getArg(2), call.getArgByName("upload_to")] and
|
|
uploadToArg = poorMansFunctionTracker(func)
|
|
)
|
|
}
|
|
|
|
override string getSourceType() {
|
|
result = "django filename parameter to function used in FileField.upload_to"
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// django.shortcuts.redirect
|
|
// ---------------------------------------------------------------------------
|
|
/**
|
|
* A call to `django.shortcuts.redirect`.
|
|
*
|
|
* Note: This works differently depending on what argument is used.
|
|
* _One_ option is to redirect to a full URL.
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.1/topics/http/shortcuts/#redirect
|
|
*/
|
|
private class DjangoShortcutsRedirectCall extends Http::Server::HttpRedirectResponse::Range,
|
|
DataFlow::CallCfgNode
|
|
{
|
|
DjangoShortcutsRedirectCall() { this = DjangoImpl::Shortcuts::redirect().getACall() }
|
|
|
|
/**
|
|
* Gets the data-flow node that specifies the location of this HTTP redirect response.
|
|
*
|
|
* Note: For `django.shortcuts.redirect`, the result might not be a full URL
|
|
* (as usually expected by this method), but could be a relative URL,
|
|
* a string identifying a view, or a Django model.
|
|
*/
|
|
override DataFlow::Node getRedirectLocation() {
|
|
result in [this.getArg(0), this.getArgByName("to")]
|
|
}
|
|
|
|
override DataFlow::Node getBody() { none() }
|
|
|
|
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
|
|
|
|
override string getMimetypeDefault() { none() }
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// RedirectView handling
|
|
// ---------------------------------------------------------------------------
|
|
/**
|
|
* A return from a method named `get_redirect_url` on a django view class.
|
|
*
|
|
* Note that in reality, this only does something on a subclass of `RedirectView` --
|
|
* but until API graphs makes this easy to model, I took a shortcut in modeling
|
|
* preciseness.
|
|
*
|
|
* See https://docs.djangoproject.com/en/3.1/ref/class-based-views/base/#redirectview
|
|
*/
|
|
private class DjangoRedirectViewGetRedirectUrlReturn extends Http::Server::HttpRedirectResponse::Range,
|
|
DataFlow::CfgNode
|
|
{
|
|
DjangoRedirectViewGetRedirectUrlReturn() {
|
|
node = any(GetRedirectUrlFunction f).getAReturnValueFlowNode()
|
|
}
|
|
|
|
override DataFlow::Node getRedirectLocation() { result = this }
|
|
|
|
override DataFlow::Node getBody() { none() }
|
|
|
|
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
|
|
|
|
override string getMimetypeDefault() { none() }
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Logging
|
|
// ---------------------------------------------------------------------------
|
|
/**
|
|
* A standard Python logger instance from Django.
|
|
* see https://github.com/django/django/blob/stable/4.0.x/django/utils/log.py#L11
|
|
*/
|
|
private class DjangoLogger extends Stdlib::Logger::InstanceSource {
|
|
DjangoLogger() {
|
|
this =
|
|
API::moduleImport("django")
|
|
.getMember("utils")
|
|
.getMember("log")
|
|
.getMember("request_logger")
|
|
.asSource()
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Settings
|
|
// ---------------------------------------------------------------------------
|
|
/**
|
|
* A custom middleware stack
|
|
*/
|
|
private class DjangoSettingsMiddlewareStack extends Http::Server::CsrfProtectionSetting::Range {
|
|
List list;
|
|
|
|
DjangoSettingsMiddlewareStack() {
|
|
this.asExpr() = list and
|
|
// we look for an assignment to the `MIDDLEWARE` setting
|
|
exists(DataFlow::Node mw |
|
|
mw.asVar().getName() = "MIDDLEWARE" and
|
|
DataFlow::localFlow(this, mw)
|
|
|
|
|
// To only include results where CSRF protection matters, we only care about CSRF
|
|
// protection when the django authentication middleware is enabled.
|
|
// Since an active session cookie is exactly what would allow an attacker to perform
|
|
// a CSRF attack.
|
|
// Notice that this does not ensure that this is not a FP, since the authentication
|
|
// middleware might be unused.
|
|
//
|
|
// This also strongly implies that `mw` is in fact a Django middleware setting and
|
|
// not just a variable named `MIDDLEWARE`.
|
|
list.getAnElt().(StrConst).getText() =
|
|
"django.contrib.auth.middleware.AuthenticationMiddleware"
|
|
)
|
|
}
|
|
|
|
override boolean getVerificationSetting() {
|
|
if
|
|
list.getAnElt().(StrConst).getText() in [
|
|
"django.middleware.csrf.CsrfViewMiddleware",
|
|
// see https://github.com/mozilla/django-session-csrf
|
|
"session_csrf.CsrfMiddleware"
|
|
]
|
|
then result = true
|
|
else result = false
|
|
}
|
|
}
|
|
|
|
private class DjangoCsrfDecorator extends Http::Server::CsrfLocalProtectionSetting::Range {
|
|
string decoratorName;
|
|
Function function;
|
|
|
|
DjangoCsrfDecorator() {
|
|
decoratorName in ["csrf_protect", "csrf_exempt", "requires_csrf_token", "ensure_csrf_cookie"] and
|
|
this =
|
|
API::moduleImport("django")
|
|
.getMember("views")
|
|
.getMember("decorators")
|
|
.getMember("csrf")
|
|
.getMember(decoratorName)
|
|
.getAValueReachableFromSource() and
|
|
this.asExpr() = function.getADecorator()
|
|
}
|
|
|
|
override Function getRequestHandler() { result = function }
|
|
|
|
override predicate csrfEnabled() { decoratorName in ["csrf_protect", "requires_csrf_token"] }
|
|
}
|
|
|
|
private predicate djangoUrlHasAllowedHostAndScheme(
|
|
DataFlow::GuardNode g, ControlFlowNode node, boolean branch
|
|
) {
|
|
exists(API::CallNode call |
|
|
call =
|
|
API::moduleImport("django")
|
|
.getMember("utils")
|
|
.getMember("http")
|
|
.getMember("url_has_allowed_host_and_scheme")
|
|
.getACall() and
|
|
g = call.asCfgNode() and
|
|
node = call.getParameter(0, "url").asSink().asCfgNode() and
|
|
branch = true
|
|
)
|
|
}
|
|
|
|
/**
|
|
* A call to `django.utils.http.url_has_allowed_host_and_scheme`, considered as a sanitizer-guard for URL redirection.
|
|
*
|
|
* See https://docs.djangoproject.com/en/4.2/_modules/django/utils/http/
|
|
*/
|
|
private class DjangoAllowedUrl extends UrlRedirect::Sanitizer {
|
|
DjangoAllowedUrl() {
|
|
this = DataFlow::BarrierGuard<djangoUrlHasAllowedHostAndScheme/3>::getABarrierNode()
|
|
}
|
|
}
|
|
}
|