Files
codeql/python/ql/test/query-tests/Expressions/general/expressions_test.py
Taus 3e6e7295c2 Python: Remove some FPs for ContainsNonContainer.ql
First fix handles the case where there's interference from a class-based
decorator on a function. In this case, _technically_ we have an instance
of the decorator class, but in practice this decorator will (hopefully)
forward all accesses to the thing it wraps.

The second fix has to do with methods that are added dynamically using
`setattr`. In this case, we cannot be sure that the relevant methods are
actually missing.
2026-03-16 14:36:29 +00:00

320 lines
6.5 KiB
Python

#encoding: utf-8
def dup_key():
return { 1: -1,
1: -2,
u'a' : u'A',
u'a' : u'B'
}
def simple_func(*args, **kwrgs): pass
#Unnecessary lambdas
lambda arg0, arg1: simple_func(arg0, arg1)
lambda arg0, *arg1: simple_func(arg0, *arg1)
lambda arg0, **arg1: simple_func(arg0, **arg1)
# these lambdas are_ necessary
lambda arg0, arg1=1: simple_func(arg0, arg1)
lambda arg0, arg1: simple_func(arg0, *arg1)
lambda arg0, arg1: simple_func(arg0, **arg1)
lambda arg0, *arg1: simple_func(arg0, arg1)
lambda arg0, **arg1: simple_func(arg0, arg1)
#Non-callable called
class NonCallable(object):
pass
class MaybeCallable(Unknown, object):
pass
def call_non_callable(arg):
non = NonCallable()
non(arg)
()()
[]()
dont_know = MaybeCallable()
dont_know() # Not a violation
#Explicit call to __del__
x.__del__()
#Unhashable object
def func():
mapping = dict(); unhash = list()
return mapping[unhash]
#Using 'is' when should be using '=='
s = "Hello " + "World"
if "Hello World" is s:
print ("OK")
#This is OK in CPython, but may not be portable
s = str(7)
if "7" is s:
print ("OK")
#And some data flow
CONSTANT = 20
if x is CONSTANT:
print ("OK")
#This is OK
x = object()
y = object()
if x is y:
print ("Very surprising!")
#This is also OK
if s is None:
print ("Also surprising")
#Portable is comparisons
def f(arg):
arg is ()
arg is 0
arg is ''
#Non-container
class XIter(object):
#Support both 2 and 3 form of next, but omit __iter__ method
def __next__(self):
pass
def next(self):
pass
def non_container():
seq = XIter()
if 1 in seq:
pass
if 1 not in seq:
pass
#Container inheriting from builtin
class MyDict(dict):
pass
class MySequence(UnresolvablebaseClass):
pass
def is_container():
mapping = MyDict()
if 1 in mapping:
pass
seq = MySequence()
if 1 in seq:
pass
seq = None
if seq is not None and 1 in seq:
pass
#Equals none
def x(arg):
return arg == None
class NotMyDict(object):
def f(self):
super(MyDict, self).f()
# class defining __del__
class Test(object):
def __del__(self):
pass
# subclass
class SubTest(Test):
def __del__(self):
# This is permitted and required.
Test.__del__(self)
# This is a violation.
self.__del__()
# This is an alternate syntax for the super() call, and hence OK.
super(SubTest, self).__del__()
# This is the Python 3 spelling of the same.
super().__del__()
#Some more lambdas
#Unnecessary lambdas
lambda arg0: len(arg0)
lambda arg0: XIter.next(arg0)
class UL(object):
def f(self, x):
pass
def g(self):
return lambda x: self.f(x)
# these lambdas are necessary
lambda arg0: XIter.next(arg0, arg1)
lambda arg0: func()(arg0)
lambda arg0, arg1: arg0.meth(arg0, arg1)
#Cannot flag lists as unhashable if the object
#we are subscripting may be a numpy array
def func(maybe_numpy):
unhash = list()
return maybe_numpy[unhash]
#Guarded non-callable called
def guarded_non_callable(cond):
if cond:
val = []
else:
val = func
if hasattr(val, "__call__"):
x(1)
#ODASA-4056
def format_string(s, formatter='minimal'):
"""Format the given string using the given formatter."""
if not callable(formatter):
formatter = get_formatter_for_name(formatter)
if formatter is None:
output = s
else:
output = formatter(s)
return output
#ODASA-4614
def f(x):
d = {}
d['one'] = {}
d['two'] = 0
return x[d['two']]
#ODASA-4055
class C:
def _internal(arg):
# arg is not a C
def wrapper(args):
return arg(args)
return wrapper
@_internal
def method(self, *args):
pass
#ODASA-4689
class StrangeIndex:
def __getitem__(self,index):
return 1
x = StrangeIndex();
print(x[{'a': 'b'}])
def not_dup_key():
return { u'a' : 0,
b'a' : 0,
u"😄" : 1,
u"😅" : 2,
u"😆" : 3
}
# Lookup of unhashable object triggers TypeError, but the
# exception is caught, so it's not a bug. This used to be
# a false positive of the HashedButNoHash query.
def func():
unhash = list()
try:
hash(unhash)
except TypeError:
return 1
return 0
def func():
mapping = dict(); unhash = list()
try:
mapping[unhash]
except TypeError:
return 1
return 0
# False positive for py/member-test-non-container
# Container wrapped in MappingProxyType
from types import MappingProxyType
def mpt_arg(d=MappingProxyType({})):
return 1 in d
#### UseofApply.ql
# Use of the builtin function `apply` is generally considered bad now that the
# ability to destructure lists of arguments is possible, but we should not flag
# cases where the function is merely named `apply` rather than being the actual
# builtin `apply` function.
def useofapply():
def foo():
pass
# Positive Cases
# This use of `apply` is a reference to the builtin function and so SHOULD be
# caught by the query.
apply(foo, [1])
# Negative Cases
# This use of `apply` is a reference to the locally defined function inside of
# `local`, and so SHOULD NOT be caught by the query.
def local():
def apply(f):
pass
apply(foo)([1])
# Class used as a decorator: the runtime value at attribute access is the
# function's return value, not the decorator class instance.
class cached_property(object):
def __init__(self, func):
self.func = func
def __get__(self, obj, cls):
val = self.func(obj)
setattr(obj, self.func.__name__, val)
return val
class MyForm(object):
@cached_property
def changed_data(self):
return [1, 2, 3]
def test_decorator_class(form):
f = MyForm()
# OK: cached_property is a descriptor; the actual runtime value is a list.
if "name" in f.changed_data:
pass
# Class with dynamically added methods via setattr: we cannot statically
# determine its full interface, so we should not flag it.
class DynamicProxy(object):
def __init__(self, args):
self._args = args
for method_name in ["__contains__", "__iter__", "__len__"]:
def wrapper(self, *args, __method_name=method_name):
pass
setattr(DynamicProxy, method_name, wrapper)
def test_dynamic_methods():
proxy = DynamicProxy(())
# OK: __contains__ is added dynamically via setattr.
if "name" in proxy:
pass