mirror of
https://github.com/github/codeql.git
synced 2026-05-05 21:55:19 +02:00
Update init calls subclass to not use pointto
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
#Superclass __init__ calls subclass method
|
||||
|
||||
class Super(object):
|
||||
|
||||
def __init__(self, arg):
|
||||
self._state = "Not OK"
|
||||
self.set_up(arg)
|
||||
self._state = "OK"
|
||||
|
||||
def set_up(self, arg):
|
||||
"Do some set up"
|
||||
|
||||
class Sub(Super):
|
||||
|
||||
def __init__(self, arg):
|
||||
Super.__init__(self, arg)
|
||||
self.important_state = "OK"
|
||||
|
||||
def set_up(self, arg):
|
||||
Super.set_up(self, arg)
|
||||
"Do some more set up" # Dangerous as self._state is "Not OK"
|
||||
|
||||
|
||||
#Improved version with inheritance:
|
||||
|
||||
class Super(object):
|
||||
|
||||
def __init__(self, arg):
|
||||
self._state = "Not OK"
|
||||
self.super_set_up(arg)
|
||||
self._state = "OK"
|
||||
|
||||
def super_set_up(self, arg):
|
||||
"Do some set up"
|
||||
|
||||
|
||||
class Sub(Super):
|
||||
|
||||
def __init__(self, arg):
|
||||
Super.__init__(self, arg)
|
||||
self.sub_set_up(self, arg)
|
||||
self.important_state = "OK"
|
||||
|
||||
|
||||
def sub_set_up(self, arg):
|
||||
"Do some more set up"
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
When an instance of a class is initialized, the super-class state should be
|
||||
fully initialized before it becomes visible to the subclass.
|
||||
Calling methods of the subclass in the superclass' <code>__init__</code>
|
||||
method violates this important invariant.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>Do not use methods that are subclassed in the construction of an object.
|
||||
For simpler cases move the initialization into the superclass' <code>__init__</code> method,
|
||||
preventing it being overridden. Additional initialization of subclass should
|
||||
be done in the <code>__init__</code> method of the subclass.
|
||||
For more complex cases, it is advisable to use a static method or function to manage
|
||||
object creation.
|
||||
</p>
|
||||
|
||||
<p>Alternatively, avoid inheritance altogether using composition instead.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<sample src="InitCallsSubclassMethod.py" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
|
||||
<li>CERT Secure Coding: <a href="https://www.securecoding.cert.org/confluence/display/java/MET05-J.+Ensure+that+constructors+do+not+call+overridable+methods">
|
||||
Rule MET05-J</a>. Although this is a Java rule it applies to most object-oriented languages.</li>
|
||||
<li>Python Standard Library: <a href="http://docs.python.org/library/functions.html#staticmethod">Static methods</a>.</li>
|
||||
<li>Wikipedia: <a href="http://en.wikipedia.org/wiki/Composition_over_inheritance">Composition over inheritance</a>.</li>
|
||||
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* @name `__init__` method calls overridden method
|
||||
* @description Calling a method from `__init__` that is overridden by a subclass may result in a partially
|
||||
* initialized instance being observed.
|
||||
* @kind problem
|
||||
* @tags reliability
|
||||
* correctness
|
||||
* @problem.severity warning
|
||||
* @sub-severity low
|
||||
* @precision high
|
||||
* @id py/init-calls-subclass
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.internal.DataFlowDispatch
|
||||
|
||||
predicate initSelfCall(Function init, DataFlow::MethodCallNode call) {
|
||||
init.isInitMethod() and
|
||||
call.getScope() = init and
|
||||
exists(DataFlow::Node self, DataFlow::ParameterNode selfArg |
|
||||
call.calls(self, _) and
|
||||
selfArg.getParameter() = init.getArg(0) and
|
||||
DataFlow::localFlow(selfArg, self)
|
||||
)
|
||||
}
|
||||
|
||||
predicate initSelfCallOverridden(Function init, DataFlow::MethodCallNode call, Function override) {
|
||||
initSelfCall(init, call) and
|
||||
exists(Class superclass, Class subclass |
|
||||
superclass = init.getScope() and
|
||||
subclass = override.getScope() and
|
||||
subclass = getADirectSubclass+(superclass) and
|
||||
call.calls(_, override.getName())
|
||||
)
|
||||
}
|
||||
|
||||
from Function init, DataFlow::MethodCallNode call, Function override
|
||||
where initSelfCallOverridden(init, call, override)
|
||||
select call,
|
||||
"This call to " + override.getName() + " in initialization method is overridden by " +
|
||||
override.getScope().getName() + ".$@.", override, override.getName()
|
||||
Reference in New Issue
Block a user