QL tests for Python queries and libraries.

This commit is contained in:
Mark Shannon
2018-11-19 15:15:54 +00:00
parent 90c75cd362
commit 05b69a1c0f
1140 changed files with 32676 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
| wrong_arguments.py:65:1:65:7 | F0() | Keyword argument 'y' is not a supported parameter name of $@. | wrong_arguments.py:4:5:4:26 | Function __init__ | F0.__init__ |
| wrong_arguments.py:66:1:66:7 | F1() | Keyword argument 'z' is not a supported parameter name of $@. | wrong_arguments.py:8:5:8:36 | Function __init__ | F1.__init__ |
| wrong_arguments.py:67:1:67:12 | F2() | Keyword argument 'y' is not a supported parameter name of $@. | wrong_arguments.py:12:5:12:30 | Function __init__ | F2.__init__ |
| wrong_arguments.py:92:1:92:27 | F6() | Keyword argument 'z' is not a supported parameter name of $@. | wrong_arguments.py:28:5:28:30 | Function __init__ | F6.__init__ |

View File

@@ -0,0 +1 @@
Classes/WrongNameForArgumentInClassInstantiation.ql

View File

@@ -0,0 +1,15 @@
| wrong_arguments.py:37:1:37:4 | F0() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:4:5:4:26 | Function __init__ | F0.__init__ |
| wrong_arguments.py:38:1:38:4 | F1() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:8:5:8:36 | Function __init__ | F1.__init__ |
| wrong_arguments.py:39:1:39:4 | F2() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:12:5:12:30 | Function __init__ | F2.__init__ |
| wrong_arguments.py:40:1:40:4 | F3() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:16:5:16:40 | Function __init__ | F3.__init__ |
| wrong_arguments.py:41:1:41:4 | F4() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:20:5:20:31 | Function __init__ | F4.__init__ |
| wrong_arguments.py:42:1:42:4 | F5() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:24:5:24:42 | Function __init__ | F5.__init__ |
| wrong_arguments.py:43:1:43:5 | F6() | Call to $@ with too few arguments; should be no fewer than 2. | wrong_arguments.py:28:5:28:30 | Function __init__ | F6.__init__ |
| wrong_arguments.py:44:1:44:7 | F7() | Call to $@ with too few arguments; should be no fewer than 3. | wrong_arguments.py:32:5:32:33 | Function __init__ | F7.__init__ |
| wrong_arguments.py:48:1:48:7 | F0() | Call to $@ with too many arguments; should be no more than 1. | wrong_arguments.py:4:5:4:26 | Function __init__ | F0.__init__ |
| wrong_arguments.py:49:1:49:9 | F1() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:8:5:8:36 | Function __init__ | F1.__init__ |
| wrong_arguments.py:50:1:50:9 | F5() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:24:5:24:42 | Function __init__ | F5.__init__ |
| wrong_arguments.py:51:1:51:9 | F6() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:28:5:28:30 | Function __init__ | F6.__init__ |
| wrong_arguments.py:52:1:52:11 | F6() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:28:5:28:30 | Function __init__ | F6.__init__ |
| wrong_arguments.py:85:1:85:12 | F6() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:28:5:28:30 | Function __init__ | F6.__init__ |
| wrong_arguments.py:86:1:86:7 | F6() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:28:5:28:30 | Function __init__ | F6.__init__ |

View File

@@ -0,0 +1 @@
Classes/WrongNumberArgumentsInClassInstantiation.ql

View File

@@ -0,0 +1,93 @@
# Test cases corresponding to /Expressions/Arguments/wrong_arguments.py
class F0(object):
def __init__(self, x):
pass
class F1(object):
def __init__(self, x, y = None):
pass
class F2(object):
def __init__(self, x, *y):
pass
class F3(object):
def __init__(self, x, y = None, *z):
pass
class F4(object):
def __init__(self, x, **y):
pass
class F5(object):
def __init__(self, x, y = None, **z):
pass
class F6(object):
def __init__(self, x, y):
pass
class F7(object):
def __init__(self, x, y, z):
pass
# Too few arguments
F0()
F1()
F2()
F3()
F4()
F5()
F6(1)
F7(1,2)
#Too many arguments
F0(1,2)
F1(1,2,3)
F5(1,2,3)
F6(1,2,3)
F6(1,2,3,4)
#OK
#Not too few
F7(*t)
#Not too many
F2(1,2,3,4,5,6)
#Illegal name
F0(y=1)
F1(z=1)
F2(x=0, y=1)
#Ok name
F0(x=0)
F1(x=0, y=1)
F4(q=4)
#This is correct, but a bit weird.
F6(**{'x':1, 'y':2})
t2 = (1,2)
t3 = (1,2,3)
#Ok
f(*t2)
#Too many
F6(*(1,2,3))
F6(*t3)
#Ok
F6(**{'x':1, 'y':2})
#Illegal name
F6(**{'x':1, 'y':2, 'z':3})

View File

@@ -0,0 +1,2 @@
| test.py:26:1:26:25 | class Conflict | Base classes have conflicting values for attribute 'attr': $@ and $@. | file://:Compiled Code:0:0:0:0 | int 1 | int 1 | test.py:20:13:20:16 | Tuple | Tuple |
| test.py:26:1:26:25 | class Conflict | Base classes have conflicting values for attribute 'meth': $@ and $@. | test.py:14:5:14:19 | Function meth | Function meth | test.py:22:5:22:19 | Function meth | Function meth |

View File

@@ -0,0 +1 @@
Classes/ConflictingAttributesInBaseClasses.ql

View File

@@ -0,0 +1,17 @@
#This code has conflicting attributes,
#but the documentation in the standard library tells you do it this way :(
#See https://discuss.lgtm.com/t/warning-on-normal-use-of-python-socketserver-mixins/677
class ThreadingMixIn(object):
def process_request(selfself, req):
pass
class HTTPServer(object):
def process_request(selfself, req):
pass
class _ThreadingSimpleServer(ThreadingMixIn, HTTPServer):
pass

View File

@@ -0,0 +1,52 @@
#Conflicting attributes in base classes
class Common(object):
ok1 = None
def ok2(self):
return None
class CB1(Common):
attr = 1
def meth(self):
pass
class CB2(Common):
attr = (x, y)
def meth(self):
return 0
class Conflict(CB1, CB2):
pass
class Override1(Common):
def ok2(self):
return 1
class Override2(Common):
def ok2(self):
return 2
class OK1(Override1, Override2):
def ok2(self):
return 3
class Override3(Override2):
pass
class OK2(Override1, Override3):
def ok2(self):
return 4

View File

@@ -0,0 +1 @@
| test.py:10:9:10:19 | Attribute | Mutation of descriptor $@ object may lead to action-at-a-distance effects or race conditions for properties. | test.py:3:1:3:33 | class MutatingDescriptor | MutatingDescriptor |

View File

@@ -0,0 +1 @@
Classes/MutatingDescriptor.ql

View File

@@ -0,0 +1,14 @@
#This is prone to strange side effects and race conditions.
class MutatingDescriptor(object):
def __init__(self, func):
self.my_func = func
def __get__(self, obj, obj_type):
#Modified state is visible to all instances of C that might call "show".
self.my_obj = obj
return self
def __call__(self, *args):
return self.my_func(self.my_obj, *args)

View File

@@ -0,0 +1 @@
Classes/DefineEqualsWhenAddingAttributes.ql

View File

@@ -0,0 +1,16 @@
class GenericEquality(object):
def __eq__(self, other):
if type(other) is not type(self):
return False
for attr in self.__dict__:
if getattr(other, attr) != getattr(self, attr):
return False
return True
class AddAttributes(GenericEquality):
def __init__(self, args):
self.a, self.b = args

View File

@@ -0,0 +1 @@
| attr_eq_test.py:21:1:21:27 | class BadColorPoint | The class 'BadColorPoint' does not override $@, but adds the new attribute $@. | attr_eq_test.py:10:5:10:28 | Function __eq__ | '__eq__' | attr_eq_test.py:25:9:25:19 | Attribute | _color |

View File

@@ -0,0 +1 @@
Classes/DefineEqualsWhenAddingAttributes.ql

View File

@@ -0,0 +1,107 @@
class Point(object):
def __init__(self, x, y):
self._x = x
self._y = y
def __repr__(self):
return 'Point(%r, %r)' % (self._x, self._y)
def __eq__(self, other):
if not isinstance(other, Point):
return False
return self._x == other._x and self._y == other._y
def __ne__(self, other):
return not self.__eq__(other)
def __hash__(self):
return hash((self._x, self._y))
class BadColorPoint(Point):
def __init__(self, x, y, color):
Point.__init__(self, x, y)
self._color = color
def __repr__(self):
return 'ColorPoint(%r, %r)' % (self._x, self._y, self._color)
class GoodColorPoint(Point):
def __init__(self, x, y, color):
Point.__init__(self, x, y)
self._color = color
def __repr__(self):
return 'ColorPoint(%r, %r)' % (self._x, self._y, self._color)
def __eq__(self, other):
if not isinstance(other, GoodColorPoint):
return False
return Point.__eq__(self, other) and self._color == other._color
def __ne__(self, other):
return not self.__eq__(other)
def __hash__(self):
return hash((self._x, self._y, self._color))
class GenericPoint(object):
def __init__(self, x, y):
self._x = x
self._y = y
def __repr__(self):
return 'Point(%r, %r)' % (self._x, self._y)
def __eq__(self, other):
return self.__class__ == other.__class__ and self.__dict__ == other.__dict__
def __ne__(self, other):
return not self.__eq__(other)
def __hash__(self):
return hash((self._x, self._y))
class GoodGenericColorPoint(GenericPoint):
def __init__(self, x, y, color):
GenericPoint.__init__(self, x, y)
self._color = color
class RedefineEq(object):
def __init__(self, x, y):
self._x = x
self._y = y
def __eq__(self, other):
return self is other
class OK1(RedefineEq):
def __init__(self, x, y, z):
RedefineEq.__init__(self, x, y)
self.z = z
class OK2(GenericPoint):
def __init__(self, x, y, color):
GenericPoint.__init__(self, x, y)
self._color = color
def __eq__(self, other):
return self is other
class ExpectingAttribute(object):
def __eq__(self, other):
return self.x == other.x
class OK3(ExpectingAttribute):
def __init__(self):
self.x = 4

View File

@@ -0,0 +1 @@
| incomplete_ordering.py:3:1:3:26 | class PartOrdered | Class PartOrdered implements $@, but does not implement __le__ or __gt__ or __ge__. | incomplete_ordering.py:13:5:13:28 | Function __lt__ | __lt__ |

View File

@@ -0,0 +1 @@
Classes/IncompleteOrdering.ql

View File

@@ -0,0 +1,18 @@
#Incomplete ordering
class PartOrdered(object):
def __eq__(self, other):
return self is other
def __ne__(self, other):
return self is not other
def __hash__(self):
return id(self)
def __lt__(self, other):
return False
#Don't blame a sub-class for super-class's sins.
class DerivedPartOrdered(PartOrdered):
pass

View File

@@ -0,0 +1 @@
| init_calls_subclass.py:7:9:7:24 | Attribute() | Call to self.$@ in __init__ method, which is overridden by $@. | init_calls_subclass.py:10:5:10:26 | Function set_up | set_up | init_calls_subclass.py:19:5:19:26 | Function set_up | method Sub.set_up |

View File

@@ -0,0 +1 @@
Classes/InitCallsSubclassMethod.ql

View File

@@ -0,0 +1,22 @@
#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" and
# self.important_state is uninitialized

View File

@@ -0,0 +1 @@
| missing_del.py:12:1:12:13 | class X3 | Class X3 may not be cleaned up properly as $@ is not called during deletion. | missing_del.py:9:5:9:22 | Function __del__ | method X2.__del__ |

View File

@@ -0,0 +1 @@
Classes/MissingCallToDel.ql

View File

@@ -0,0 +1,15 @@
#Not calling an __del__ method:
class X1(object):
def __del__(self):
pass
class X2(X1):
def __del__(self):
X1.__del__(self)
class X3(X2):
def __del__(self):
X1.__del__(self)

View File

@@ -0,0 +1,3 @@
| missing_init.py:12:1:12:13 | class B3 | Class B3 may not be initialized properly as $@ is not called from its $@. | missing_init.py:9:5:9:23 | Function __init__ | method B2.__init__ | missing_init.py:14:5:14:23 | Function __init__ | __init__ method |
| missing_init.py:39:1:39:21 | class IUVT | Class IUVT may not be initialized properly as $@ is not called from its $@. | missing_init.py:30:5:30:23 | Function __init__ | method UT.__init__ | missing_init.py:26:5:26:23 | Function __init__ | __init__ method |
| missing_init.py:72:1:72:13 | class AB | Class AB may not be initialized properly as $@ is not called from its $@. | missing_init.py:69:5:69:23 | Function __init__ | method AA.__init__ | missing_init.py:75:5:75:23 | Function __init__ | __init__ method |

View File

@@ -0,0 +1 @@
Classes/MissingCallToInit.ql

View File

@@ -0,0 +1,185 @@
#Not calling an __init__ method:
class B1(object):
def __init__(self):
do_something()
class B2(B1):
def __init__(self):
B1.__init__(self)
class B3(B2):
def __init__(self):
B1.__init__(self)
#OK if superclass __init__ is builtin as
#builtin classes tend to rely on __new__
class MyException(Exception):
def __init__(self):
self.message = "Uninformative"
#ODASA-4107
class IUT(object):
def __init__(self):
print("IUT init")
class UT(object):
def __init__(self):
print("UT init")
class PU(object):
pass
class UVT(UT, PU):
pass
class IUVT(IUT, UVT):
pass
#False positive observed on LGTM
class M1(object):
def __init__(self):
print("A")
class M2(object):
pass
class Mult(M2, M1):
def __init__(self):
super(Mult, self).__init__() # Calls M1.__init__
class X:
def __init__(self):
do_something()
class Y(X):
@decorated
def __init__(self):
X.__init__(self)
class Z(Y):
def __init__(self):
Y.__init__(self)
class AA(object):
def __init__(self):
do_something()
class AB(AA):
#Don't call super class init
def __init__(self):
do_something()
class AC(AB):
def __init__(self):
#Missing call to AA.__init__ but not AC's fault.
super(AC, self).__init__()
import six
import abc
class BA(object):
def __init__(self):
do_something()
@six.add_metaclass(abc.ABCMeta)
class BB(BA):
def __init__(self):
super(BB,self).__init__()
@six.add_metaclass(abc.ABCMeta)
class CA(object):
def __init__(self):
do_something()
class CB(BA):
def __init__(self):
super(CB,self).__init__()
#ODASA-5799
class DA(object):
def __init__(self):
do_something()
class DB(DA):
class DC(DA):
def __init__(self):
sup = super(DB.DC, self)
sup.__init__()
#Simpler variants
class DD(DA):
def __init__(self):
sup = super(DD, self)
sup.__init__()
class DE(DA):
class DF(DA):
def __init__(self):
sup = super(DE.DF, self).__init__()
class FA(object):
def __init__(self):
pass
class FB(object):
def __init__(self):
do_something()
class FC(FA, FB):
def __init__(self):
#OK to skip call to FA.__init__ as that does nothing.
FB.__init__(self)
#Potential false positives.
class ConfusingInit(B1):
def __init__(self):
super_call = super(ConfusingInit, self).__init__
super_call()
# Library class
import collections
class G1(collections.Counter):
def __init__(self):
collections.Counter.__init__(self)
class G2(G1):
def __init__(self):
super(G2, self).__init__()
class G3(collections.Counter):
def __init__(self):
super(G3, self).__init__()
class G4(G3):
def __init__(self):
G3.__init__(self)

View File

@@ -0,0 +1,2 @@
| multiple_del.py:17:1:17:17 | class Y3 | Class Y3 may not be cleaned up properly as $@ may be called multiple times during destruction. | multiple_del.py:9:5:9:22 | Function __del__ | method Y1.__del__ |
| multiple_del.py:34:1:34:17 | class Z3 | Class Z3 may not be cleaned up properly as $@ may be called multiple times during destruction. | multiple_del.py:26:5:26:22 | Function __del__ | method Z1.__del__ |

View File

@@ -0,0 +1 @@
Classes/SuperclassDelCalledMultipleTimes.ql

View File

@@ -0,0 +1,2 @@
| multiple_init.py:17:1:17:17 | class C3 | Class C3 may not be initialized properly as $@ may be called multiple times during initialization. | multiple_init.py:9:5:9:23 | Function __init__ | method C1.__init__ |
| multiple_init.py:34:1:34:17 | class D3 | Class D3 may not be initialized properly as $@ may be called multiple times during initialization. | multiple_init.py:26:5:26:23 | Function __init__ | method D1.__init__ |

View File

@@ -0,0 +1 @@
Classes/SuperclassInitCalledMultipleTimes.ql

View File

@@ -0,0 +1,38 @@
#Calling a method multiple times by using explicit calls when a base uses super()
class Base(object):
def __del__(self):
pass
class Y1(Base):
def __del__(self):
super(Y1, self).__del__()
class Y2(Base):
def __del__(self):
super(Y2, self).__del__() #When `type(self) == Y3` this calls `Y1.__del__`
class Y3(Y2, Y1):
def __del__(self):
Y1.__del__(self)
Y2.__del__(self)
#Calling a method multiple times by using explicit calls when a base inherits from other base
class Z1(object):
def __del__(self):
pass
class Z2(Z1):
def __del__(self):
Z1.__del__(self)
class Z3(Z2, Z1):
def __del__(self):
Z1.__del__(self)
Z2.__del__(self)

View File

@@ -0,0 +1,76 @@
#Calling a method multiple times by using explicit calls when a base uses super()
class Base(object):
def __init__(self):
pass
class C1(Base):
def __init__(self):
super(C1, self).__init__()
class C2(Base):
def __init__(self):
super(C2, self).__init__() #When `type(self) == C3` this calls `C1.__init__`
class C3(C2, C1):
def __init__(self):
C1.__init__(self)
C2.__init__(self)
#Calling a method multiple times by using explicit calls when a base inherits from other base
class D1(object):
def __init__(self):
pass
class D2(D1):
def __init__(self):
D1.__init__(self)
class D3(D2, D1):
def __init__(self):
D1.__init__(self)
D2.__init__(self)
#OK to call object.__init__ multiple times
class E1(object):
def __init__(self):
super(E1, self).__init__()
class E2(object):
def __init__(self):
object.__init__(self)
class E3(E2, E1):
def __init__(self):
E1.__init__(self)
E2.__init__(self)
#Two invocations, but can only be called once
class F1(Base):
def __init__(self, cond):
if cond:
Base.__init__(self)
else:
Base.__init__(self)
#Single call, splitting causes what seems to be multiple invocations.
class F2(Base):
def __init__(self, cond):
if cond:
pass
if cond:
pass
Base.__init__(self)

View File

@@ -0,0 +1,2 @@
| overwriting_attribute.py:5:9:5:20 | AssignStmt | Assignment overwrites attribute var, which was previously defined in subclass $@. | overwriting_attribute.py:10:9:10:20 | AssignStmt | D |
| overwriting_attribute.py:23:9:23:20 | AssignStmt | Assignment overwrites attribute var, which was previously defined in superclass $@. | overwriting_attribute.py:17:9:17:20 | AssignStmt | E |

View File

@@ -0,0 +1 @@
Classes/OverwritingAttributeInSuperClass.ql

View File

@@ -0,0 +1,23 @@
#Attribute set in both superclass and subclass
class C(object):
def __init__(self):
self.var = 0
class D(C):
def __init__(self):
self.var = 1 # self.var will be overwritten
C.__init__(self)
#Attribute set in both superclass and subclass
class E(object):
def __init__(self):
self.var = 0 # self.var will be overwritten
class F(E):
def __init__(self):
E.__init__(self)
self.var = 1

View File

@@ -0,0 +1,2 @@
| should_be_context_manager.py:3:1:3:22 | class MegaDel | Class MegaDel implements __del__ (presumably to release some resource). Consider making it a context manager. |
| should_be_context_manager.py:16:1:16:22 | class MiniDel | Class MiniDel implements __del__ (presumably to release some resource). Consider making it a context manager. |

View File

@@ -0,0 +1 @@
Classes/ShouldBeContextManager.ql

View File

@@ -0,0 +1,22 @@
#Should be context manager
class MegaDel(object):
def __del__(self):
a = self.x + self.y
if a:
print(a)
if sys._getframe().f_lineno > 100:
print("Hello")
sum = 0
for a in range(100):
sum += a
print(sum)
class MiniDel(object):
def close(self):
pass
def __del__(self):
self.close()

View File

@@ -0,0 +1 @@
| subclass_shadowing.py:10:5:10:21 | FunctionExpr | Method shadow is shadowed by $@ in super class 'Base'. | subclass_shadowing.py:6:9:6:23 | AssignStmt | an attribute |

View File

@@ -0,0 +1 @@
Classes/SubclassShadowing.ql

View File

@@ -0,0 +1,30 @@
#Subclass shadowing
class Base(object):
def __init__(self):
self.shadow = 4
class Derived(Base):
def shadow(self):
pass
#OK if the super class defines the method as well.
#Since the original method must exist for some reason.
#See JSONEncoder.default for real example
class Base2(object):
def __init__(self, shadowy=None):
if shadowy:
self.shadow = shadowy
def shadow(self):
pass
class Derived2(Base2):
def shadow(self):
return 0

View File

@@ -0,0 +1,4 @@
| undefined_attribute.py:27:16:27:29 | Attribute | Attribute 'may_exist' is not defined in the class body nor in the __init__() method, but it is defined $@ | undefined_attribute.py:11:9:11:22 | Attribute | here |
| undefined_attribute.py:184:16:184:32 | Attribute | Attribute 'return_queue' is not defined in the class body nor in the __init__() method, but it is defined $@ | undefined_attribute.py:181:13:181:29 | Attribute | here |
| undefined_attribute.py:257:16:257:31 | Attribute | Attribute 'glance_host' is not defined in the class body nor in the __init__() method, but it is defined $@ | undefined_attribute.py:262:13:262:28 | Attribute | here |
| undefined_attribute.py:258:16:258:31 | Attribute | Attribute 'glance_port' is not defined in the class body nor in the __init__() method, but it is defined $@ | undefined_attribute.py:263:10:263:25 | Attribute | here |

View File

@@ -0,0 +1 @@
Classes/MaybeUndefinedClassAttribute.ql

View File

@@ -0,0 +1,4 @@
| undefined_attribute.py:24:16:24:30 | Attribute | Attribute 'not_exists' is not defined in either the class body or in any method |
| undefined_attribute.py:109:16:109:21 | Attribute | Attribute 'y' is not defined in either the class body or in any method |
| undefined_attribute.py:250:16:250:31 | Attribute | Attribute 'glance_host' is not defined in either the class body or in any method |
| undefined_attribute.py:251:16:251:31 | Attribute | Attribute 'glance_port' is not defined in either the class body or in any method |

View File

@@ -0,0 +1 @@
Classes/UndefinedClassAttribute.ql

View File

@@ -0,0 +1,263 @@
#Non existent class attribute
class Attributes(object):
exists1 = 1
def __init__(self):
self.exists2 = 2
def method(self):
self.may_exist = 3
def ok1(self):
print (self.exists1)
def ok2(self):
print (self.exists2)
def ok3(self):
self.local_exists = 4
print (self.local_exists)
def neca1(self):
print (self.not_exists)
def neca2(self):
print (self.may_exist)
#This is OK
class SetViaDict(object):
def __init__(self, x):
self.__dict__['x'] = x
def use_x(self):
return self.x
#This is also OK
class SetLocally(object):
def use_x(self):
self.x = 1
return self.x
class UsesSetattr(object):
def __init__(self, vals):
for k, v in vals.items():
setattr(self, k, v)
def use_values(self):
return self.x, self.y, self.z
#OK
class GuardedByHasAttr(object):
def ok4(self):
if hasattr(self, "x"):
return self.x
else:
return None
class HasGetAttr(object):
def __getattr__(self, name):
return name
def use_values(self):
return self.x, self.y, self.z
class HasGetAttribute(object):
def __getattribute__(self, name):
return name
def use_values(self):
return self.x, self.y, self.z
class DecoratedInit(object):
@decorator
def __init__(self):
self.x = x
def use(self):
return self.x
#This is not OK
class NoInit(object):
def use_y(self):
return self.y
#This is also OK
class SetLocally2(object):
def __init__(self):
pass
def use_y(self):
self.x = 0
self.y = 1
if cond:
print(self.y)
else:
return False
return self.y
#Guarded
class Guarded(object):
def __init__(self):
self.guard = False
def set_x(self):
self.guard = True
self.x = 1
def use_x(self):
if self.guard:
return self.x
else:
return 0
#ODASA-2034
class ODASA2034A(object):
def __init__(self, data):
d = self.__dict__
d['data'] = data
def use_data(self):
return self.data
class ODASA2034B(object):
def __init__(self, key, value):
d = self.__dict__
d[key] = value
def use_data(self):
return self.data
class Test5(object):
def readFromStream(stream):
word = stream.read(4)
if word == "true":
return BooleanObject(True)
elif word == "fals":
stream.read(1)
return BooleanObject(False)
assert False
readFromStream = staticmethod(readFromStream)
class Test1(object):
def __init__(self, application):
self.rabbitmq_channel = None
def queue_declared(frame): # called in callback
self.return_queue = frame.method.queue
def use_it(self):
return self.return_queue
#Check for FPs when overriding builtin methods
class PrintingDict(dict):
def pop(self):
print ("pop")
return dict.pop(self)
def __setitem__(self, key, value):
print("__setitem__")
return dict.__setitem__(self, key, value)
#Locally set by Call
#ODASA-4612
class WSGIContext(object):
def _app_call(self, env):
self._response_headers = None
class Odasa4612(WSGIContext):
def meth1(self, arg):
val = self._app_call(arg)
if self._response_headers is None:
self._response_headers = []
class OK9(object):
cls_attr = 0
def __init__(self):
self.attr = self.cls_attr
class Foo1(object):
pass
foo = Foo1()
setattr(foo, 'a', 1)
assert foo.a == 1
class Foo2(object):
pass
setattr(Foo2, 'a', 1)
assert Foo2.a == 1
# False positive observed at customer
class Customer1(object):
def x(self):
if not hasattr(self, "attr"):
return None
else:
return self.attr
#ODASA-4619
class Odasa4619a(object):
def call(self):
host = self.glance_host
port = self.glance_port
class Odasa4619b(object):
def call(self):
host = self.glance_host
port = self.glance_port
@decorator
def foo(self):
(x, self.glance_host,
self.glance_port, y) = bar()

View File

@@ -0,0 +1,2 @@
| test.py:28:1:28:23 | Class Useless1 | Class Useless1 defines only one public method, which should be replaced by a function. |
| test.py:37:1:37:23 | Class Useless2 | Class Useless2 defines only one public method, which should be replaced by a function. |

View File

@@ -0,0 +1 @@
Classes/UselessClass.ql

View File

@@ -0,0 +1,65 @@
#Useless class
class Useful1(object):
def __init__(self):
pass
def do_something(self):
pass
def do_something_else(self):
pass
def do_yet_another_thing(self):
pass
class Useful2(object):
def do_something(self):
pass
def do_something_else(self):
pass
class Useless1(object):
def __init__(self):
pass
def do_something(self):
pass
class Useless2(object):
def do_something(self):
pass
class Stateful1(object):
def __init__(self):
self.data = []
def do_something(self, x):
self.data.append(x)
class Stateful2(object):
def __init__(self):
self.data = None
def do_something(self, x):
self.data = x
class Inherited(object):
pass
class Inherits(Inherited):
def do_something(self):
pass

View File

@@ -0,0 +1,2 @@
| exceptions_test.py:7:5:7:11 | ExceptStmt | Except block directly handles BaseException. |
| exceptions_test.py:71:5:71:25 | ExceptStmt | Except block directly handles BaseException. |

View File

@@ -0,0 +1 @@
Exceptions/CatchingBaseException.ql

View File

@@ -0,0 +1,2 @@
| exceptions_test.py:7:5:7:11 | ExceptStmt | 'except' clause does nothing but pass and there is no explanatory comment. |
| exceptions_test.py:13:5:13:21 | ExceptStmt | 'except' clause does nothing but pass and there is no explanatory comment. |

View File

@@ -0,0 +1 @@
Exceptions/EmptyExcept.ql

View File

@@ -0,0 +1,4 @@
| exceptions_test.py:51:5:51:25 | ExceptStmt | Non-exception $@ in exception handler which will never match raised exception. | exceptions_test.py:33:1:33:28 | ControlFlowNode for ClassExpr | class 'NotException1' |
| exceptions_test.py:54:5:54:25 | ExceptStmt | Non-exception $@ in exception handler which will never match raised exception. | exceptions_test.py:36:1:36:28 | ControlFlowNode for ClassExpr | class 'NotException2' |
| exceptions_test.py:112:5:112:22 | ExceptStmt | Non-exception $@ in exception handler which will never match raised exception. | exceptions_test.py:107:12:107:14 | ControlFlowNode for FloatLiteral | instance of 'float' |
| pypy_test.py:14:5:14:14 | ExceptStmt | Non-exception $@ in exception handler which will never match raised exception. | pypy_test.py:14:12:14:13 | ControlFlowNode for IntegerLiteral | instance of 'int' |

View File

@@ -0,0 +1 @@
Exceptions/IllegalExceptionHandlerType.ql

View File

@@ -0,0 +1,3 @@
| exceptions_test.py:40:5:40:23 | Raise | Illegal class 'NotException1' raised; will result in a TypeError being raised instead. |
| exceptions_test.py:43:5:43:21 | Raise | Illegal class 'str' raised; will result in a TypeError being raised instead. |
| exceptions_test.py:46:5:46:25 | Raise | Illegal class 'NotException2' raised; will result in a TypeError being raised instead. |

View File

@@ -0,0 +1 @@
Exceptions/IllegalRaise.ql

View File

@@ -0,0 +1 @@
| exceptions_test.py:64:1:64:22 | ExceptStmt | Except block for $@ is unreachable; the more general $@ for $@ will always be executed in preference. | file://:Compiled Code:0:0:0:0 | builtin-class AttributeError | AttributeError | exceptions_test.py:62:1:62:17 | ExceptStmt | except block | file://:Compiled Code:0:0:0:0 | builtin-class Exception | Exception |

View File

@@ -0,0 +1 @@
Exceptions/IncorrectExceptOrder.ql

View File

@@ -0,0 +1,2 @@
| exceptions_test.py:170:11:170:24 | NotImplemented | NotImplemented is not an Exception. Did you mean NotImplementedError? |
| exceptions_test.py:173:11:173:24 | NotImplemented | NotImplemented is not an Exception. Did you mean NotImplementedError? |

View File

@@ -0,0 +1 @@
Exceptions/NotImplementedIsNotAnException.ql

View File

@@ -0,0 +1,173 @@
#Empty Except
def ee1(val):
try:
val.attr
except:
pass
def ee1(val):
try:
val.attr()
except TypeError:
pass
def ee2(val):
try:
val.attr
except Error:
#But it is OK if there is a comment
pass
#OK with an else clause as well...
def ee3(val):
try:
val.attr
except Error:
pass
else:
return 42
class NotException1(object):
pass
class NotException2(object):
pass
def illegal_raise_type():
raise NotException1
def illegal_raise_value1():
raise "Exception"
def illegal_raise_value2():
raise NotException2()
def illegal_handler():
try:
illegal_raise()
except NotException1:
#Must do something
print("NotException1")
except NotException2:
#Must do something
print("NotException2")
#Incorrect except order
try:
val.attr
except Exception:
print (2)
except AttributeError:
print (3)
#Catch BaseException
def catch_base_exception():
try:
illegal_raise()
except BaseException:
#Consumes KeyboardInterrupt
pass
def catch_base_exception_ok():
try:
illegal_raise()
except BaseException:
raise
def legal_handler1():
try:
illegal_raise()
except (IOError, KeyError):
print ("Caught exception")
pair = IOError, KeyError
triple = pair, AttributeError
def legal_handler2():
try:
illegal_raise()
except pair:
print ("Caught exception")
try:
illegal_raise()
except triple:
print ("Caught exception")
def legal_handler3():
try:
illegal_raise()
except could_be_anything():
print ("Caught exception")
def a_number():
return 4.0
def illegal_handler2():
try:
illegal_raise()
except a_number():
print ("Caught exception")
def stop_iter_ok(seq):
try:
next(seq)
except StopIteration:
pass
#Guarded None in nested function
def f(x=None):
def inner(arg):
if x:
raise x
#ODASA-4705
def g(cond):
try:
if cond:
return may_raise_io_error()
else:
raise KeyError
except IOError:
pass # This is OK, as it is just passing to the following statement which handles the exception.
return 0
def ee4(x):
try:
del x.attr
except AttributeError:
pass
def ee5(x):
try:
x[0]
except IndexError:
pass
def ee6(x):
try:
del x[0]
except IndexError:
pass
def ee7(x):
try:
delattr(x, "attr")
except AttributeError:
pass
def ee8(x):
try:
x.encode("martian-18")
except UnicodeEncodeError:
pass
#These are so common, we give warnings not errors.
def foo():
raise NotImplemented
def bar():
raise NotImplemented()

View File

@@ -0,0 +1,20 @@
def test():
class A(BaseException):
class __metaclass__(type):
def __getattribute__(self, name):
if flag and name == '__bases__':
fail("someone read bases attr")
else:
return type.__getattribute__(self, name)
try:
a = A()
raise a
except 42:
#Some comment
pass
except A:
#Another comment
pass

View File

@@ -0,0 +1,2 @@
| test.py:5:15:5:22 | ControlFlowNode for next() | Call to next() in a generator |
| test.py:10:20:10:27 | ControlFlowNode for next() | Call to next() in a generator |

View File

@@ -0,0 +1 @@
Exceptions/UnguardedNextInGenerator.ql

View File

@@ -0,0 +1,49 @@
#Unguarded calls to next()
def bad1(it):
while True:
yield next(it)
def bad2(seq):
it = iter(seq)
#Not OK as seq may be empty
raise KeyError(next(it))
yield 0
def ok1(seq):
#Not a generator
it = iter(seq)
#Not OK as seq may be empty
raise KeyError(next(it))
def ok2(seq):
if seq:
it = iter(seq)
#OK seq is non-empty so next(it) will not raise StopIteration
raise KeyError(next(it))
yield 0
def explicit_raise_stop_iter(seq):
for i in seq:
yield seq
raise StopIteration()
def ok3(seq):
it = iter(seq)
try:
yield next(iter)
except StopIteration:
return
def ok4(seq, ctx):
try:
with ctx:
yield next(iter)
except StopIteration:
return
#ODASA-6536
def next_in_comp(seq, fields):
seq_iter = iter(seq)
values = [ next(seq_iter) if f.attname in NAMES else DEFAULT for f in fields ]
return values

View File

@@ -0,0 +1,5 @@
| wrong_arguments.py:57:1:57:7 | f0() | Keyword argument 'y' is not a supported parameter name of $@. | wrong_arguments.py:3:1:3:10 | Function f0 | function f0 |
| wrong_arguments.py:58:1:58:7 | f1() | Keyword argument 'z' is not a supported parameter name of $@. | wrong_arguments.py:6:1:6:20 | Function f1 | function f1 |
| wrong_arguments.py:59:1:59:12 | f2() | Keyword argument 'y' is not a supported parameter name of $@. | wrong_arguments.py:9:1:9:14 | Function f2 | function f2 |
| wrong_arguments.py:103:1:103:27 | f6() | Keyword argument 'z' is not a supported parameter name of $@. | wrong_arguments.py:21:1:21:13 | Function f6 | function f6 |
| wrong_arguments.py:115:1:115:13 | f1() | Keyword argument 'z' is not a supported parameter name of $@. | wrong_arguments.py:6:1:6:20 | Function f1 | function f1 |

View File

@@ -0,0 +1 @@
Expressions/WrongNameForArgumentInCall.ql

View File

@@ -0,0 +1,25 @@
| use_mox.py:28:1:28:4 | f0() | Call to $@ with too few arguments; should be no fewer than 1. | use_mox.py:7:1:7:10 | Function f0 | function f0 |
| use_mox.py:29:1:29:5 | f1() | Call to $@ with too few arguments; should be no fewer than 2. | use_mox.py:10:1:10:13 | Function f1 | function f1 |
| use_mox.py:32:1:32:8 | Attribute() | Call to $@ with too few arguments; should be no fewer than 1. | use_mox.py:15:5:15:20 | Function m0 | method C.m0 |
| use_mox.py:33:1:33:9 | Attribute() | Call to $@ with too few arguments; should be no fewer than 2. | use_mox.py:18:5:18:23 | Function m1 | method C.m1 |
| wrong_arguments.py:29:1:29:4 | f0() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:3:1:3:10 | Function f0 | function f0 |
| wrong_arguments.py:30:1:30:4 | f1() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:6:1:6:20 | Function f1 | function f1 |
| wrong_arguments.py:31:1:31:4 | f2() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:9:1:9:14 | Function f2 | function f2 |
| wrong_arguments.py:32:1:32:4 | f3() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:12:1:12:24 | Function f3 | function f3 |
| wrong_arguments.py:33:1:33:4 | f4() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:15:1:15:15 | Function f4 | function f4 |
| wrong_arguments.py:34:1:34:4 | f5() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:18:1:18:25 | Function f5 | function f5 |
| wrong_arguments.py:35:1:35:5 | f6() | Call to $@ with too few arguments; should be no fewer than 2. | wrong_arguments.py:21:1:21:13 | Function f6 | function f6 |
| wrong_arguments.py:36:1:36:7 | f7() | Call to $@ with too few arguments; should be no fewer than 3. | wrong_arguments.py:24:1:24:16 | Function f7 | function f7 |
| wrong_arguments.py:40:1:40:7 | f0() | Call to $@ with too many arguments; should be no more than 1. | wrong_arguments.py:3:1:3:10 | Function f0 | function f0 |
| wrong_arguments.py:41:1:41:9 | f1() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:6:1:6:20 | Function f1 | function f1 |
| wrong_arguments.py:42:1:42:9 | f5() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:18:1:18:25 | Function f5 | function f5 |
| wrong_arguments.py:43:1:43:9 | f6() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:21:1:21:13 | Function f6 | function f6 |
| wrong_arguments.py:44:1:44:11 | f6() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:21:1:21:13 | Function f6 | function f6 |
| wrong_arguments.py:81:1:81:5 | l0() | Call to $@ with too many arguments; should be no more than 0. | wrong_arguments.py:70:6:70:15 | Function lambda | function lambda |
| wrong_arguments.py:82:1:82:7 | l1() | Call to $@ with too many arguments; should be no more than 1. | wrong_arguments.py:71:6:71:21 | Function lambda | function lambda |
| wrong_arguments.py:83:1:83:8 | l1d() | Call to $@ with too many arguments; should be no more than 1. | wrong_arguments.py:72:7:72:25 | Function lambda | function lambda |
| wrong_arguments.py:86:1:86:4 | l1() | Call to $@ with too few arguments; should be no fewer than 1. | wrong_arguments.py:71:6:71:21 | Function lambda | function lambda |
| wrong_arguments.py:96:1:96:12 | f6() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:21:1:21:13 | Function f6 | function f6 |
| wrong_arguments.py:97:1:97:7 | f6() | Call to $@ with too many arguments; should be no more than 2. | wrong_arguments.py:21:1:21:13 | Function f6 | function f6 |
| wrong_arguments.py:130:1:130:9 | Attribute() | Call to $@ with too few arguments; should be no fewer than 2. | wrong_arguments.py:126:5:126:31 | Function spam | method Eggs2.spam |
| wrong_arguments.py:130:1:130:9 | Attribute() | Call to $@ with too many arguments; should be no more than 0. | wrong_arguments.py:121:5:121:19 | Function spam | method Eggs1.spam |

View File

@@ -0,0 +1 @@
Expressions/WrongNumberArgumentsInCall.ql

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,33 @@
import mox
#Use it
mox
def f0(x):
pass
def f1(x, y):
pass
class C(object):
def m0(self, x):
pass
def m1(self, x, y):
pass
# These are treated as magically OK since we are using mox
C.m0(1)
C.m1(1,2)
#But normal functions are treated normally
f0()
f1(1)
#As are normal methods
C().m0()
C().m1(1)

View File

@@ -0,0 +1,131 @@
#!/usr/bin/env python
def f0(x):
pass
def f1(x, y = None):
pass
def f2(x, *y):
pass
def f3(x, y = None, *z):
pass
def f4(x, **y):
pass
def f5(x, y = None, **z):
pass
def f6(x, y):
pass
def f7(x, y, z):
pass
# Too few arguments
f0()
f1()
f2()
f3()
f4()
f5()
f6(1)
f7(1,2)
#Too many arguments
f0(1,2)
f1(1,2,3)
f5(1,2,3)
f6(1,2,3)
f6(1,2,3,4)
#OK
#Not too few
f7(*t)
#Not too many
f2(1,2,3,4,5,6)
#Illegal name
f0(y=1)
f1(z=1)
f2(x=0, y=1)
#Ok name
f0(x=0)
f1(x=0, y=1)
f4(q=4)
#This is correct, but a bit weird.
f6(**{'x':1, 'y':2})
l0 = lambda : 0
l1 = lambda x : 2 * x
l1d = lambda x = 0 : 2 *x
#OK
l0()
l1(1)
l1d()
l1d(1)
#Too many
l0(1)
l1(1,2)
l1d(1,2)
#Too few
l1()
t2 = (1,2)
t3 = (1,2,3)
#Ok
f(*t2)
#Too many
f6(*(1,2,3))
f6(*t3)
#Ok
f6(**{'x':1, 'y':2})
#Illegal name
f6(**{'x':1, 'y':2, 'z':3})
#Theoretically -1 arguments required. Don't report
class C(object):
def f():
pass
C().f()
#Too many and wrong name -- check only wrong name is flagged.
f1(x, y, z=1)
#Overriding and call is wrong.
class Eggs1(object):
def spam(self):
pass
class Eggs2(Eggs1):
def spam(self, arg0, arg1):
pass
e = Eggs1() if cond else Eggs2()
e.spam(0)

View File

@@ -0,0 +1,2 @@
| test.py:3:17:3:23 | Str | Formatting string mixes implicitly and explicitly numbered fields. |
| test.py:8:17:8:23 | Str | Formatting string mixes implicitly and explicitly numbered fields. |

View File

@@ -0,0 +1 @@
Expressions/Formatting/MixedExplicitImplicitIn3101Format.ql

View File

@@ -0,0 +1,4 @@
| test.py:29:1:29:50 | Attribute() | Too many arguments for string format. Format $@ requires only 2, but 3 are provided. | test.py:5:20:5:29 | Str | "{0}, {1}" |
| test.py:30:1:30:51 | format() | Too many arguments for string format. Format $@ requires only 2, but 3 are provided. | test.py:10:20:10:29 | Str | "{0}, {1}" |
| test.py:32:1:32:50 | Attribute() | Too many arguments for string format. Format $@ requires only 2, but 3 are provided. | test.py:6:20:6:27 | Str | "{}, {}" |
| test.py:33:1:33:51 | format() | Too many arguments for string format. Format $@ requires only 2, but 3 are provided. | test.py:11:20:11:27 | Str | "{}, {}" |

View File

@@ -0,0 +1 @@
Expressions/Formatting/UnusedArgumentIn3101Format.ql

View File

@@ -0,0 +1,8 @@
| test.py:17:1:17:44 | Attribute() | Surplus named argument for string format. An argument named 'world' is provided, but it is not required by $@. | test.py:4:17:4:31 | Str | format "{name!r}, {0}" |
| test.py:18:1:18:45 | format() | Surplus named argument for string format. An argument named 'world' is provided, but it is not required by $@. | test.py:9:17:9:31 | Str | format "{name!r}, {0}" |
| test.py:20:1:20:49 | Attribute() | Surplus named argument for string format. An argument named 'world' is provided, but it is not required by $@. | test.py:4:17:4:31 | Str | format "{name!r}, {0}" |
| test.py:21:1:21:50 | format() | Surplus named argument for string format. An argument named 'world' is provided, but it is not required by $@. | test.py:9:17:9:31 | Str | format "{name!r}, {0}" |
| test.py:45:1:45:35 | format() | Surplus named argument for string format. An argument named 'z' is provided, but it is not required by $@. | test.py:37:14:37:18 | Str | any format used. |
| test.py:45:1:45:35 | format() | Surplus named argument for string format. An argument named 'z' is provided, but it is not required by $@. | test.py:39:14:39:18 | Str | any format used. |
| test.py:46:1:46:34 | Attribute() | Surplus named argument for string format. An argument named 'z' is provided, but it is not required by $@. | test.py:37:14:37:18 | Str | any format used. |
| test.py:46:1:46:34 | Attribute() | Surplus named argument for string format. An argument named 'z' is provided, but it is not required by $@. | test.py:39:14:39:18 | Str | any format used. |

View File

@@ -0,0 +1 @@
Expressions/Formatting/UnusedNamedArgumentIn3101Format.ql

View File

@@ -0,0 +1,2 @@
| test.py:17:1:17:44 | Attribute() | Missing named argument for string format. Format $@ requires 'name', but it is omitted. | test.py:4:17:4:31 | Str | "{name!r}, {0}" |
| test.py:18:1:18:45 | format() | Missing named argument for string format. Format $@ requires 'name', but it is omitted. | test.py:9:17:9:31 | Str | "{name!r}, {0}" |

View File

@@ -0,0 +1 @@
Expressions/Formatting/WrongNameInArgumentsFor3101Format.ql

View File

@@ -0,0 +1,6 @@
| test.py:20:1:20:49 | Attribute() | Too few arguments for string format. Format $@ requires at least 1, but 0 are provided. | test.py:4:17:4:31 | Str | "{name!r}, {0}" |
| test.py:21:1:21:50 | format() | Too few arguments for string format. Format $@ requires at least 1, but 0 are provided. | test.py:9:17:9:31 | Str | "{name!r}, {0}" |
| test.py:23:1:23:32 | Attribute() | Too few arguments for string format. Format $@ requires at least 2, but 1 is provided. | test.py:5:20:5:29 | Str | "{0}, {1}" |
| test.py:24:1:24:33 | format() | Too few arguments for string format. Format $@ requires at least 2, but 1 is provided. | test.py:10:20:10:29 | Str | "{0}, {1}" |
| test.py:26:1:26:32 | Attribute() | Too few arguments for string format. Format $@ requires at least 2, but 1 is provided. | test.py:6:20:6:27 | Str | "{}, {}" |
| test.py:27:1:27:33 | format() | Too few arguments for string format. Format $@ requires at least 2, but 1 is provided. | test.py:11:20:11:27 | Str | "{}, {}" |

View File

@@ -0,0 +1 @@
Expressions/Formatting/WrongNumberArgumentsFor3101Format.ql

View File

@@ -0,0 +1,108 @@
from __future__ import unicode_literals
mixed_format1 = "{}{1}"
named_format1 = "{name!r}, {0}"
explicit_format1 = "{0}, {1}"
implicit_format1 = "{}, {}"
mixed_format2 = "{}{1}"
named_format2 = "{name!r}, {0}"
explicit_format2 = "{0}, {1}"
implicit_format2 = "{}, {}"
mixed_format1.format("Hello", "World")
format(mixed_format2, "Hello", "World")
named_format1.format("Hello", world="World")
format(named_format2, "Hello", world="World")
named_format1.format(name="Hello", world="World")
format(named_format2, name="Hello", world="World")
explicit_format1.format("Hello")
format(explicit_format2, "Hello")
implicit_format1.format("Hello")
format(implicit_format2, "Hello")
explicit_format1.format("Hello", "World", "Extra")
format(explicit_format2, "Hello", "World", "Extra")
implicit_format1.format("Hello", "World", "Extra")
format(implicit_format2, "Hello", "World", "Extra")
#OK ODASA-3197
if cond:
x_or_y = "{x}"
else:
x_or_y = "{y}"
format(x_or_y, x="x", y="y")
x_or_y.format(x="x", y="y")
#Still fail for multiple formats
format(x_or_y, x="x", y="y", z="z")
x_or_y.format(x="x", y="y", z="z")
#False positive reported by customer. -- Verify fix.
"<td class={}>{{}}></td>".format(html_class)
"{{0}}{0}".format("X")
"{{{}}}".format("X")
#Crash JDK regex engine -- unless possessive quantifiers are used.
(
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{"
"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{}"
)
#Make sure nested braces are handled properly:
"ANS_{}=${{{}}}".format(x, xID)
def foo(msg_width):
stdout.write(u'{}\r{}{:<{}}'.format(1, 2, 3, 4))
stdout.write(u'{}\r{}{:<{width}}'.format(1, 2, 3, width=msg_width))
#Check parsing with punctuation.
"invalid value of type {.__name__}: {}".format(int, 1)
def varying_format(cond):
fmt = "{}" if cond else "{}, {}"
return fmt.format("hello", "world")

View File

@@ -0,0 +1,2 @@
| test.py:17:12:17:22 | Str | Backspace escape in regular expression at offset 1. |
| test.py:19:12:19:28 | Str | Backspace escape in regular expression at offset 8. |

View File

@@ -0,0 +1 @@
Expressions/Regex/BackspaceEscape.ql

View File

@@ -0,0 +1,3 @@
| test.py:41:12:41:18 | Str | This regular expression includes duplicate character 'A' in a set of characters. |
| test.py:42:12:42:19 | Str | This regular expression includes duplicate character '0' in a set of characters. |
| test.py:43:12:43:21 | Str | This regular expression includes duplicate character '-' in a set of characters. |

View File

@@ -0,0 +1 @@
Expressions/Regex/DuplicateCharacterInSet.ql

View File

@@ -0,0 +1,2 @@
| test.py:22:12:22:29 | Str | Regular expression is missing '?' in named group. |
| test.py:23:12:23:33 | Str | Regular expression is missing '?' in named group. |

View File

@@ -0,0 +1 @@
Expressions/Regex/MissingPartSpecialGroup.ql

View File

@@ -0,0 +1,4 @@
| test.py:4:12:4:19 | Str | This regular expression includes an unmatchable caret at offset 1. |
| test.py:5:12:5:23 | Str | This regular expression includes an unmatchable caret at offset 5. |
| test.py:6:12:6:21 | Str | This regular expression includes an unmatchable caret at offset 2. |
| test.py:74:12:74:27 | Str | This regular expression includes an unmatchable caret at offset 8. |

View File

@@ -0,0 +1 @@
Expressions/Regex/UnmatchableCaret.ql

View File

@@ -0,0 +1,4 @@
| test.py:29:12:29:19 | Str | This regular expression includes an unmatchable dollar at offset 3. |
| test.py:30:12:30:23 | Str | This regular expression includes an unmatchable dollar at offset 3. |
| test.py:31:12:31:20 | Str | This regular expression includes an unmatchable dollar at offset 2. |
| test.py:75:12:75:26 | Str | This regular expression includes an unmatchable dollar at offset 3. |

View File

@@ -0,0 +1 @@
Expressions/Regex/UnmatchableDollar.ql

View File

@@ -0,0 +1 @@
semmle-extractor-options: --max-import-depth=2

View File

@@ -0,0 +1,135 @@
import re
#Unmatchable caret
re.compile(b' ^abc')
re.compile(b"(?s) ^abc")
re.compile(b"\[^123]")
#Likely false positives for unmatchable caret
re.compile(b"[^123]")
re.compile(b"[123^]")
re.sub(b'(?m)^(?!$)', indent*' ', s)
re.compile(b"()^abc")
re.compile(b"(?:(?:\n\r?)|^)( *)\S")
re.compile(b"^diff (?:-r [0-9a-f]+ ){1,2}(.*)$")
#Backspace escape
re.compile(br"[\b\t ]") # Should warn
re.compile(br"E\d+\b.*") # Fine
re.compile(br"E\d+\b[ \b\t]") #Both
#Missing part in named group
re.compile(br'(P<name>[\w]+)')
re.compile(br'(_(P<name>[\w]+)|)')
#This is OK...
re.compile(br'(?P<name>\w+)')
#Unmatchable dollar
re.compile(b"abc$ ")
re.compile(b"abc$ (?s)")
re.compile(b"\[$] ")
#Likely false positives for unmatchable dollar
re.compile(b"[$] ")
re.compile(b"\$ ")
re.compile(b"abc$(?m)")
re.compile(b"abc$()")
#Duplicate character in set
re.compile(b"[AA]")
re.compile(b"[000]")
re.compile(b"[-0-9-]")
#Possible false positives
re.compile(b"[S\S]")
re.compile(b"[0\000]")
re.compile(b"[\0000]")
re.compile(b"[^^]")
re.compile(b"[-0-9]")
re.compile(b"[]]")
re.compile(b"^^^x.*")
re.compile(b".*x$$$")
re.compile(b"x*^y")
re.compile(b"x$y*")
# False positive for unmatchable caret
re.compile(br'(?!DEFAULT_PREFS)(?!CAN_SET_ANON)^[A-Z_]+$')
#Equivalent for unmatchable dollar
re.compile(br'^[A-Z_]+(?!DEFAULT_PREFS)(?!CAN_SET_ANON)$')
#And for negative look-behind assertions
re.compile(br'(?<!DEFAULT_PREFS)(?<!CAN_SET_ANON)^[A-Z_]+$')
re.compile(br'^[A-Z_]+(?<!DEFAULT_PREFS)(?<!CAN_SET_ANON)$')
#OK
re.compile(br'(?=foo)^\w+')
re.compile(br'\w+$(?<=foo)')
#Not OK
re.compile(br'(?<=foo)^\w+')
re.compile(br'\w+$(?=foo)')
#OK -- ODASA-ODASA-3968
re.compile('(?:[^%]|^)?%\((\w*)\)[a-z]')
#ODASA-3985
#Half Surrogate pairs
re.compile(u'[\uD800-\uDBFF][\uDC00-\uDFFF]')
#Outside BMP
re.compile(u'[\U00010000-\U0010ffff]')
#ODASA-6394 -- Flags defined by keyword
REGEX0 = re.compile(r''' ^\s* ''', re.VERBOSE)
REGEX1 = re.compile(r''' ^\s* ''', flags=re.VERBOSE)
REGEX2 = re.compile(r'''
^\s*
(?P<modifier>[+-]?)
(?: (?P<week> \d+ (?:\.\d*)? ) \s* [wW] )? \s*
(?: (?P<day> \d+ (?:\.\d*)? ) \s* [dD] )? \s*
(?: (?P<hour> \d+ (?:\.\d*)? ) \s* [hH] )? \s*
(?: (?P<minute> \d+ (?:\.\d*)? ) \s* [mM] )? \s*
(?: (?P<second> \d+ (?:\.\d*)? ) \s* [sS] )? \s*
$
''',
flags=re.VERBOSE)
REGEX3 = re.compile(r'''
^\s*
(?P<modifier>[+-]?)
(?: (?P<week> \d+ (?:\.\d*)? ) \s* [wW] )? \s*
(?: (?P<day> \d+ (?:\.\d*)? ) \s* [dD] )? \s*
(?: (?P<hour> \d+ (?:\.\d*)? ) \s* [hH] )? \s*
(?: (?P<minute> \d+ (?:\.\d*)? ) \s* [mM] )? \s*
(?: (?P<second> \d+ (?:\.\d*)? ) \s* [sS] )? \s*
$
''',
re.VERBOSE)
#ODASA-6780
DYLIB_RE = re.compile(r"""(?x)
(?P<location>^.*)(?:^|/)
(?P<name>
(?P<shortname>\w+?)
(?:\.(?P<version>[^._]+))?
(?:_(?P<suffix>[^._]+))?
\.dylib$
)
""")
#ODASA-6786
VERBOSE_REGEX = r"""
\[ # [
(?P<header>[^]]+) # very permissive!
\] # ]
"""
# Compiled regular expression marking it as verbose
ODASA_6786 = re.compile(VERBOSE_REGEX, re.VERBOSE)

Some files were not shown because too many files have changed in this diff Show More