Created
January 2, 2020 05:09
-
-
Save BekaValentine/b2afc67ec965a930006d6e7159662972 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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