Skip to content

Instantly share code, notes, and snippets.

@BekaValentine
Created January 2, 2020 05:09
Show Gist options
  • Select an option

  • Save BekaValentine/b2afc67ec965a930006d6e7159662972 to your computer and use it in GitHub Desktop.

Select an option

Save BekaValentine/b2afc67ec965a930006d6e7159662972 to your computer and use it in GitHub Desktop.
import python
import semmle.python.objects.Callables
import semmle.python.web.RateLimiters
import semmle.python.web.django.General
class DjangoModel extends ClassValue {
DjangoModel() {
Module::named("django.db.models").attr("Model") = this.getASuperType()
}
}
class ModelSaveMethod extends CallableValue {
ModelSaveMethod() {
exists(DjangoModel m | m.lookup("save") = this)
}
}
class ModelSaveMethodCallNode extends CallNode {
ModelSaveMethodCallNode() {
exists(BoundMethodObjectInternal m | this.getFunction().pointsTo(m) and m.getFunction() instanceof ModelSaveMethod)
}
}
class ModelDeleteMethod extends CallableValue {
ModelDeleteMethod() {
exists(DjangoModel m | m.lookup("delete") = this)
}
}
class ModelDeleteMethodCallNode extends CallNode {
ModelDeleteMethodCallNode() {
exists(BoundMethodObjectInternal m | this.getFunction().pointsTo(m) and m.getFunction() instanceof ModelDeleteMethod)
}
}
/*
* This is an unfortunate hack. The way Django creates `Model.objects` is such
* that it's difficult to analyze it with `pointsTo`, because it's all runtime
* constructed. Consequently, we have to fake it by looking instead for accesses
* to the `objects` field on sub*classes of `Model`. This will likely be broken
* in many situations, but is probably the best we can do.
*/
class ModelObjectsQuerySetAttrNode extends AttrNode {
ModelObjectsQuerySetAttrNode() {
exists(DjangoModel m | this.getObject().pointsTo(m))
and
this.getName() = "objects"
}
}
/*
* As above, this doesn't precisely pick out the `create` method, but rather it
* picks out accesses to the `create` field on instances of
* `ModelObjectQuerySet`. This will naturally have limitations.
*/
class QuerySetCreateMethodAttrNode extends AttrNode {
QuerySetCreateMethodAttrNode() {
this.getObject() instanceof ModelObjectsQuerySetAttrNode
and
this.getName() = "create"
}
}
class QuerySetCreateMethodAttrNodeCall extends CallNode {
QuerySetCreateMethodAttrNodeCall() {
this.getFunction() instanceof QuerySetCreateMethodAttrNode
}
}
class ExpensiveDjangoMethodCall extends CallNode {
ExpensiveDjangoMethodCall() {
this instanceof ModelSaveMethodCallNode
or
this instanceof ModelDeleteMethodCallNode
or
this instanceof QuerySetCreateMethodAttrNodeCall
}
}
predicate callsNode(Function f, AstNode n) {
f.contains(n)
}
predicate callsNodePlus(Function f, AstNode n) {
callsNode(f,n)
or
exists(Call c, CallableValue v |
f.contains(c) and
c.getFunc().pointsTo(v) and
callsNodePlus(v.getScope(), n)
)
}
class DjangoDBExpensiveRouteHandler extends ExpensiveRouteHandler {
ExpensiveDjangoMethodCall expensiveCall;
DjangoDBExpensiveRouteHandler() {
exists(DjangoRoute r | r.getViewFunction().getScope() = this ) and
callsNodePlus(this, expensiveCall.getNode())
}
override string getExplanation() {
result = "calls expensive Django DB function"
}
}
from DjangoModel x
select x
from mydjango.db import models
from mydjango.conf.urls import url
from mydjango.urls import path
class MyModel(models.Model):
pass
def cheap_handler():
pass
def expensive_handler_save():
MyModel().save()
def expensive_handler_delete():
MyModel().delete()
def expensive_handler_update():
MyModel().update()
def expensive_handler_create():
MyModel.objects.create()
def expensive_handler_create_indirect():
helper()
def helper():
MyModel.objects.create()
patterns = [
url("cheap_handler", cheap_handler),
url("expensive_handler_save", expensive_handler_save),
path("expensive_handler_delete", expensive_handler_delete)
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment