Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Change `.getASubclass()` on `API::Node` so it allows to follow subclasses even if the class has a class decorator.
12 changes: 10 additions & 2 deletions python/ql/lib/semmle/python/ApiGraphs.qll
Original file line number Diff line number Diff line change
Expand Up @@ -680,8 +680,16 @@ module API {
or
// Subclassing a node
lbl = Label::subclass() and
exists(DataFlow::Node superclass | pred.flowsTo(superclass) |
ref.asExpr().(PY::ClassExpr).getABase() = superclass.asExpr()
exists(PY::ClassExpr clsExpr, DataFlow::Node superclass | pred.flowsTo(superclass) |
clsExpr.getABase() = superclass.asExpr() and
// Potentially a class decorator could do anything, but we assume they are
// "benign" and let subclasses edges flow through anyway.
// see example in https://github.com/django/django/blob/c2250cfb80e27cdf8d098428824da2800a18cadf/tests/auth_tests/test_views.py#L40-L46
(
ref.asExpr() = clsExpr
or
ref.asExpr() = clsExpr.getADecoratorCall()
)
)
or
// awaiting
Expand Down
5 changes: 4 additions & 1 deletion python/ql/test/experimental/dataflow/typetracking/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ def foo():
also_x = foo() # $ MISSING: tracked
print(also_x) # $ MISSING: tracked

# ------------------------------------------------------------------------------
# Function decorator
# ------------------------------------------------------------------------------

def my_decorator(func):
# This part doesn't make any sense in a normal decorator, but just shows how we
Expand Down Expand Up @@ -135,7 +138,7 @@ def meth3(self):
def track_self(self): # $ tracked_self
self.meth1() # $ tracked_self
super().meth2()
super(Bar, self).foo3() # $ tracked_self
super(Bar, self).meth3() # $ tracked_self

# ------------------------------------------------------------------------------
# Tracking of attribute lookup after "long" import chain
Expand Down
17 changes: 16 additions & 1 deletion python/ql/test/library-tests/ApiGraphs/py3/deftest2.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,19 @@ class IntMyView(View): #$ use=moduleImport("pflask").getMember("views").getMembe
def my_internal_method(self): #$ def=moduleImport("pflask").getMember("views").getMember("View").getASubclass().getMember("my_internal_method")
pass

int_instance = IntMyView() #$ use=moduleImport("pflask").getMember("views").getMember("View").getASubclass().getReturn()
int_instance = IntMyView() #$ use=moduleImport("pflask").getMember("views").getMember("View").getASubclass().getReturn()

# ------------------------------------------------------------------------------
# Class decorator
# ------------------------------------------------------------------------------

def my_class_decorator(cls):
print("dummy decorator")
return cls

@my_class_decorator
class MyViewWithDecorator(View): #$ use=moduleImport("flask").getMember("views").getMember("View").getASubclass()
pass

class SubclassFromDecorated(MyViewWithDecorator): #$ use=moduleImport("flask").getMember("views").getMember("View").getASubclass().getASubclass()
pass