Merge branch 'dev' into testing-strategy

pull/1782/head
alpharush 2 years ago
commit db6bd76cb5
  1. 6
      examples/scripts/data_dependency.py
  2. 1
      examples/scripts/variable_in_condition.py
  3. 67
      slither/__main__.py
  4. 146
      slither/analyses/data_dependency/data_dependency.py
  5. 29
      slither/analyses/write/are_variables_written.py
  6. 100
      slither/core/cfg/node.py
  7. 19
      slither/core/children/child_contract.py
  8. 17
      slither/core/children/child_event.py
  9. 18
      slither/core/children/child_expression.py
  10. 17
      slither/core/children/child_function.py
  11. 17
      slither/core/children/child_inheritance.py
  12. 31
      slither/core/children/child_node.py
  13. 17
      slither/core/children/child_structure.py
  14. 17
      slither/core/compilation_unit.py
  15. 76
      slither/core/declarations/contract.py
  16. 29
      slither/core/declarations/contract_level.py
  17. 10
      slither/core/declarations/custom_error.py
  18. 12
      slither/core/declarations/custom_error_contract.py
  19. 2
      slither/core/declarations/custom_error_top_level.py
  20. 4
      slither/core/declarations/enum_contract.py
  21. 4
      slither/core/declarations/event.py
  22. 7
      slither/core/declarations/function.py
  23. 29
      slither/core/declarations/function_contract.py
  24. 12
      slither/core/declarations/solidity_variables.py
  25. 4
      slither/core/declarations/structure_contract.py
  26. 6
      slither/core/declarations/top_level.py
  27. 3
      slither/core/declarations/using_for_top_level.py
  28. 1
      slither/core/dominators/utils.py
  29. 5
      slither/core/expressions/assignment_operation.py
  30. 3
      slither/core/expressions/binary_operation.py
  31. 8
      slither/core/expressions/call_expression.py
  32. 2
      slither/core/expressions/conditional_expression.py
  33. 20
      slither/core/expressions/expression_typed.py
  34. 76
      slither/core/expressions/identifier.py
  35. 19
      slither/core/expressions/index_access.py
  36. 4
      slither/core/expressions/literal.py
  37. 3
      slither/core/expressions/member_access.py
  38. 11
      slither/core/expressions/type_conversion.py
  39. 3
      slither/core/expressions/unary_operation.py
  40. 10
      slither/core/slither_core.py
  41. 15
      slither/core/solidity_types/array_type.py
  42. 6
      slither/core/solidity_types/elementary_type.py
  43. 4
      slither/core/solidity_types/mapping_type.py
  44. 8
      slither/core/solidity_types/type_alias.py
  45. 6
      slither/core/solidity_types/type_information.py
  46. 6
      slither/core/source_mapping/source_mapping.py
  47. 5
      slither/core/variables/event_variable.py
  48. 17
      slither/core/variables/local_variable.py
  49. 4
      slither/core/variables/state_variable.py
  50. 19
      slither/core/variables/structure_variable.py
  51. 18
      slither/core/variables/variable.py
  52. 4
      slither/detectors/abstract_detector.py
  53. 13
      slither/detectors/assembly/shift_parameter_mixup.py
  54. 12
      slither/detectors/attributes/const_functions_asm.py
  55. 9
      slither/detectors/attributes/const_functions_state.py
  56. 15
      slither/detectors/attributes/constant_pragma.py
  57. 8
      slither/detectors/attributes/incorrect_solc.py
  58. 8
      slither/detectors/attributes/locked_ether.py
  59. 8
      slither/detectors/attributes/unimplemented_interface.py
  60. 20
      slither/detectors/compiler_bugs/array_by_reference.py
  61. 11
      slither/detectors/compiler_bugs/enum_conversion.py
  62. 11
      slither/detectors/compiler_bugs/multiple_constructor_schemes.py
  63. 3
      slither/detectors/compiler_bugs/reused_base_constructor.py
  64. 9
      slither/detectors/compiler_bugs/storage_ABIEncoderV2_array.py
  65. 32
      slither/detectors/compiler_bugs/storage_signed_integer_array.py
  66. 5
      slither/detectors/compiler_bugs/uninitialized_function_ptr_in_constructor.py
  67. 8
      slither/detectors/erc/erc20/arbitrary_send_erc20.py
  68. 8
      slither/detectors/erc/erc20/arbitrary_send_erc20_no_permit.py
  69. 8
      slither/detectors/erc/erc20/arbitrary_send_erc20_permit.py
  70. 8
      slither/detectors/erc/erc20/incorrect_erc20_interface.py
  71. 12
      slither/detectors/erc/incorrect_erc721_interface.py
  72. 8
      slither/detectors/examples/backdoor.py
  73. 15
      slither/detectors/functions/arbitrary_send_eth.py
  74. 8
      slither/detectors/functions/cyclomatic_complexity.py
  75. 8
      slither/detectors/functions/dead_code.py
  76. 12
      slither/detectors/functions/modifier.py
  77. 8
      slither/detectors/functions/permit_domain_signature_collision.py
  78. 8
      slither/detectors/functions/protected_variable.py
  79. 8
      slither/detectors/functions/suicidal.py
  80. 14
      slither/detectors/functions/unimplemented.py
  81. 7
      slither/detectors/naming_convention/naming_convention.py
  82. 9
      slither/detectors/operations/bad_prng.py
  83. 29
      slither/detectors/operations/block_timestamp.py
  84. 8
      slither/detectors/operations/low_level_calls.py
  85. 8
      slither/detectors/operations/missing_events_access_control.py
  86. 8
      slither/detectors/operations/missing_events_arithmetic.py
  87. 8
      slither/detectors/operations/missing_zero_address_validation.py
  88. 8
      slither/detectors/operations/unused_return_values.py
  89. 8
      slither/detectors/operations/void_constructor.py
  90. 8
      slither/detectors/reentrancy/token.py
  91. 8
      slither/detectors/shadowing/builtin_symbols.py
  92. 10
      slither/detectors/shadowing/local.py
  93. 8
      slither/detectors/shadowing/state.py
  94. 13
      slither/detectors/slither/name_reused.py
  95. 8
      slither/detectors/source/rtlo.py
  96. 16
      slither/detectors/statements/array_length_assignment.py
  97. 8
      slither/detectors/statements/assembly.py
  98. 17
      slither/detectors/statements/assert_state_change.py
  99. 8
      slither/detectors/statements/boolean_constant_equality.py
  100. 8
      slither/detectors/statements/boolean_constant_misuse.py
  101. Some files were not shown because too many files have changed in this diff Show More

@ -18,6 +18,8 @@ assert len(contracts) == 1
contract = contracts[0]
destination = contract.get_state_variable_from_name("destination")
source = contract.get_state_variable_from_name("source")
assert source
assert destination
print(f"{source} is dependent of {destination}: {is_dependent(source, destination, contract)}")
assert not is_dependent(source, destination, contract)
@ -47,9 +49,11 @@ print(f"{destination} is tainted {is_tainted(destination, contract)}")
assert is_tainted(destination, contract)
destination_indirect_1 = contract.get_state_variable_from_name("destination_indirect_1")
assert destination_indirect_1
print(f"{destination_indirect_1} is tainted {is_tainted(destination_indirect_1, contract)}")
assert is_tainted(destination_indirect_1, contract)
destination_indirect_2 = contract.get_state_variable_from_name("destination_indirect_2")
assert destination_indirect_2
print(f"{destination_indirect_2} is tainted {is_tainted(destination_indirect_2, contract)}")
assert is_tainted(destination_indirect_2, contract)
@ -88,6 +92,8 @@ contract = contracts[0]
contract_derived = slither.get_contract_from_name("Derived")[0]
destination = contract.get_state_variable_from_name("destination")
source = contract.get_state_variable_from_name("source")
assert destination
assert source
print(f"{destination} is dependent of {source}: {is_dependent(destination, source, contract)}")
assert not is_dependent(destination, source, contract)

@ -14,6 +14,7 @@ assert len(contracts) == 1
contract = contracts[0]
# Get the variable
var_a = contract.get_state_variable_from_name("a")
assert var_a
# Get the functions reading the variable
functions_reading_a = contract.get_functions_reading_from_variable(var_a)

@ -66,7 +66,7 @@ def process_single(
args: argparse.Namespace,
detector_classes: List[Type[AbstractDetector]],
printer_classes: List[Type[AbstractPrinter]],
) -> Tuple[Slither, List[Dict], List[Dict], int]:
) -> Tuple[Slither, List[Dict], List[Output], int]:
"""
The core high-level code for running Slither static analysis.
@ -86,7 +86,7 @@ def process_all(
args: argparse.Namespace,
detector_classes: List[Type[AbstractDetector]],
printer_classes: List[Type[AbstractPrinter]],
) -> Tuple[List[Slither], List[Dict], List[Dict], int]:
) -> Tuple[List[Slither], List[Dict], List[Output], int]:
compilations = compile_all(target, **vars(args))
slither_instances = []
results_detectors = []
@ -141,23 +141,6 @@ def _process(
return slither, results_detectors, results_printers, analyzed_contracts_count
# TODO: delete me?
def process_from_asts(
filenames: List[str],
args: argparse.Namespace,
detector_classes: List[Type[AbstractDetector]],
printer_classes: List[Type[AbstractPrinter]],
) -> Tuple[Slither, List[Dict], List[Dict], int]:
all_contracts: List[str] = []
for filename in filenames:
with open(filename, encoding="utf8") as file_open:
contract_loaded = json.load(file_open)
all_contracts.append(contract_loaded["ast"])
return process_single(all_contracts, args, detector_classes, printer_classes)
# endregion
###################################################################################
###################################################################################
@ -605,9 +588,6 @@ def parse_args(
default=False,
)
# if the json is splitted in different files
parser.add_argument("--splitted", help=argparse.SUPPRESS, action="store_true", default=False)
# Disable the throw/catch on partial analyses
parser.add_argument(
"--disallow-partial", help=argparse.SUPPRESS, action="store_true", default=False
@ -623,7 +603,7 @@ def parse_args(
args.filter_paths = parse_filter_paths(args)
# Verify our json-type output is valid
args.json_types = set(args.json_types.split(","))
args.json_types = set(args.json_types.split(",")) # type:ignore
for json_type in args.json_types:
if json_type not in JSON_OUTPUT_TYPES:
raise Exception(f'Error: "{json_type}" is not a valid JSON result output type.')
@ -632,7 +612,9 @@ def parse_args(
class ListDetectors(argparse.Action): # pylint: disable=too-few-public-methods
def __call__(self, parser, *args, **kwargs): # pylint: disable=signature-differs
def __call__(
self, parser: Any, *args: Any, **kwargs: Any
) -> None: # pylint: disable=signature-differs
detectors, _ = get_detectors_and_printers()
output_detectors(detectors)
parser.exit()
@ -694,14 +676,14 @@ class OutputWiki(argparse.Action): # pylint: disable=too-few-public-methods
class FormatterCryticCompile(logging.Formatter):
def format(self, record):
def format(self, record: logging.LogRecord) -> str:
# for i, msg in enumerate(record.msg):
if record.msg.startswith("Compilation warnings/errors on "):
txt = record.args[1]
txt = txt.split("\n")
txt = record.args[1] # type:ignore
txt = txt.split("\n") # type:ignore
txt = [red(x) if "Error" in x else x for x in txt]
txt = "\n".join(txt)
record.args = (record.args[0], txt)
record.args = (record.args[0], txt) # type:ignore
return super().format(record)
@ -744,7 +726,7 @@ def main_impl(
set_colorization_enabled(False if args.disable_color else sys.stdout.isatty())
# Define some variables for potential JSON output
json_results = {}
json_results: Dict[str, Any] = {}
output_error = None
outputting_json = args.json is not None
outputting_json_stdout = args.json == "-"
@ -793,7 +775,7 @@ def main_impl(
crytic_compile_error.setLevel(logging.INFO)
results_detectors: List[Dict] = []
results_printers: List[Dict] = []
results_printers: List[Output] = []
try:
filename = args.filename
@ -806,26 +788,17 @@ def main_impl(
number_contracts = 0
slither_instances = []
if args.splitted:
for filename in filenames:
(
slither_instance,
results_detectors,
results_printers,
number_contracts,
) = process_from_asts(filenames, args, detector_classes, printer_classes)
results_detectors_tmp,
results_printers_tmp,
number_contracts_tmp,
) = process_single(filename, args, detector_classes, printer_classes)
number_contracts += number_contracts_tmp
results_detectors += results_detectors_tmp
results_printers += results_printers_tmp
slither_instances.append(slither_instance)
else:
for filename in filenames:
(
slither_instance,
results_detectors_tmp,
results_printers_tmp,
number_contracts_tmp,
) = process_single(filename, args, detector_classes, printer_classes)
number_contracts += number_contracts_tmp
results_detectors += results_detectors_tmp
results_printers += results_printers_tmp
slither_instances.append(slither_instance)
# Rely on CryticCompile to discern the underlying type of compilations.
else:

@ -2,8 +2,9 @@
Compute the data depenency between all the SSA variables
"""
from collections import defaultdict
from typing import Union, Set, Dict, TYPE_CHECKING
from typing import Union, Set, Dict, TYPE_CHECKING, List
from slither.core.cfg.node import Node
from slither.core.declarations import (
Contract,
Enum,
@ -12,11 +13,14 @@ from slither.core.declarations import (
SolidityVariable,
SolidityVariableComposed,
Structure,
FunctionContract,
)
from slither.core.declarations.solidity_import_placeholder import SolidityImportPlaceHolder
from slither.core.solidity_types.type import Type
from slither.core.variables.top_level_variable import TopLevelVariable
from slither.core.variables.variable import Variable
from slither.slithir.operations import Index, OperationWithLValue, InternalCall, Operation
from slither.slithir.utils.utils import LVALUE
from slither.slithir.variables import (
Constant,
LocalIRVariable,
@ -26,12 +30,11 @@ from slither.slithir.variables import (
TemporaryVariableSSA,
TupleVariableSSA,
)
from slither.core.solidity_types.type import Type
from slither.slithir.variables.variable import SlithIRVariable
if TYPE_CHECKING:
from slither.core.compilation_unit import SlitherCompilationUnit
###################################################################################
###################################################################################
# region User APIs
@ -39,26 +42,39 @@ if TYPE_CHECKING:
###################################################################################
Variable_types = Union[Variable, SolidityVariable]
SUPPORTED_TYPES = Union[Variable, SolidityVariable]
# TODO refactor the data deps to be better suited for top level function object
# Right now we allow to pass a node to ease the API, but we need something
# better
# The deps propagation for top level elements is also not working as expected
Context_types_API = Union[Contract, Function, Node]
Context_types = Union[Contract, Function]
def is_dependent(
variable: Variable_types,
source: Variable_types,
context: Context_types,
variable: SUPPORTED_TYPES,
source: SUPPORTED_TYPES,
context: Context_types_API,
only_unprotected: bool = False,
) -> bool:
"""
If Node is provided as context, the context will be the broader context, either the contract or the function,
depending on if the node is in a top level function or not
Args:
variable (Variable)
source (Variable)
context (Contract|Function)
context (Contract|Function|Node).
only_unprotected (bool): True only unprotected function are considered
Returns:
bool
"""
assert isinstance(context, (Contract, Function))
assert isinstance(context, (Contract, Function, Node))
if isinstance(context, Node):
func = context.function
context = func.contract if isinstance(func, FunctionContract) else func
if isinstance(variable, Constant):
return False
if variable == source:
@ -74,12 +90,15 @@ def is_dependent(
def is_dependent_ssa(
variable: Variable_types,
source: Variable_types,
context: Context_types,
variable: SUPPORTED_TYPES,
source: SUPPORTED_TYPES,
context: Context_types_API,
only_unprotected: bool = False,
) -> bool:
"""
If Node is provided as context, the context will be the broader context, either the contract or the function,
depending on if the node is in a top level function or not
Args:
variable (Variable)
taint (Variable)
@ -88,7 +107,10 @@ def is_dependent_ssa(
Returns:
bool
"""
assert isinstance(context, (Contract, Function))
assert isinstance(context, (Contract, Function, Node))
if isinstance(context, Node):
func = context.function
context = func.contract if isinstance(func, FunctionContract) else func
context_dict = context.context
if isinstance(variable, Constant):
return False
@ -111,12 +133,15 @@ GENERIC_TAINT = {
def is_tainted(
variable: Variable_types,
context: Context_types,
variable: SUPPORTED_TYPES,
context: Context_types_API,
only_unprotected: bool = False,
ignore_generic_taint: bool = False,
) -> bool:
"""
If Node is provided as context, the context will be the broader context, either the contract or the function,
depending on if the node is in a top level function or not
Args:
variable
context (Contract|Function)
@ -124,7 +149,10 @@ def is_tainted(
Returns:
bool
"""
assert isinstance(context, (Contract, Function))
assert isinstance(context, (Contract, Function, Node))
if isinstance(context, Node):
func = context.function
context = func.contract if isinstance(func, FunctionContract) else func
assert isinstance(only_unprotected, bool)
if isinstance(variable, Constant):
return False
@ -138,12 +166,15 @@ def is_tainted(
def is_tainted_ssa(
variable: Variable_types,
context: Context_types,
variable: SUPPORTED_TYPES,
context: Context_types_API,
only_unprotected: bool = False,
ignore_generic_taint: bool = False,
):
) -> bool:
"""
If Node is provided as context, the context will be the broader context, either the contract or the function,
depending on if the node is in a top level function or not
Args:
variable
context (Contract|Function)
@ -151,7 +182,10 @@ def is_tainted_ssa(
Returns:
bool
"""
assert isinstance(context, (Contract, Function))
assert isinstance(context, (Contract, Function, Node))
if isinstance(context, Node):
func = context.function
context = func.contract if isinstance(func, FunctionContract) else func
assert isinstance(only_unprotected, bool)
if isinstance(variable, Constant):
return False
@ -165,19 +199,24 @@ def is_tainted_ssa(
def get_dependencies(
variable: Variable_types,
context: Context_types,
variable: SUPPORTED_TYPES,
context: Context_types_API,
only_unprotected: bool = False,
) -> Set[Variable]:
"""
Return the variables for which `variable` depends on.
If Node is provided as context, the context will be the broader context, either the contract or the function,
depending on if the node is in a top level function or not
:param variable: The target
:param context: Either a function (interprocedural) or a contract (inter transactional)
:param only_unprotected: True if consider only protected functions
:return: set(Variable)
"""
assert isinstance(context, (Contract, Function))
assert isinstance(context, (Contract, Function, Node))
if isinstance(context, Node):
func = context.function
context = func.contract if isinstance(func, FunctionContract) else func
assert isinstance(only_unprotected, bool)
if only_unprotected:
return context.context[KEY_NON_SSA_UNPROTECTED].get(variable, set())
@ -185,16 +224,21 @@ def get_dependencies(
def get_all_dependencies(
context: Context_types, only_unprotected: bool = False
context: Context_types_API, only_unprotected: bool = False
) -> Dict[Variable, Set[Variable]]:
"""
Return the dictionary of dependencies.
If Node is provided as context, the context will be the broader context, either the contract or the function,
depending on if the node is in a top level function or not
:param context: Either a function (interprocedural) or a contract (inter transactional)
:param only_unprotected: True if consider only protected functions
:return: Dict(Variable, set(Variable))
"""
assert isinstance(context, (Contract, Function))
assert isinstance(context, (Contract, Function, Node))
if isinstance(context, Node):
func = context.function
context = func.contract if isinstance(func, FunctionContract) else func
assert isinstance(only_unprotected, bool)
if only_unprotected:
return context.context[KEY_NON_SSA_UNPROTECTED]
@ -202,19 +246,24 @@ def get_all_dependencies(
def get_dependencies_ssa(
variable: Variable_types,
context: Context_types,
variable: SUPPORTED_TYPES,
context: Context_types_API,
only_unprotected: bool = False,
) -> Set[Variable]:
"""
Return the variables for which `variable` depends on (SSA version).
If Node is provided as context, the context will be the broader context, either the contract or the function,
depending on if the node is in a top level function or not
:param variable: The target (must be SSA variable)
:param context: Either a function (interprocedural) or a contract (inter transactional)
:param only_unprotected: True if consider only protected functions
:return: set(Variable)
"""
assert isinstance(context, (Contract, Function))
assert isinstance(context, (Contract, Function, Node))
if isinstance(context, Node):
func = context.function
context = func.contract if isinstance(func, FunctionContract) else func
assert isinstance(only_unprotected, bool)
if only_unprotected:
return context.context[KEY_SSA_UNPROTECTED].get(variable, set())
@ -222,16 +271,21 @@ def get_dependencies_ssa(
def get_all_dependencies_ssa(
context: Context_types, only_unprotected: bool = False
context: Context_types_API, only_unprotected: bool = False
) -> Dict[Variable, Set[Variable]]:
"""
Return the dictionary of dependencies.
If Node is provided as context, the context will be the broader context, either the contract or the function,
depending on if the node is in a top level function or not
:param context: Either a function (interprocedural) or a contract (inter transactional)
:param only_unprotected: True if consider only protected functions
:return: Dict(Variable, set(Variable))
"""
assert isinstance(context, (Contract, Function))
assert isinstance(context, (Contract, Function, Node))
if isinstance(context, Node):
func = context.function
context = func.contract if isinstance(func, FunctionContract) else func
assert isinstance(only_unprotected, bool)
if only_unprotected:
return context.context[KEY_SSA_UNPROTECTED]
@ -341,13 +395,9 @@ def transitive_close_dependencies(
while changed:
changed = False
to_add = defaultdict(set)
[ # pylint: disable=expression-not-assigned
[
for key, items in context.context[context_key].items():
for item in items & keys:
to_add[key].update(context.context[context_key][item] - {key} - items)
for item in items & keys
]
for key, items in context.context[context_key].items()
]
for k, v in to_add.items():
# Because we dont have any check on the update operation
# We might update an empty set with an empty set
@ -366,20 +416,20 @@ def add_dependency(lvalue: Variable, function: Function, ir: Operation, is_prote
function.context[KEY_SSA][lvalue] = set()
if not is_protected:
function.context[KEY_SSA_UNPROTECTED][lvalue] = set()
read: Union[List[Union[LVALUE, SolidityVariableComposed]], List[SlithIRVariable]]
if isinstance(ir, Index):
read = [ir.variable_left]
elif isinstance(ir, InternalCall):
elif isinstance(ir, InternalCall) and ir.function:
read = ir.function.return_values_ssa
else:
read = ir.read
# pylint: disable=expression-not-assigned
[function.context[KEY_SSA][lvalue].add(v) for v in read if not isinstance(v, Constant)]
for v in read:
if not isinstance(v, Constant):
function.context[KEY_SSA][lvalue].add(v)
if not is_protected:
[
function.context[KEY_SSA_UNPROTECTED][lvalue].add(v)
for v in read
if not isinstance(v, Constant)
]
for v in read:
if not isinstance(v, Constant):
function.context[KEY_SSA_UNPROTECTED][lvalue].add(v)
def compute_dependency_function(function: Function) -> None:
@ -407,7 +457,7 @@ def compute_dependency_function(function: Function) -> None:
)
def convert_variable_to_non_ssa(v: Variable_types) -> Variable_types:
def convert_variable_to_non_ssa(v: SUPPORTED_TYPES) -> SUPPORTED_TYPES:
if isinstance(
v,
(
@ -438,10 +488,10 @@ def convert_variable_to_non_ssa(v: Variable_types) -> Variable_types:
def convert_to_non_ssa(
data_depencies: Dict[Variable_types, Set[Variable_types]]
) -> Dict[Variable_types, Set[Variable_types]]:
data_depencies: Dict[SUPPORTED_TYPES, Set[SUPPORTED_TYPES]]
) -> Dict[SUPPORTED_TYPES, Set[SUPPORTED_TYPES]]:
# Need to create new set() as its changed during iteration
ret: Dict[Variable_types, Set[Variable_types]] = {}
ret: Dict[SUPPORTED_TYPES, Set[SUPPORTED_TYPES]] = {}
for (k, values) in data_depencies.items():
var = convert_variable_to_non_ssa(k)
if not var in ret:

@ -2,10 +2,10 @@
Detect if all the given variables are written in all the paths of the function
"""
from collections import defaultdict
from typing import Dict, Set, List
from typing import Dict, Set, List, Any, Optional
from slither.core.cfg.node import NodeType, Node
from slither.core.declarations import SolidityFunction
from slither.core.declarations import SolidityFunction, Function
from slither.core.variables.variable import Variable
from slither.slithir.operations import (
Index,
@ -18,7 +18,7 @@ from slither.slithir.variables import ReferenceVariable, TemporaryVariable
class State: # pylint: disable=too-few-public-methods
def __init__(self):
def __init__(self) -> None:
# Map node -> list of variables set
# Were each variables set represents a configuration of a path
# If two paths lead to the exact same set of variables written, we dont need to explore both
@ -34,11 +34,11 @@ class State: # pylint: disable=too-few-public-methods
# pylint: disable=too-many-branches
def _visit(
node: Node,
node: Optional[Node],
state: State,
variables_written: Set[Variable],
variables_to_write: List[Variable],
):
) -> List[Variable]:
"""
Explore all the nodes to look for values not written when the node's function return
Fixpoint reaches if no new written variables are found
@ -51,6 +51,8 @@ def _visit(
refs = {}
variables_written = set(variables_written)
if not node:
return []
for ir in node.irs:
if isinstance(ir, SolidityCall):
# TODO convert the revert to a THROW node
@ -70,17 +72,20 @@ def _visit(
if ir.lvalue and not isinstance(ir.lvalue, (TemporaryVariable, ReferenceVariable)):
variables_written.add(ir.lvalue)
lvalue = ir.lvalue
lvalue: Any = ir.lvalue
while isinstance(lvalue, ReferenceVariable):
if lvalue not in refs:
break
if refs[lvalue] and not isinstance(
refs[lvalue], (TemporaryVariable, ReferenceVariable)
refs_lvalues = refs[lvalue]
if (
refs_lvalues
and isinstance(refs_lvalues, Variable)
and not isinstance(refs_lvalues, (TemporaryVariable, ReferenceVariable))
):
variables_written.add(refs[lvalue])
lvalue = refs[lvalue]
variables_written.add(refs_lvalues)
lvalue = refs_lvalues
ret = []
ret: List[Variable] = []
if not node.sons and node.type not in [NodeType.THROW, NodeType.RETURN]:
ret += [v for v in variables_to_write if v not in variables_written]
@ -96,7 +101,7 @@ def _visit(
return ret
def are_variables_written(function, variables_to_write):
def are_variables_written(function: Function, variables_to_write: List[Variable]) -> List[Variable]:
"""
Return the list of variable that are not written at the end of the function

@ -5,8 +5,7 @@ from enum import Enum
from typing import Optional, List, Set, Dict, Tuple, Union, TYPE_CHECKING
from slither.all_exceptions import SlitherException
from slither.core.children.child_function import ChildFunction
from slither.core.declarations import Contract, Function
from slither.core.declarations import Contract, Function, FunctionContract
from slither.core.declarations.solidity_variables import (
SolidityVariable,
SolidityFunction,
@ -33,6 +32,7 @@ from slither.slithir.operations import (
Return,
Operation,
)
from slither.slithir.utils.utils import RVALUE
from slither.slithir.variables import (
Constant,
LocalIRVariable,
@ -106,7 +106,7 @@ class NodeType(Enum):
# I am not sure why, but pylint reports a lot of "no-member" issue that are not real (Josselin)
# pylint: disable=no-member
class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-methods
class Node(SourceMapping): # pylint: disable=too-many-public-methods
"""
Node class
@ -146,12 +146,12 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
self._node_id: int = node_id
self._vars_written: List[Variable] = []
self._vars_read: List[Variable] = []
self._vars_read: List[Union[Variable, SolidityVariable]] = []
self._ssa_vars_written: List["SlithIRVariable"] = []
self._ssa_vars_read: List["SlithIRVariable"] = []
self._internal_calls: List["Function"] = []
self._internal_calls: List[Union["Function", "SolidityFunction"]] = []
self._solidity_calls: List[SolidityFunction] = []
self._high_level_calls: List["HighLevelCallType"] = [] # contains library calls
self._library_calls: List["LibraryCallType"] = []
@ -172,7 +172,9 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
self._local_vars_read: List[LocalVariable] = []
self._local_vars_written: List[LocalVariable] = []
self._slithir_vars: Set["SlithIRVariable"] = set() # non SSA
self._slithir_vars: Set[
Union["SlithIRVariable", ReferenceVariable, TemporaryVariable, TupleVariable]
] = set() # non SSA
self._ssa_local_vars_read: List[LocalIRVariable] = []
self._ssa_local_vars_written: List[LocalIRVariable] = []
@ -189,6 +191,7 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
self.scope: Union["Scope", "Function"] = scope
self.file_scope: "FileScope" = file_scope
self._function: Optional["Function"] = None
###################################################################################
###################################################################################
@ -213,7 +216,7 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
return self._node_type
@type.setter
def type(self, new_type: NodeType):
def type(self, new_type: NodeType) -> None:
self._node_type = new_type
@property
@ -224,6 +227,13 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
return True
return False
def set_function(self, function: "Function") -> None:
self._function = function
@property
def function(self) -> "Function":
return self._function
# endregion
###################################################################################
###################################################################################
@ -232,7 +242,7 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
###################################################################################
@property
def variables_read(self) -> List[Variable]:
def variables_read(self) -> List[Union[Variable, SolidityVariable]]:
"""
list(Variable): Variables read (local/state/solidity)
"""
@ -285,11 +295,13 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
return self._expression_vars_read
@variables_read_as_expression.setter
def variables_read_as_expression(self, exprs: List[Expression]):
def variables_read_as_expression(self, exprs: List[Expression]) -> None:
self._expression_vars_read = exprs
@property
def slithir_variables(self) -> List["SlithIRVariable"]:
def slithir_variables(
self,
) -> List[Union["SlithIRVariable", ReferenceVariable, TemporaryVariable, TupleVariable]]:
return list(self._slithir_vars)
@property
@ -339,7 +351,7 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
return self._expression_vars_written
@variables_written_as_expression.setter
def variables_written_as_expression(self, exprs: List[Expression]):
def variables_written_as_expression(self, exprs: List[Expression]) -> None:
self._expression_vars_written = exprs
# endregion
@ -399,7 +411,7 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
return self._external_calls_as_expressions
@external_calls_as_expressions.setter
def external_calls_as_expressions(self, exprs: List[Expression]):
def external_calls_as_expressions(self, exprs: List[Expression]) -> None:
self._external_calls_as_expressions = exprs
@property
@ -410,7 +422,7 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
return self._internal_calls_as_expressions
@internal_calls_as_expressions.setter
def internal_calls_as_expressions(self, exprs: List[Expression]):
def internal_calls_as_expressions(self, exprs: List[Expression]) -> None:
self._internal_calls_as_expressions = exprs
@property
@ -418,10 +430,10 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
return list(self._expression_calls)
@calls_as_expression.setter
def calls_as_expression(self, exprs: List[Expression]):
def calls_as_expression(self, exprs: List[Expression]) -> None:
self._expression_calls = exprs
def can_reenter(self, callstack=None) -> bool:
def can_reenter(self, callstack: Optional[List[Union[Function, Variable]]] = None) -> bool:
"""
Check if the node can re-enter
Do not consider CREATE as potential re-enter, but check if the
@ -567,7 +579,7 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
"""
self._fathers.append(father)
def set_fathers(self, fathers: List["Node"]):
def set_fathers(self, fathers: List["Node"]) -> None:
"""Set the father nodes
Args:
@ -663,20 +675,20 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
return self._irs_ssa
@irs_ssa.setter
def irs_ssa(self, irs):
def irs_ssa(self, irs: List[Operation]) -> None:
self._irs_ssa = irs
def add_ssa_ir(self, ir: Operation) -> None:
"""
Use to place phi operation
"""
ir.set_node(self)
ir.set_node(self) # type: ignore
self._irs_ssa.append(ir)
def slithir_generation(self) -> None:
if self.expression:
expression = self.expression
self._irs = convert_expression(expression, self)
self._irs = convert_expression(expression, self) # type:ignore
self._find_read_write_call()
@ -713,7 +725,7 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
return self._dominators
@dominators.setter
def dominators(self, dom: Set["Node"]):
def dominators(self, dom: Set["Node"]) -> None:
self._dominators = dom
@property
@ -725,7 +737,7 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
return self._immediate_dominator
@immediate_dominator.setter
def immediate_dominator(self, idom: "Node"):
def immediate_dominator(self, idom: "Node") -> None:
self._immediate_dominator = idom
@property
@ -737,7 +749,7 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
return self._dominance_frontier
@dominance_frontier.setter
def dominance_frontier(self, doms: Set["Node"]):
def dominance_frontier(self, doms: Set["Node"]) -> None:
"""
Returns:
set(Node)
@ -789,6 +801,7 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
def add_phi_origin_local_variable(self, variable: LocalVariable, node: "Node") -> None:
if variable.name not in self._phi_origins_local_variables:
assert variable.name
self._phi_origins_local_variables[variable.name] = (variable, set())
(v, nodes) = self._phi_origins_local_variables[variable.name]
assert v == variable
@ -827,7 +840,8 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
if isinstance(ir, OperationWithLValue):
var = ir.lvalue
if var and self._is_valid_slithir_var(var):
self._slithir_vars.add(var)
# The type is checked by is_valid_slithir_var
self._slithir_vars.add(var) # type: ignore
if not isinstance(ir, (Phi, Index, Member)):
self._vars_read += [v for v in ir.read if self._is_non_slithir_var(v)]
@ -835,8 +849,9 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
if isinstance(var, ReferenceVariable):
self._vars_read.append(var.points_to_origin)
elif isinstance(ir, (Member, Index)):
# TODO investigate types for member variable left
var = ir.variable_left if isinstance(ir, Member) else ir.variable_right
if self._is_non_slithir_var(var):
if var and self._is_non_slithir_var(var):
self._vars_read.append(var)
if isinstance(var, ReferenceVariable):
origin = var.points_to_origin
@ -860,14 +875,21 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
self._internal_calls.append(ir.function)
if isinstance(ir, LowLevelCall):
assert isinstance(ir.destination, (Variable, SolidityVariable))
self._low_level_calls.append((ir.destination, ir.function_name.value))
self._low_level_calls.append((ir.destination, str(ir.function_name.value)))
elif isinstance(ir, HighLevelCall) and not isinstance(ir, LibraryCall):
# Todo investigate this if condition
# It does seem right to compare against a contract
# This might need a refactoring
if isinstance(ir.destination.type, Contract):
self._high_level_calls.append((ir.destination.type, ir.function))
elif ir.destination == SolidityVariable("this"):
self._high_level_calls.append((self.function.contract, ir.function))
func = self.function
# Can't use this in a top level function
assert isinstance(func, FunctionContract)
self._high_level_calls.append((func.contract, ir.function))
else:
try:
# Todo this part needs more tests and documentation
self._high_level_calls.append((ir.destination.type.type, ir.function))
except AttributeError as error:
# pylint: disable=raise-missing-from
@ -883,7 +905,9 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
self._vars_read = list(set(self._vars_read))
self._state_vars_read = [v for v in self._vars_read if isinstance(v, StateVariable)]
self._local_vars_read = [v for v in self._vars_read if isinstance(v, LocalVariable)]
self._solidity_vars_read = [v for v in self._vars_read if isinstance(v, SolidityVariable)]
self._solidity_vars_read = [
v_ for v_ in self._vars_read if isinstance(v_, SolidityVariable)
]
self._vars_written = list(set(self._vars_written))
self._state_vars_written = [v for v in self._vars_written if isinstance(v, StateVariable)]
self._local_vars_written = [v for v in self._vars_written if isinstance(v, LocalVariable)]
@ -895,12 +919,15 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
@staticmethod
def _convert_ssa(v: Variable) -> Optional[Union[StateVariable, LocalVariable]]:
non_ssa_var: Optional[Union[StateVariable, LocalVariable]]
if isinstance(v, StateIRVariable):
contract = v.contract
assert v.name
non_ssa_var = contract.get_state_variable_from_name(v.name)
return non_ssa_var
assert isinstance(v, LocalIRVariable)
function = v.function
assert v.name
non_ssa_var = function.get_local_variable_from_name(v.name)
return non_ssa_var
@ -921,10 +948,11 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
self._ssa_vars_read.append(origin)
elif isinstance(ir, (Member, Index)):
if isinstance(ir.variable_right, (StateIRVariable, LocalIRVariable)):
self._ssa_vars_read.append(ir.variable_right)
if isinstance(ir.variable_right, ReferenceVariable):
origin = ir.variable_right.points_to_origin
variable_right: RVALUE = ir.variable_right
if isinstance(variable_right, (StateIRVariable, LocalIRVariable)):
self._ssa_vars_read.append(variable_right)
if isinstance(variable_right, ReferenceVariable):
origin = variable_right.points_to_origin
if isinstance(origin, (StateIRVariable, LocalIRVariable)):
self._ssa_vars_read.append(origin)
@ -944,20 +972,20 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
self._ssa_local_vars_read = [v for v in self._ssa_vars_read if isinstance(v, LocalVariable)]
self._ssa_vars_written = list(set(self._ssa_vars_written))
self._ssa_state_vars_written = [
v for v in self._ssa_vars_written if isinstance(v, StateVariable)
v for v in self._ssa_vars_written if v and isinstance(v, StateIRVariable)
]
self._ssa_local_vars_written = [
v for v in self._ssa_vars_written if isinstance(v, LocalVariable)
v for v in self._ssa_vars_written if v and isinstance(v, LocalIRVariable)
]
vars_read = [self._convert_ssa(x) for x in self._ssa_vars_read]
vars_written = [self._convert_ssa(x) for x in self._ssa_vars_written]
self._vars_read += [v for v in vars_read if v not in self._vars_read]
self._vars_read += [v_ for v_ in vars_read if v_ and v_ not in self._vars_read]
self._state_vars_read = [v for v in self._vars_read if isinstance(v, StateVariable)]
self._local_vars_read = [v for v in self._vars_read if isinstance(v, LocalVariable)]
self._vars_written += [v for v in vars_written if v not in self._vars_written]
self._vars_written += [v_ for v_ in vars_written if v_ and v_ not in self._vars_written]
self._state_vars_written = [v for v in self._vars_written if isinstance(v, StateVariable)]
self._local_vars_written = [v for v in self._vars_written if isinstance(v, LocalVariable)]
@ -974,7 +1002,7 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
additional_info += " " + str(self.expression)
elif self.variable_declaration:
additional_info += " " + str(self.variable_declaration)
txt = self._node_type.value + additional_info
txt = str(self._node_type.value) + additional_info
return txt

@ -1,19 +0,0 @@
from typing import TYPE_CHECKING
from slither.core.source_mapping.source_mapping import SourceMapping
if TYPE_CHECKING:
from slither.core.declarations import Contract
class ChildContract(SourceMapping):
def __init__(self) -> None:
super().__init__()
self._contract = None
def set_contract(self, contract: "Contract") -> None:
self._contract = contract
@property
def contract(self) -> "Contract":
return self._contract

@ -1,17 +0,0 @@
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from slither.core.declarations import Event
class ChildEvent:
def __init__(self) -> None:
super().__init__()
self._event = None
def set_event(self, event: "Event"):
self._event = event
@property
def event(self) -> "Event":
return self._event

@ -1,18 +0,0 @@
from typing import TYPE_CHECKING, Union
if TYPE_CHECKING:
from slither.core.expressions.expression import Expression
from slither.slithir.operations import Operation
class ChildExpression:
def __init__(self) -> None:
super().__init__()
self._expression = None
def set_expression(self, expression: Union["Expression", "Operation"]) -> None:
self._expression = expression
@property
def expression(self) -> Union["Expression", "Operation"]:
return self._expression

@ -1,17 +0,0 @@
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from slither.core.declarations import Function
class ChildFunction:
def __init__(self) -> None:
super().__init__()
self._function = None
def set_function(self, function: "Function") -> None:
self._function = function
@property
def function(self) -> "Function":
return self._function

@ -1,17 +0,0 @@
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from slither.core.declarations import Contract
class ChildInheritance:
def __init__(self) -> None:
super().__init__()
self._contract_declarer = None
def set_contract_declarer(self, contract: "Contract") -> None:
self._contract_declarer = contract
@property
def contract_declarer(self) -> "Contract":
return self._contract_declarer

@ -1,31 +0,0 @@
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.core.cfg.node import Node
from slither.core.declarations import Function, Contract
class ChildNode:
def __init__(self) -> None:
super().__init__()
self._node = None
def set_node(self, node: "Node") -> None:
self._node = node
@property
def node(self) -> "Node":
return self._node
@property
def function(self) -> "Function":
return self.node.function
@property
def contract(self) -> "Contract":
return self.node.function.contract
@property
def compilation_unit(self) -> "SlitherCompilationUnit":
return self.node.compilation_unit

@ -1,17 +0,0 @@
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from slither.core.declarations import Structure
class ChildStructure:
def __init__(self) -> None:
super().__init__()
self._structure = None
def set_structure(self, structure: "Structure") -> None:
self._structure = structure
@property
def structure(self) -> "Structure":
return self._structure

@ -57,7 +57,7 @@ class SlitherCompilationUnit(Context):
self._storage_layouts: Dict[str, Dict[str, Tuple[int, int]]] = {}
self._contract_with_missing_inheritance = set()
self._contract_with_missing_inheritance: Set[Contract] = set()
self._source_units: Dict[int, str] = {}
@ -88,7 +88,8 @@ class SlitherCompilationUnit(Context):
@property
def solc_version(self) -> str:
return self._crytic_compile_compilation_unit.compiler_version.version
# TODO: make version a non optional argument of compiler version in cc
return self._crytic_compile_compilation_unit.compiler_version.version # type:ignore
@property
def crytic_compile_compilation_unit(self) -> CompilationUnit:
@ -162,13 +163,14 @@ class SlitherCompilationUnit(Context):
@property
def functions_and_modifiers(self) -> List[Function]:
return self.functions + self.modifiers
return self.functions + list(self.modifiers)
def propagate_function_calls(self) -> None:
for f in self.functions_and_modifiers:
for node in f.nodes:
for ir in node.irs_ssa:
if isinstance(ir, InternalCall):
assert ir.function
ir.function.add_reachable_from_node(node, ir)
# endregion
@ -181,8 +183,8 @@ class SlitherCompilationUnit(Context):
@property
def state_variables(self) -> List[StateVariable]:
if self._all_state_variables is None:
state_variables = [c.state_variables for c in self.contracts]
state_variables = [item for sublist in state_variables for item in sublist]
state_variabless = [c.state_variables for c in self.contracts]
state_variables = [item for sublist in state_variabless for item in sublist]
self._all_state_variables = set(state_variables)
return list(self._all_state_variables)
@ -229,7 +231,7 @@ class SlitherCompilationUnit(Context):
###################################################################################
@property
def contracts_with_missing_inheritance(self) -> Set:
def contracts_with_missing_inheritance(self) -> Set[Contract]:
return self._contract_with_missing_inheritance
# endregion
@ -266,6 +268,7 @@ class SlitherCompilationUnit(Context):
if var.is_constant or var.is_immutable:
continue
assert var.type
size, new_slot = var.type.storage_size
if new_slot:
@ -285,7 +288,7 @@ class SlitherCompilationUnit(Context):
else:
offset += size
def storage_layout_of(self, contract, var) -> Tuple[int, int]:
def storage_layout_of(self, contract: Contract, var: StateVariable) -> Tuple[int, int]:
return self._storage_layouts[contract.name][var.canonical_name]
# endregion

@ -49,6 +49,9 @@ if TYPE_CHECKING:
LOGGER = logging.getLogger("Contract")
USING_FOR_KEY = Union[str, Type]
USING_FOR_ITEM = List[Union[Type, Function]]
class Contract(SourceMapping): # pylint: disable=too-many-public-methods
"""
@ -80,8 +83,8 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
self._custom_errors: Dict[str, "CustomErrorContract"] = {}
# The only str is "*"
self._using_for: Dict[Union[str, Type], List[Type]] = {}
self._using_for_complete: Dict[Union[str, Type], List[Type]] = None
self._using_for: Dict[USING_FOR_KEY, USING_FOR_ITEM] = {}
self._using_for_complete: Optional[Dict[USING_FOR_KEY, USING_FOR_ITEM]] = None
self._kind: Optional[str] = None
self._is_interface: bool = False
self._is_library: bool = False
@ -126,7 +129,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
return self._name
@name.setter
def name(self, name: str):
def name(self, name: str) -> None:
self._name = name
@property
@ -136,7 +139,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
return self._id
@id.setter
def id(self, new_id):
def id(self, new_id: int) -> None:
"""Unique id."""
self._id = new_id
@ -149,7 +152,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
return self._kind
@contract_kind.setter
def contract_kind(self, kind):
def contract_kind(self, kind: str) -> None:
self._kind = kind
@property
@ -157,7 +160,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
return self._is_interface
@is_interface.setter
def is_interface(self, is_interface: bool):
def is_interface(self, is_interface: bool) -> None:
self._is_interface = is_interface
@property
@ -165,7 +168,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
return self._is_library
@is_library.setter
def is_library(self, is_library: bool):
def is_library(self, is_library: bool) -> None:
self._is_library = is_library
@property
@ -302,16 +305,18 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
###################################################################################
@property
def using_for(self) -> Dict[Union[str, Type], List[Type]]:
def using_for(self) -> Dict[USING_FOR_KEY, USING_FOR_ITEM]:
return self._using_for
@property
def using_for_complete(self) -> Dict[Union[str, Type], List[Type]]:
def using_for_complete(self) -> Dict[USING_FOR_KEY, USING_FOR_ITEM]:
"""
Dict[Union[str, Type], List[Type]]: Dict of merged local using for directive with top level directive
"""
def _merge_using_for(uf1, uf2):
def _merge_using_for(
uf1: Dict[USING_FOR_KEY, USING_FOR_ITEM], uf2: Dict[USING_FOR_KEY, USING_FOR_ITEM]
) -> Dict[USING_FOR_KEY, USING_FOR_ITEM]:
result = {**uf1, **uf2}
for key, value in result.items():
if key in uf1 and key in uf2:
@ -491,7 +496,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
)
@property
def constructors(self) -> List["Function"]:
def constructors(self) -> List["FunctionContract"]:
"""
Return the list of constructors (including inherited)
"""
@ -560,14 +565,14 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
"""
return list(self._functions.values())
def available_functions_as_dict(self) -> Dict[str, "FunctionContract"]:
def available_functions_as_dict(self) -> Dict[str, "Function"]:
if self._available_functions_as_dict is None:
self._available_functions_as_dict = {
f.full_name: f for f in self._functions.values() if not f.is_shadowed
}
return self._available_functions_as_dict
def add_function(self, func: "FunctionContract"):
def add_function(self, func: "FunctionContract") -> None:
self._functions[func.canonical_name] = func
def set_functions(self, functions: Dict[str, "FunctionContract"]) -> None:
@ -735,7 +740,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
list(Contract): Return the list of contracts derived from self
"""
candidates = self.compilation_unit.contracts
return [c for c in candidates if self in c.inheritance]
return [c for c in candidates if self in c.inheritance] # type: ignore
# endregion
###################################################################################
@ -891,7 +896,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
"""
return next((e for e in self.enums if e.name == enum_name), None)
def get_enum_from_canonical_name(self, enum_name) -> Optional["Enum"]:
def get_enum_from_canonical_name(self, enum_name: str) -> Optional["Enum"]:
"""
Return an enum from a canonical name
Args:
@ -992,7 +997,9 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
###################################################################################
###################################################################################
def get_summary(self, include_shadowed=True) -> Tuple[str, List[str], List[str], List, List]:
def get_summary(
self, include_shadowed: bool = True
) -> Tuple[str, List[str], List[str], List, List]:
"""Return the function summary
:param include_shadowed: boolean to indicate if shadowed functions should be included (default True)
@ -1245,7 +1252,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
@property
def is_test(self) -> bool:
return is_test_contract(self) or self.is_truffle_migration
return is_test_contract(self) or self.is_truffle_migration # type: ignore
# endregion
###################################################################################
@ -1255,7 +1262,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
###################################################################################
def update_read_write_using_ssa(self) -> None:
for function in self.functions + self.modifiers:
for function in self.functions + list(self.modifiers):
function.update_read_write_using_ssa()
# endregion
@ -1290,7 +1297,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
return self._is_upgradeable
@is_upgradeable.setter
def is_upgradeable(self, upgradeable: bool):
def is_upgradeable(self, upgradeable: bool) -> None:
self._is_upgradeable = upgradeable
@property
@ -1319,7 +1326,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
return self._is_upgradeable_proxy
@is_upgradeable_proxy.setter
def is_upgradeable_proxy(self, upgradeable_proxy: bool):
def is_upgradeable_proxy(self, upgradeable_proxy: bool) -> None:
self._is_upgradeable_proxy = upgradeable_proxy
@property
@ -1327,7 +1334,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
return self._upgradeable_version
@upgradeable_version.setter
def upgradeable_version(self, version_name: str):
def upgradeable_version(self, version_name: str) -> None:
self._upgradeable_version = version_name
# endregion
@ -1346,7 +1353,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
return self._is_incorrectly_parsed
@is_incorrectly_constructed.setter
def is_incorrectly_constructed(self, incorrect: bool):
def is_incorrectly_constructed(self, incorrect: bool) -> None:
self._is_incorrectly_parsed = incorrect
def add_constructor_variables(self) -> None:
@ -1358,8 +1365,8 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
constructor_variable = FunctionContract(self.compilation_unit)
constructor_variable.set_function_type(FunctionType.CONSTRUCTOR_VARIABLES)
constructor_variable.set_contract(self)
constructor_variable.set_contract_declarer(self)
constructor_variable.set_contract(self) # type: ignore
constructor_variable.set_contract_declarer(self) # type: ignore
constructor_variable.set_visibility("internal")
# For now, source mapping of the constructor variable is the whole contract
# Could be improved with a targeted source mapping
@ -1390,8 +1397,8 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
constructor_variable.set_function_type(
FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES
)
constructor_variable.set_contract(self)
constructor_variable.set_contract_declarer(self)
constructor_variable.set_contract(self) # type: ignore
constructor_variable.set_contract_declarer(self) # type: ignore
constructor_variable.set_visibility("internal")
# For now, source mapping of the constructor variable is the whole contract
# Could be improved with a targeted source mapping
@ -1472,22 +1479,23 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
all_ssa_state_variables_instances[v.canonical_name] = new_var
self._initial_state_variables.append(new_var)
for func in self.functions + self.modifiers:
for func in self.functions + list(self.modifiers):
func.generate_slithir_ssa(all_ssa_state_variables_instances)
def fix_phi(self) -> None:
last_state_variables_instances = {}
initial_state_variables_instances = {}
last_state_variables_instances: Dict[str, List["StateVariable"]] = {}
initial_state_variables_instances: Dict[str, "StateVariable"] = {}
for v in self._initial_state_variables:
last_state_variables_instances[v.canonical_name] = []
initial_state_variables_instances[v.canonical_name] = v
for func in self.functions + self.modifiers:
for func in self.functions + list(self.modifiers):
result = func.get_last_ssa_state_variables_instances()
for variable_name, instances in result.items():
last_state_variables_instances[variable_name] += instances
# TODO: investigate the next operation
last_state_variables_instances[variable_name] += list(instances)
for func in self.functions + self.modifiers:
for func in self.functions + list(self.modifiers):
func.fix_phi(last_state_variables_instances, initial_state_variables_instances)
# endregion
@ -1497,7 +1505,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
###################################################################################
###################################################################################
def __eq__(self, other: SourceMapping) -> bool:
def __eq__(self, other: Any) -> bool:
if isinstance(other, str):
return other == self.name
return NotImplemented
@ -1511,6 +1519,6 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
return self.name
def __hash__(self) -> int:
return self._id
return self._id # type:ignore
# endregion

@ -0,0 +1,29 @@
from typing import TYPE_CHECKING, Optional
from slither.core.source_mapping.source_mapping import SourceMapping
if TYPE_CHECKING:
from slither.core.declarations import Contract
class ContractLevel(SourceMapping):
"""
This class is used to represent objects that are at the contract level
The opposite is TopLevel
"""
def __init__(self) -> None:
super().__init__()
# TODO remove all the setters for the child objects
# And make it a constructor arguement
# This will remove the optional
self._contract: Optional["Contract"] = None
def set_contract(self, contract: "Contract") -> None:
self._contract = contract
@property
def contract(self) -> "Contract":
assert self._contract
return self._contract

@ -1,4 +1,4 @@
from typing import List, TYPE_CHECKING, Optional, Type, Union
from typing import List, TYPE_CHECKING, Optional, Type
from slither.core.solidity_types import UserDefinedType
from slither.core.source_mapping.source_mapping import SourceMapping
@ -42,7 +42,7 @@ class CustomError(SourceMapping):
###################################################################################
@staticmethod
def _convert_type_for_solidity_signature(t: Optional[Union[Type, List[Type]]]) -> str:
def _convert_type_for_solidity_signature(t: Optional[Type]) -> str:
# pylint: disable=import-outside-toplevel
from slither.core.declarations import Contract
@ -51,7 +51,7 @@ class CustomError(SourceMapping):
return str(t)
@property
def solidity_signature(self) -> Optional[str]:
def solidity_signature(self) -> str:
"""
Return a signature following the Solidity Standard
Contract and converted into address
@ -63,7 +63,7 @@ class CustomError(SourceMapping):
# (set_solidity_sig was not called before find_variable)
if self._solidity_signature is None:
raise ValueError("Custom Error not yet built")
return self._solidity_signature
return self._solidity_signature # type: ignore
def set_solidity_sig(self) -> None:
"""
@ -72,7 +72,7 @@ class CustomError(SourceMapping):
Returns:
"""
parameters = [x.type for x in self.parameters]
parameters = [x.type for x in self.parameters if x.type]
self._full_name = self.name + "(" + ",".join(map(str, parameters)) + ")"
solidity_parameters = map(self._convert_type_for_solidity_signature, parameters)
self._solidity_signature = self.name + "(" + ",".join(solidity_parameters) + ")"

@ -1,9 +1,15 @@
from slither.core.children.child_contract import ChildContract
from typing import TYPE_CHECKING
from slither.core.declarations.contract_level import ContractLevel
from slither.core.declarations.custom_error import CustomError
if TYPE_CHECKING:
from slither.core.declarations import Contract
class CustomErrorContract(CustomError, ChildContract):
def is_declared_by(self, contract):
class CustomErrorContract(CustomError, ContractLevel):
def is_declared_by(self, contract: "Contract") -> bool:
"""
Check if the element is declared by the contract
:param contract:

@ -9,6 +9,6 @@ if TYPE_CHECKING:
class CustomErrorTopLevel(CustomError, TopLevel):
def __init__(self, compilation_unit: "SlitherCompilationUnit", scope: "FileScope"):
def __init__(self, compilation_unit: "SlitherCompilationUnit", scope: "FileScope") -> None:
super().__init__(compilation_unit)
self.file_scope: "FileScope" = scope

@ -1,13 +1,13 @@
from typing import TYPE_CHECKING
from slither.core.children.child_contract import ChildContract
from slither.core.declarations.contract_level import ContractLevel
from slither.core.declarations import Enum
if TYPE_CHECKING:
from slither.core.declarations import Contract
class EnumContract(Enum, ChildContract):
class EnumContract(Enum, ContractLevel):
def is_declared_by(self, contract: "Contract") -> bool:
"""
Check if the element is declared by the contract

@ -1,6 +1,6 @@
from typing import List, Tuple, TYPE_CHECKING
from slither.core.children.child_contract import ChildContract
from slither.core.declarations.contract_level import ContractLevel
from slither.core.source_mapping.source_mapping import SourceMapping
from slither.core.variables.event_variable import EventVariable
@ -8,7 +8,7 @@ if TYPE_CHECKING:
from slither.core.declarations import Contract
class Event(ChildContract, SourceMapping):
class Event(ContractLevel, SourceMapping):
def __init__(self) -> None:
super().__init__()
self._name = None

@ -47,7 +47,6 @@ if TYPE_CHECKING:
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.core.scope.scope import FileScope
from slither.slithir.variables.state_variable import StateIRVariable
from slither.core.declarations.function_contract import FunctionContract
LOGGER = logging.getLogger("Function")
ReacheableNode = namedtuple("ReacheableNode", ["node", "ir"])
@ -298,7 +297,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
def contains_assembly(self, c: bool):
self._contains_assembly = c
def can_reenter(self, callstack: Optional[List["FunctionContract"]] = None) -> bool:
def can_reenter(self, callstack: Optional[List[Union["Function", "Variable"]]] = None) -> bool:
"""
Check if the function can re-enter
Follow internal calls.
@ -1720,8 +1719,8 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
def fix_phi(
self,
last_state_variables_instances: Dict[str, List["StateIRVariable"]],
initial_state_variables_instances: Dict[str, "StateIRVariable"],
last_state_variables_instances: Dict[str, List["StateVariable"]],
initial_state_variables_instances: Dict[str, "StateVariable"],
) -> None:
from slither.slithir.operations import InternalCall, PhiCallback
from slither.slithir.variables import Constant, StateIRVariable

@ -1,10 +1,9 @@
"""
Function module
"""
from typing import Dict, TYPE_CHECKING, List, Tuple
from typing import Dict, TYPE_CHECKING, List, Tuple, Optional
from slither.core.children.child_contract import ChildContract
from slither.core.children.child_inheritance import ChildInheritance
from slither.core.declarations.contract_level import ContractLevel
from slither.core.declarations import Function
from slither.utils.code_complexity import compute_cyclomatic_complexity
@ -15,9 +14,31 @@ if TYPE_CHECKING:
from slither.core.declarations import Contract
from slither.core.scope.scope import FileScope
from slither.slithir.variables.state_variable import StateIRVariable
from slither.core.compilation_unit import SlitherCompilationUnit
class FunctionContract(Function, ChildContract, ChildInheritance):
class FunctionContract(Function, ContractLevel):
def __init__(self, compilation_unit: "SlitherCompilationUnit") -> None:
super().__init__(compilation_unit)
self._contract_declarer: Optional["Contract"] = None
def set_contract_declarer(self, contract: "Contract") -> None:
self._contract_declarer = contract
@property
def contract_declarer(self) -> "Contract":
"""
Return the contract where this function was declared. Only functions have both a contract, and contract_declarer
This is because we need to have separate representation of the function depending of the contract's context
For example a function calling super.f() will generate different IR depending on the current contract's inheritance
Returns:
The contract where this function was declared
"""
assert self._contract_declarer
return self._contract_declarer
@property
def canonical_name(self) -> str:
"""

@ -82,7 +82,7 @@ SOLIDITY_FUNCTIONS: Dict[str, List[str]] = {
}
def solidity_function_signature(name):
def solidity_function_signature(name: str) -> str:
"""
Return the function signature (containing the return value)
It is useful if a solidity function is used as a pointer
@ -106,7 +106,7 @@ class SolidityVariable(SourceMapping):
assert name in SOLIDITY_VARIABLES or name.endswith(("_slot", "_offset"))
@property
def state_variable(self):
def state_variable(self) -> str:
if self._name.endswith("_slot"):
return self._name[:-5]
if self._name.endswith("_offset"):
@ -125,7 +125,7 @@ class SolidityVariable(SourceMapping):
def __str__(self) -> str:
return self._name
def __eq__(self, other: SourceMapping) -> bool:
def __eq__(self, other: Any) -> bool:
return self.__class__ == other.__class__ and self.name == other.name
def __hash__(self) -> int:
@ -182,13 +182,13 @@ class SolidityFunction(SourceMapping):
return self._return_type
@return_type.setter
def return_type(self, r: List[Union[TypeInformation, ElementaryType]]):
def return_type(self, r: List[Union[TypeInformation, ElementaryType]]) -> None:
self._return_type = r
def __str__(self) -> str:
return self._name
def __eq__(self, other: "SolidityFunction") -> bool:
def __eq__(self, other: Any) -> bool:
return self.__class__ == other.__class__ and self.name == other.name
def __hash__(self) -> int:
@ -201,7 +201,7 @@ class SolidityCustomRevert(SolidityFunction):
self._custom_error = custom_error
self._return_type: List[Union[TypeInformation, ElementaryType]] = []
def __eq__(self, other: Union["SolidityCustomRevert", SolidityFunction]) -> bool:
def __eq__(self, other: Any) -> bool:
return (
self.__class__ == other.__class__
and self.name == other.name

@ -1,8 +1,8 @@
from slither.core.children.child_contract import ChildContract
from slither.core.declarations.contract_level import ContractLevel
from slither.core.declarations import Structure
class StructureContract(Structure, ChildContract):
class StructureContract(Structure, ContractLevel):
def is_declared_by(self, contract):
"""
Check if the element is declared by the contract

@ -2,4 +2,8 @@ from slither.core.source_mapping.source_mapping import SourceMapping
class TopLevel(SourceMapping):
pass
"""
This class is used to represent objects that are at the top level
The opposite is ContractLevel
"""

@ -1,5 +1,6 @@
from typing import TYPE_CHECKING, List, Dict, Union
from slither.core.declarations.contract import USING_FOR_KEY, USING_FOR_ITEM
from slither.core.solidity_types.type import Type
from slither.core.declarations.top_level import TopLevel
@ -14,5 +15,5 @@ class UsingForTopLevel(TopLevel):
self.file_scope: "FileScope" = scope
@property
def using_for(self) -> Dict[Union[str, Type], List[Type]]:
def using_for(self) -> Dict[USING_FOR_KEY, USING_FOR_ITEM]:
return self._using_for

@ -95,4 +95,5 @@ def compute_dominance_frontier(nodes: List["Node"]) -> None:
runner.dominance_frontier = runner.dominance_frontier.union({node})
while runner != node.immediate_dominator:
runner.dominance_frontier = runner.dominance_frontier.union({node})
assert runner.immediate_dominator
runner = runner.immediate_dominator

@ -2,7 +2,6 @@ import logging
from enum import Enum
from typing import Optional, TYPE_CHECKING, List
from slither.core.expressions.expression_typed import ExpressionTyped
from slither.core.expressions.expression import Expression
from slither.core.exceptions import SlitherCoreError
@ -78,7 +77,7 @@ class AssignmentOperationType(Enum):
raise SlitherCoreError(f"str: Unknown operation type {self})")
class AssignmentOperation(ExpressionTyped):
class AssignmentOperation(Expression):
def __init__(
self,
left_expression: Expression,
@ -91,7 +90,7 @@ class AssignmentOperation(ExpressionTyped):
super().__init__()
left_expression.set_lvalue()
self._expressions = [left_expression, right_expression]
self._type: Optional["AssignmentOperationType"] = expression_type
self._type: AssignmentOperationType = expression_type
self._expression_return_type: Optional["Type"] = expression_return_type
@property

@ -2,7 +2,6 @@ import logging
from enum import Enum
from typing import List
from slither.core.expressions.expression_typed import ExpressionTyped
from slither.core.expressions.expression import Expression
from slither.core.exceptions import SlitherCoreError
@ -148,7 +147,7 @@ class BinaryOperationType(Enum):
raise SlitherCoreError(f"str: Unknown operation type {self})")
class BinaryOperation(ExpressionTyped):
class BinaryOperation(Expression):
def __init__(
self,
left_expression: Expression,

@ -22,7 +22,7 @@ class CallExpression(Expression): # pylint: disable=too-many-instance-attribute
return self._value
@call_value.setter
def call_value(self, v):
def call_value(self, v: Optional[Expression]) -> None:
self._value = v
@property
@ -30,15 +30,15 @@ class CallExpression(Expression): # pylint: disable=too-many-instance-attribute
return self._gas
@call_gas.setter
def call_gas(self, gas):
def call_gas(self, gas: Optional[Expression]) -> None:
self._gas = gas
@property
def call_salt(self):
def call_salt(self) -> Optional[Expression]:
return self._salt
@call_salt.setter
def call_salt(self, salt):
def call_salt(self, salt: Optional[Expression]) -> None:
self._salt = salt
@property

@ -42,7 +42,7 @@ class ConditionalExpression(Expression):
def then_expression(self) -> Expression:
return self._then_expression
def __str__(self):
def __str__(self) -> str:
return (
"if "
+ str(self._if_expression)

@ -1,20 +0,0 @@
from typing import Optional, TYPE_CHECKING
from slither.core.expressions.expression import Expression
if TYPE_CHECKING:
from slither.core.solidity_types.type import Type
class ExpressionTyped(Expression): # pylint: disable=too-few-public-methods
def __init__(self) -> None:
super().__init__()
self._type: Optional["Type"] = None
@property
def type(self) -> Optional["Type"]:
return self._type
@type.setter
def type(self, new_type: "Type"):
self._type = new_type

@ -1,18 +1,80 @@
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Optional, Union
from slither.core.declarations.contract_level import ContractLevel
from slither.core.declarations.top_level import TopLevel
from slither.core.expressions.expression import Expression
from slither.core.variables.variable import Variable
from slither.core.expressions.expression_typed import ExpressionTyped
if TYPE_CHECKING:
from slither.core.variables.variable import Variable
from slither.core.solidity_types.type import Type
from slither.core.declarations import Contract, SolidityVariable, SolidityFunction
from slither.solc_parsing.yul.evm_functions import YulBuiltin
class Identifier(ExpressionTyped):
def __init__(self, value) -> None:
class Identifier(Expression):
def __init__(
self,
value: Union[
Variable,
"TopLevel",
"ContractLevel",
"Contract",
"SolidityVariable",
"SolidityFunction",
"YulBuiltin",
],
) -> None:
super().__init__()
self._value: "Variable" = value
# pylint: disable=import-outside-toplevel
from slither.core.declarations import Contract, SolidityVariable, SolidityFunction
from slither.solc_parsing.yul.evm_functions import YulBuiltin
assert isinstance(
value,
(
Variable,
TopLevel,
ContractLevel,
Contract,
SolidityVariable,
SolidityFunction,
YulBuiltin,
),
)
self._value: Union[
Variable,
"TopLevel",
"ContractLevel",
"Contract",
"SolidityVariable",
"SolidityFunction",
"YulBuiltin",
] = value
self._type: Optional["Type"] = None
@property
def type(self) -> Optional["Type"]:
return self._type
@type.setter
def type(self, new_type: "Type") -> None:
self._type = new_type
@property
def value(self) -> "Variable":
def value(
self,
) -> Union[
Variable,
"TopLevel",
"ContractLevel",
"Contract",
"SolidityVariable",
"SolidityFunction",
"YulBuiltin",
]:
return self._value
def __str__(self) -> str:

@ -1,27 +1,18 @@
from typing import Union, List, TYPE_CHECKING
from typing import Union, List
from slither.core.expressions.expression_typed import ExpressionTyped
from slither.core.expressions.expression import Expression
from slither.core.expressions.identifier import Identifier
from slither.core.expressions.literal import Literal
if TYPE_CHECKING:
from slither.core.expressions.expression import Expression
from slither.core.solidity_types.type import Type
class IndexAccess(ExpressionTyped):
class IndexAccess(Expression):
def __init__(
self,
left_expression: Union["IndexAccess", Identifier],
right_expression: Union[Literal, Identifier],
index_type: str,
) -> None:
super().__init__()
self._expressions = [left_expression, right_expression]
# TODO type of undexAccess is not always a Type
# assert isinstance(index_type, Type)
self._type: "Type" = index_type
@property
def expressions(self) -> List["Expression"]:
@ -35,9 +26,5 @@ class IndexAccess(ExpressionTyped):
def expression_right(self) -> "Expression":
return self._expressions[1]
@property
def type(self) -> "Type":
return self._type
def __str__(self) -> str:
return str(self.expression_left) + "[" + str(self.expression_right) + "]"

@ -1,4 +1,4 @@
from typing import Optional, Union, TYPE_CHECKING
from typing import Optional, Union, TYPE_CHECKING, Any
from slither.core.expressions.expression import Expression
from slither.core.solidity_types.elementary_type import Fixed, Int, Ufixed, Uint
@ -47,7 +47,7 @@ class Literal(Expression):
# be sure to handle any character
return str(self._value)
def __eq__(self, other) -> bool:
def __eq__(self, other: Any) -> bool:
if not isinstance(other, Literal):
return False
return (self.value, self.subdenomination) == (other.value, other.subdenomination)

@ -1,10 +1,9 @@
from slither.core.expressions.expression import Expression
from slither.core.expressions.expression_typed import ExpressionTyped
from slither.core.solidity_types.type import Type
class MemberAccess(ExpressionTyped):
class MemberAccess(Expression):
def __init__(self, member_name: str, member_type: str, expression: Expression) -> None:
# assert isinstance(member_type, Type)
# TODO member_type is not always a Type

@ -1,6 +1,5 @@
from typing import Union, TYPE_CHECKING
from slither.core.expressions.expression_typed import ExpressionTyped
from slither.core.expressions.expression import Expression
from slither.core.solidity_types.type import Type
@ -14,7 +13,7 @@ if TYPE_CHECKING:
from slither.core.solidity_types.user_defined_type import UserDefinedType
class TypeConversion(ExpressionTyped):
class TypeConversion(Expression):
def __init__(
self,
expression: Union[
@ -28,6 +27,14 @@ class TypeConversion(ExpressionTyped):
self._expression: Expression = expression
self._type: Type = expression_type
@property
def type(self) -> Type:
return self._type
@type.setter
def type(self, new_type: Type) -> None:
self._type = new_type
@property
def expression(self) -> Expression:
return self._expression

@ -2,7 +2,6 @@ import logging
from typing import Union
from enum import Enum
from slither.core.expressions.expression_typed import ExpressionTyped
from slither.core.expressions.expression import Expression
from slither.core.exceptions import SlitherCoreError
from slither.core.expressions.identifier import Identifier
@ -91,7 +90,7 @@ class UnaryOperationType(Enum):
raise SlitherCoreError(f"is_prefix: Unknown operation type {operation_type}")
class UnaryOperation(ExpressionTyped):
class UnaryOperation(Expression):
def __init__(
self,
expression: Union[Literal, Identifier, IndexAccess, TupleExpression],

@ -13,7 +13,7 @@ from typing import Optional, Dict, List, Set, Union, Tuple
from crytic_compile import CryticCompile
from crytic_compile.utils.naming import Filename
from slither.core.children.child_contract import ChildContract
from slither.core.declarations.contract_level import ContractLevel
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.core.context.context import Context
from slither.core.declarations import Contract, FunctionContract
@ -206,7 +206,7 @@ class SlitherCore(Context):
isinstance(thing, FunctionContract)
and thing.contract_declarer == thing.contract
)
or (isinstance(thing, ChildContract) and not isinstance(thing, FunctionContract))
or (isinstance(thing, ContractLevel) and not isinstance(thing, FunctionContract))
):
self._offset_to_objects[definition.filename][offset].add(thing)
@ -224,7 +224,7 @@ class SlitherCore(Context):
and thing.contract_declarer == thing.contract
)
or (
isinstance(thing, ChildContract) and not isinstance(thing, FunctionContract)
isinstance(thing, ContractLevel) and not isinstance(thing, FunctionContract)
)
):
self._offset_to_objects[definition.filename][offset].add(thing)
@ -482,8 +482,8 @@ class SlitherCore(Context):
###################################################################################
@property
def crytic_compile(self) -> Optional[CryticCompile]:
return self._crytic_compile
def crytic_compile(self) -> CryticCompile:
return self._crytic_compile # type: ignore
# endregion
###################################################################################

@ -1,38 +1,37 @@
from typing import Union, Optional, Tuple, Any, TYPE_CHECKING
from slither.core.expressions.expression import Expression
from slither.core.expressions.literal import Literal
from slither.core.solidity_types.elementary_type import ElementaryType
from slither.core.solidity_types.type import Type
from slither.visitors.expression.constants_folding import ConstantFolding
from slither.core.expressions.literal import Literal
if TYPE_CHECKING:
from slither.core.expressions.binary_operation import BinaryOperation
from slither.core.expressions.identifier import Identifier
from slither.core.solidity_types.elementary_type import ElementaryType
from slither.core.solidity_types.function_type import FunctionType
from slither.core.solidity_types.type_alias import TypeAliasTopLevel
class ArrayType(Type):
def __init__(
self,
t: Union["TypeAliasTopLevel", "ArrayType", "FunctionType", "ElementaryType"],
t: Type,
length: Optional[Union["Identifier", Literal, "BinaryOperation", int]],
) -> None:
assert isinstance(t, Type)
if length:
if isinstance(length, int):
length = Literal(length, "uint256")
assert isinstance(length, Expression)
length = Literal(length, ElementaryType("uint256"))
super().__init__()
self._type: Type = t
assert length is None or isinstance(length, Expression)
self._length: Optional[Expression] = length
if length:
if not isinstance(length, Literal):
cf = ConstantFolding(length, "uint256")
length = cf.result()
self._length_value = length
self._length_value: Optional[Literal] = length
else:
self._length_value = None

@ -1,5 +1,5 @@
import itertools
from typing import Tuple
from typing import Tuple, Optional, Any
from slither.core.solidity_types.type import Type
@ -176,7 +176,7 @@ class ElementaryType(Type):
return self.type
@property
def size(self) -> int:
def size(self) -> Optional[int]:
"""
Return the size in bits
Return None if the size is not known
@ -219,7 +219,7 @@ class ElementaryType(Type):
def __str__(self) -> str:
return self._type
def __eq__(self, other) -> bool:
def __eq__(self, other: Any) -> bool:
if not isinstance(other, ElementaryType):
return False
return self.type == other.type

@ -1,4 +1,4 @@
from typing import Union, Tuple, TYPE_CHECKING
from typing import Union, Tuple, TYPE_CHECKING, Any
from slither.core.solidity_types.type import Type
@ -38,7 +38,7 @@ class MappingType(Type):
def __str__(self) -> str:
return f"mapping({str(self._from)} => {str(self._to)})"
def __eq__(self, other):
def __eq__(self, other: Any) -> bool:
if not isinstance(other, MappingType):
return False
return self.type_from == other.type_from and self.type_to == other.type_to

@ -1,7 +1,7 @@
from typing import TYPE_CHECKING, Tuple
from slither.core.children.child_contract import ChildContract
from slither.core.declarations.top_level import TopLevel
from slither.core.declarations.contract_level import ContractLevel
from slither.core.solidity_types import Type, ElementaryType
if TYPE_CHECKING:
@ -40,7 +40,7 @@ class TypeAlias(Type):
class TypeAliasTopLevel(TypeAlias, TopLevel):
def __init__(self, underlying_type: Type, name: str, scope: "FileScope") -> None:
def __init__(self, underlying_type: ElementaryType, name: str, scope: "FileScope") -> None:
super().__init__(underlying_type, name)
self.file_scope: "FileScope" = scope
@ -48,8 +48,8 @@ class TypeAliasTopLevel(TypeAlias, TopLevel):
return self.name
class TypeAliasContract(TypeAlias, ChildContract):
def __init__(self, underlying_type: Type, name: str, contract: "Contract") -> None:
class TypeAliasContract(TypeAlias, ContractLevel):
def __init__(self, underlying_type: ElementaryType, name: str, contract: "Contract") -> None:
super().__init__(underlying_type, name)
self._contract: "Contract" = contract

@ -1,4 +1,4 @@
from typing import Union, TYPE_CHECKING, Tuple
from typing import Union, TYPE_CHECKING, Tuple, Any
from slither.core.solidity_types import ElementaryType
from slither.core.solidity_types.type import Type
@ -40,10 +40,10 @@ class TypeInformation(Type):
def is_dynamic(self) -> bool:
raise NotImplementedError
def __str__(self):
def __str__(self) -> str:
return f"type({self.type.name})"
def __eq__(self, other):
def __eq__(self, other: Any) -> bool:
if not isinstance(other, TypeInformation):
return False
return self.type == other.type

@ -1,6 +1,6 @@
import re
from abc import ABCMeta
from typing import Dict, Union, List, Tuple, TYPE_CHECKING, Optional
from typing import Dict, Union, List, Tuple, TYPE_CHECKING, Optional, Any
from Crypto.Hash import SHA1
from crytic_compile.utils.naming import Filename
@ -98,10 +98,10 @@ class Source:
filename_short: str = self.filename.short if self.filename.short else ""
return f"{filename_short}{lines}"
def __hash__(self):
def __hash__(self) -> int:
return hash(str(self))
def __eq__(self, other):
def __eq__(self, other: Any) -> bool:
if not isinstance(other, type(self)):
return NotImplemented
return (

@ -1,8 +1,7 @@
from slither.core.variables.variable import Variable
from slither.core.children.child_event import ChildEvent
class EventVariable(ChildEvent, Variable):
class EventVariable(Variable):
def __init__(self) -> None:
super().__init__()
self._indexed = False
@ -16,5 +15,5 @@ class EventVariable(ChildEvent, Variable):
return self._indexed
@indexed.setter
def indexed(self, is_indexed: bool):
def indexed(self, is_indexed: bool) -> None:
self._indexed = is_indexed

@ -1,7 +1,6 @@
from typing import Optional
from typing import Optional, TYPE_CHECKING
from slither.core.variables.variable import Variable
from slither.core.children.child_function import ChildFunction
from slither.core.solidity_types.user_defined_type import UserDefinedType
from slither.core.solidity_types.array_type import ArrayType
from slither.core.solidity_types.mapping_type import MappingType
@ -9,11 +8,23 @@ from slither.core.solidity_types.elementary_type import ElementaryType
from slither.core.declarations.structure import Structure
if TYPE_CHECKING: # type: ignore
from slither.core.declarations import Function
class LocalVariable(ChildFunction, Variable):
class LocalVariable(Variable):
def __init__(self) -> None:
super().__init__()
self._location: Optional[str] = None
self._function: Optional["Function"] = None
def set_function(self, function: "Function") -> None:
self._function = function
@property
def function(self) -> "Function":
assert self._function
return self._function
def set_location(self, loc: str) -> None:
self._location = loc

@ -1,6 +1,6 @@
from typing import Optional, TYPE_CHECKING
from slither.core.children.child_contract import ChildContract
from slither.core.declarations.contract_level import ContractLevel
from slither.core.variables.variable import Variable
if TYPE_CHECKING:
@ -8,7 +8,7 @@ if TYPE_CHECKING:
from slither.core.declarations import Contract
class StateVariable(ChildContract, Variable):
class StateVariable(ContractLevel, Variable):
def __init__(self) -> None:
super().__init__()
self._node_initialization: Optional["Node"] = None

@ -1,6 +1,19 @@
from typing import TYPE_CHECKING, Optional
from slither.core.variables.variable import Variable
from slither.core.children.child_structure import ChildStructure
class StructureVariable(ChildStructure, Variable):
pass
if TYPE_CHECKING:
from slither.core.declarations import Structure
class StructureVariable(Variable):
def __init__(self) -> None:
super().__init__()
self._structure: Optional["Structure"] = None
def set_structure(self, structure: "Structure") -> None:
self._structure = structure
@property
def structure(self) -> "Structure":
return self._structure

@ -55,7 +55,7 @@ class Variable(SourceMapping):
return self._initialized
@initialized.setter
def initialized(self, is_init: bool):
def initialized(self, is_init: bool) -> None:
self._initialized = is_init
@property
@ -73,23 +73,24 @@ class Variable(SourceMapping):
return self._name
@name.setter
def name(self, name):
def name(self, name: str) -> None:
self._name = name
@property
def type(self) -> Optional[Union[Type, List[Type]]]:
def type(self) -> Optional[Type]:
return self._type
@type.setter
def type(self, types: Union[Type, List[Type]]):
self._type = types
def type(self, new_type: Type) -> None:
assert isinstance(new_type, Type)
self._type = new_type
@property
def is_constant(self) -> bool:
return self._is_constant
@is_constant.setter
def is_constant(self, is_cst: bool):
def is_constant(self, is_cst: bool) -> None:
self._is_constant = is_cst
@property
@ -159,8 +160,8 @@ class Variable(SourceMapping):
return (
self.name,
[str(x) for x in export_nested_types_from_variable(self)],
[str(x) for x in export_return_type_from_variable(self)],
[str(x) for x in export_nested_types_from_variable(self)], # type: ignore
[str(x) for x in export_return_type_from_variable(self)], # type: ignore
)
@property
@ -178,4 +179,5 @@ class Variable(SourceMapping):
return f'{name}({",".join(parameters)})'
def __str__(self) -> str:
assert self._name
return self._name

@ -59,6 +59,8 @@ ALL_SOLC_VERSIONS_06 = make_solc_versions(6, 0, 12)
ALL_SOLC_VERSIONS_07 = make_solc_versions(7, 0, 6)
# No VERSIONS_08 as it is still in dev
DETECTOR_INFO = List[Union[str, SupportedOutput]]
class AbstractDetector(metaclass=abc.ABCMeta):
ARGUMENT = "" # run the detector with slither.py --ARGUMENT
@ -251,7 +253,7 @@ class AbstractDetector(metaclass=abc.ABCMeta):
def generate_result(
self,
info: Union[str, List[Union[str, SupportedOutput]]],
info: DETECTOR_INFO,
additional_fields: Optional[Dict] = None,
) -> Output:
output = Output(

@ -1,5 +1,9 @@
from typing import List
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.slithir.operations import Binary, BinaryType
from slither.slithir.variables import Constant
from slither.core.declarations.function_contract import FunctionContract
@ -49,7 +53,12 @@ The shift statement will right-shift the constant 8 by `a` bits"""
BinaryType.RIGHT_SHIFT,
]:
if isinstance(ir.variable_left, Constant):
info = [f, " contains an incorrect shift operation: ", node, "\n"]
info: DETECTOR_INFO = [
f,
" contains an incorrect shift operation: ",
node,
"\n",
]
json = self.generate_result(info)
results.append(json)

@ -2,11 +2,14 @@
Module detecting constant functions
Recursively check the called functions
"""
from typing import List
from typing import List, Dict
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
ALL_SOLC_VERSIONS_04,
DETECTOR_INFO,
)
from slither.formatters.attributes.const_functions import custom_format
from slither.utils.output import Output
@ -73,7 +76,10 @@ All the calls to `get` revert, breaking Bob's smart contract execution."""
if f.contains_assembly:
attr = "view" if f.view else "pure"
info = [f, f" is declared {attr} but contains assembly code\n"]
info: DETECTOR_INFO = [
f,
f" is declared {attr} but contains assembly code\n",
]
res = self.generate_result(info, {"contains_assembly": True})
results.append(res)
@ -81,5 +87,5 @@ All the calls to `get` revert, breaking Bob's smart contract execution."""
return results
@staticmethod
def _format(comilation_unit, result):
def _format(comilation_unit: SlitherCompilationUnit, result: Dict) -> None:
custom_format(comilation_unit, result)

@ -2,11 +2,14 @@
Module detecting constant functions
Recursively check the called functions
"""
from typing import List
from typing import List, Dict
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
ALL_SOLC_VERSIONS_04,
DETECTOR_INFO,
)
from slither.formatters.attributes.const_functions import custom_format
from slither.utils.output import Output
@ -74,7 +77,7 @@ All the calls to `get` revert, breaking Bob's smart contract execution."""
if variables_written:
attr = "view" if f.view else "pure"
info = [
info: DETECTOR_INFO = [
f,
f" is declared {attr} but changes state variables:\n",
]
@ -89,5 +92,5 @@ All the calls to `get` revert, breaking Bob's smart contract execution."""
return results
@staticmethod
def _format(slither, result):
def _format(slither: SlitherCompilationUnit, result: Dict) -> None:
custom_format(slither, result)

@ -1,9 +1,14 @@
"""
Check that the same pragma is used in all the files
"""
from typing import List
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from typing import List, Dict
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.formatters.attributes.constant_pragma import custom_format
from slither.utils.output import Output
@ -31,7 +36,7 @@ class ConstantPragma(AbstractDetector):
versions = sorted(list(set(versions)))
if len(versions) > 1:
info = ["Different versions of Solidity are used:\n"]
info: DETECTOR_INFO = ["Different versions of Solidity are used:\n"]
info += [f"\t- Version used: {[str(v) for v in versions]}\n"]
for p in sorted(pragma, key=lambda x: x.version):
@ -44,5 +49,5 @@ class ConstantPragma(AbstractDetector):
return results
@staticmethod
def _format(slither, result):
def _format(slither: SlitherCompilationUnit, result: Dict) -> None:
custom_format(slither, result)

@ -5,7 +5,11 @@
import re
from typing import List, Optional, Tuple
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.formatters.attributes.incorrect_solc import custom_format
from slither.utils.output import Output
@ -141,7 +145,7 @@ Consider using the latest version of Solidity for testing."""
# If we found any disallowed pragmas, we output our findings.
if disallowed_pragmas:
for (reason, p) in disallowed_pragmas:
info = ["Pragma version", p, f" {reason}\n"]
info: DETECTOR_INFO = ["Pragma version", p, f" {reason}\n"]
json = self.generate_result(info)

@ -4,7 +4,11 @@
from typing import List
from slither.core.declarations.contract import Contract
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.slithir.operations import (
HighLevelCall,
LowLevelCall,
@ -85,7 +89,7 @@ Every Ether sent to `Locked` will be lost."""
funcs_payable = [function for function in contract.functions if function.payable]
if funcs_payable:
if self.do_no_send_ether(contract):
info = ["Contract locking ether found:\n"]
info: DETECTOR_INFO = ["Contract locking ether found:\n"]
info += ["\tContract ", contract, " has payable functions:\n"]
for function in funcs_payable:
info += ["\t - ", function, "\n"]

@ -5,7 +5,11 @@ Collect all the interfaces
Check for contracts which implement all interface functions but do not explicitly derive from those interfaces.
"""
from typing import List
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.core.declarations.contract import Contract
from slither.utils.output import Output
@ -139,7 +143,7 @@ contract Something {
continue
intended_interfaces = self.detect_unimplemented_interface(contract, interfaces)
for interface in intended_interfaces:
info = [contract, " should inherit from ", interface, "\n"]
info: DETECTOR_INFO = [contract, " should inherit from ", interface, "\n"]
res = self.generate_result(info)
results.append(res)
return results

@ -2,7 +2,14 @@
Detects the passing of arrays located in memory to functions which expect to modify arrays via storage reference.
"""
from typing import List, Set, Tuple, Union
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.core.declarations import Function
from slither.core.variables import Variable
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.core.solidity_types.array_type import ArrayType
from slither.core.variables.state_variable import StateVariable
from slither.core.variables.local_variable import LocalVariable
@ -89,12 +96,7 @@ As a result, Bob's usage of the contract is incorrect."""
@staticmethod
def detect_calls_passing_ref_to_function(
contracts: List[Contract], array_modifying_funcs: Set[FunctionContract]
) -> List[
Union[
Tuple[Node, StateVariable, FunctionContract],
Tuple[Node, LocalVariable, FunctionContract],
]
]:
) -> List[Tuple[Node, Variable, Union[Function, Variable]]]:
"""
Obtains all calls passing storage arrays by value to a function which cannot write to them successfully.
:param contracts: The collection of contracts to check for problematic calls in.
@ -105,7 +107,7 @@ As a result, Bob's usage of the contract is incorrect."""
write to the array unsuccessfully.
"""
# Define our resulting array.
results = []
results: List[Tuple[Node, Variable, Union[Function, Variable]]] = []
# Verify we have functions in our list to check for.
if not array_modifying_funcs:
@ -159,7 +161,7 @@ As a result, Bob's usage of the contract is incorrect."""
if problematic_calls:
for calling_node, affected_argument, invoked_function in problematic_calls:
info = [
info: DETECTOR_INFO = [
calling_node.function,
" passes array ",
affected_argument,

@ -10,6 +10,7 @@ from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
make_solc_versions,
DETECTOR_INFO,
)
from slither.slithir.operations import TypeConversion
from slither.core.declarations.enum import Enum
@ -73,10 +74,14 @@ Attackers can trigger unexpected behaviour by calling `bug(1)`."""
for c in self.compilation_unit.contracts:
ret = _detect_dangerous_enum_conversions(c)
for node, var in ret:
func_info = [node, " has a dangerous enum conversion\n"]
func_info: DETECTOR_INFO = [node, " has a dangerous enum conversion\n"]
# Output each node with the function info header as a separate result.
variable_info = ["\t- Variable: ", var, f" of type: {str(var.type)}\n"]
node_info = ["\t- Enum conversion: ", node, "\n"]
variable_info: DETECTOR_INFO = [
"\t- Variable: ",
var,
f" of type: {str(var.type)}\n",
]
node_info: DETECTOR_INFO = ["\t- Enum conversion: ", node, "\n"]
json = self.generate_result(func_info + variable_info + node_info)
results.append(json)

@ -1,6 +1,10 @@
from typing import List
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.utils.output import Output
@ -58,7 +62,10 @@ In Solidity [0.4.22](https://github.com/ethereum/solidity/releases/tag/v0.4.23),
# If there is more than one, we encountered the described issue occurring.
if constructors and len(constructors) > 1:
info = [contract, " contains multiple constructors in the same contract:\n"]
info: DETECTOR_INFO = [
contract,
" contains multiple constructors in the same contract:\n",
]
for constructor in constructors:
info += ["\t- ", constructor, "\n"]

@ -6,6 +6,7 @@ from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
ALL_SOLC_VERSIONS_04,
DETECTOR_INFO,
)
from slither.core.declarations.contract import Contract
from slither.core.declarations.function_contract import FunctionContract
@ -151,7 +152,7 @@ The constructor of `A` is called multiple times in `D` and `E`:
continue
# Generate data to output.
info = [
info: DETECTOR_INFO = [
contract,
" gives base constructor ",
base_constructor,

@ -6,6 +6,7 @@ from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
make_solc_versions,
DETECTOR_INFO,
)
from slither.core.solidity_types import ArrayType
from slither.core.solidity_types import UserDefinedType
@ -122,7 +123,13 @@ contract A {
for contract in self.contracts:
storage_abiencoderv2_arrays = self._detect_storage_abiencoderv2_arrays(contract)
for function, node in storage_abiencoderv2_arrays:
info = ["Function ", function, " trigger an abi encoding bug:\n\t- ", node, "\n"]
info: DETECTOR_INFO = [
"Function ",
function,
" trigger an abi encoding bug:\n\t- ",
node,
"\n",
]
res = self.generate_result(info)
results.append(res)

@ -1,18 +1,21 @@
"""
Module detecting storage signed integer array bug
"""
from typing import List
from typing import List, Tuple, Set
from slither.core.declarations import Function, Contract
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
make_solc_versions,
DETECTOR_INFO,
)
from slither.core.cfg.node import NodeType
from slither.core.cfg.node import NodeType, Node
from slither.core.solidity_types import ArrayType
from slither.core.solidity_types.elementary_type import Int, ElementaryType
from slither.core.variables.local_variable import LocalVariable
from slither.core.variables.state_variable import StateVariable
from slither.slithir.operations import Operation, OperationWithLValue
from slither.slithir.operations.assignment import Assignment
from slither.slithir.operations.init_array import InitArray
from slither.utils.output import Output
@ -60,7 +63,7 @@ contract A {
VULNERABLE_SOLC_VERSIONS = make_solc_versions(4, 7, 25) + make_solc_versions(5, 0, 9)
@staticmethod
def _is_vulnerable_type(ir):
def _is_vulnerable_type(ir: Operation) -> bool:
"""
Detect if the IR lvalue is a vulnerable type
Must be a storage allocation, and an array of Int
@ -68,23 +71,28 @@ contract A {
"""
# Storage allocation
# Base type is signed integer
if not isinstance(ir, OperationWithLValue):
return False
return (
(
isinstance(ir.lvalue, StateVariable)
or (isinstance(ir.lvalue, LocalVariable) and ir.lvalue.is_storage)
)
and isinstance(ir.lvalue.type.type, ElementaryType)
and ir.lvalue.type.type.type in Int
and isinstance(ir.lvalue.type.type, ElementaryType) # type: ignore
and ir.lvalue.type.type.type in Int # type: ignore
)
def detect_storage_signed_integer_arrays(self, contract):
def detect_storage_signed_integer_arrays(
self, contract: Contract
) -> Set[Tuple[Function, Node]]:
"""
Detects and returns all nodes with storage-allocated signed integer array init/assignment
:param contract: Contract to detect within
:return: A list of tuples with (function, node) where function node has storage-allocated signed integer array init/assignment
"""
# Create our result set.
results = set()
results: Set[Tuple[Function, Node]] = set()
# Loop for each function and modifier.
for function in contract.functions_and_modifiers_declared:
@ -118,9 +126,13 @@ contract A {
for contract in self.contracts:
storage_signed_integer_arrays = self.detect_storage_signed_integer_arrays(contract)
for function, node in storage_signed_integer_arrays:
contract_info = ["Contract ", contract, " \n"]
function_info = ["\t- Function ", function, "\n"]
node_info = ["\t\t- ", node, " has a storage signed integer array assignment\n"]
contract_info: DETECTOR_INFO = ["Contract ", contract, " \n"]
function_info: DETECTOR_INFO = ["\t- Function ", function, "\n"]
node_info: DETECTOR_INFO = [
"\t\t- ",
node,
" has a storage signed integer array assignment\n",
]
res = self.generate_result(contract_info + function_info + node_info)
results.append(res)

@ -6,6 +6,7 @@ from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
make_solc_versions,
DETECTOR_INFO,
)
from slither.slithir.operations import InternalDynamicCall, OperationWithLValue
from slither.slithir.variables import ReferenceVariable
@ -115,10 +116,10 @@ The call to `a(10)` will lead to unexpected behavior because function pointer `a
results = []
for contract in self.compilation_unit.contracts:
contract_info = ["Contract ", contract, " \n"]
contract_info: DETECTOR_INFO = ["Contract ", contract, " \n"]
nodes = self._detect_uninitialized_function_ptr_in_constructor(contract)
for node in nodes:
node_info = [
node_info: DETECTOR_INFO = [
"\t ",
node,
" is an unintialized function pointer call in a constructor\n",

@ -61,12 +61,12 @@ class ArbitrarySendErc20:
is_dependent(
ir.arguments[0],
SolidityVariableComposed("msg.sender"),
node.function.contract,
node,
)
or is_dependent(
ir.arguments[0],
SolidityVariable("this"),
node.function.contract,
node,
)
)
):
@ -79,12 +79,12 @@ class ArbitrarySendErc20:
is_dependent(
ir.arguments[1],
SolidityVariableComposed("msg.sender"),
node.function.contract,
node,
)
or is_dependent(
ir.arguments[1],
SolidityVariable("this"),
node.function.contract,
node,
)
)
):

@ -1,5 +1,9 @@
from typing import List
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.utils.output import Output
from .arbitrary_send_erc20 import ArbitrarySendErc20
@ -38,7 +42,7 @@ Use `msg.sender` as `from` in transferFrom.
arbitrary_sends.detect()
for node in arbitrary_sends.no_permit_results:
func = node.function
info = [func, " uses arbitrary from in transferFrom: ", node, "\n"]
info: DETECTOR_INFO = [func, " uses arbitrary from in transferFrom: ", node, "\n"]
res = self.generate_result(info)
results.append(res)

@ -1,5 +1,9 @@
from typing import List
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.utils.output import Output
from .arbitrary_send_erc20 import ArbitrarySendErc20
@ -41,7 +45,7 @@ Ensure that the underlying ERC20 token correctly implements a permit function.
arbitrary_sends.detect()
for node in arbitrary_sends.permit_results:
func = node.function
info = [
info: DETECTOR_INFO = [
func,
" uses arbitrary from in transferFrom in combination with permit: ",
node,

@ -6,7 +6,11 @@ from typing import List, Tuple
from slither.core.declarations.contract import Contract
from slither.core.declarations.function_contract import FunctionContract
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.utils.output import Output
@ -109,7 +113,7 @@ contract Token{
functions = IncorrectERC20InterfaceDetection.detect_incorrect_erc20_interface(c)
if functions:
for function in functions:
info = [
info: DETECTOR_INFO = [
c,
" has incorrect ERC20 function interface:",
function,

@ -2,7 +2,11 @@
Detect incorrect erc721 interface.
"""
from typing import Any, List, Tuple, Union
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.core.declarations.contract import Contract
from slither.core.declarations.function_contract import FunctionContract
from slither.utils.output import Output
@ -89,7 +93,9 @@ contract Token{
return False
@staticmethod
def detect_incorrect_erc721_interface(contract: Contract) -> List[Union[FunctionContract, Any]]:
def detect_incorrect_erc721_interface(
contract: Contract,
) -> List[Union[FunctionContract, Any]]:
"""Detect incorrect ERC721 interface
Returns:
@ -119,7 +125,7 @@ contract Token{
functions = IncorrectERC721InterfaceDetection.detect_incorrect_erc721_interface(c)
if functions:
for function in functions:
info = [
info: DETECTOR_INFO = [
c,
" has incorrect ERC721 function interface:",
function,

@ -1,6 +1,10 @@
from typing import List
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.utils.output import Output
@ -28,7 +32,7 @@ class Backdoor(AbstractDetector):
for f in contract.functions:
if "backdoor" in f.name:
# Info to be printed
info = ["Backdoor function found in ", f, "\n"]
info: DETECTOR_INFO = ["Backdoor function found in ", f, "\n"]
# Add the result in result
res = self.generate_result(info)

@ -18,7 +18,9 @@ from slither.core.declarations.function_contract import FunctionContract
from slither.core.declarations.solidity_variables import (
SolidityFunction,
SolidityVariableComposed,
SolidityVariable,
)
from slither.core.variables import Variable
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.slithir.operations import (
HighLevelCall,
@ -39,6 +41,10 @@ def arbitrary_send(func: Function) -> Union[bool, List[Node]]:
ret: List[Node] = []
for node in func.nodes:
func = node.function
deps_target: Union[Contract, Function] = (
func.contract if isinstance(func, FunctionContract) else func
)
for ir in node.irs:
if isinstance(ir, SolidityCall):
if ir.function == SolidityFunction("ecrecover(bytes32,uint8,bytes32,bytes32)"):
@ -49,7 +55,7 @@ def arbitrary_send(func: Function) -> Union[bool, List[Node]]:
if is_dependent(
ir.variable_right,
SolidityVariableComposed("msg.sender"),
func.contract,
deps_target,
):
return False
if isinstance(ir, (HighLevelCall, LowLevelCall, Transfer, Send)):
@ -64,12 +70,13 @@ def arbitrary_send(func: Function) -> Union[bool, List[Node]]:
if is_dependent(
ir.call_value,
SolidityVariableComposed("msg.value"),
func.contract,
node,
):
continue
if is_tainted(ir.destination, func.contract):
ret.append(node)
if isinstance(ir.destination, (Variable, SolidityVariable)):
if is_tainted(ir.destination, node):
ret.append(node)
return ret

@ -1,7 +1,11 @@
from typing import List, Tuple
from slither.core.declarations import Function
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.utils.code_complexity import compute_cyclomatic_complexity
from slither.utils.output import Output
@ -44,7 +48,7 @@ class CyclomaticComplexity(AbstractDetector):
_check_for_high_cc(high_cc_functions, f)
for f, cc in high_cc_functions:
info = [f, f" has a high cyclomatic complexity ({cc}).\n"]
info: DETECTOR_INFO = [f, f" has a high cyclomatic complexity ({cc}).\n"]
res = self.generate_result(info)
results.append(res)
return results

@ -4,7 +4,11 @@ Module detecting dead code
from typing import List, Tuple
from slither.core.declarations import Function, FunctionContract, Contract
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.utils.output import Output
@ -72,7 +76,7 @@ contract Contract{
# Continue if the functon is not implemented because it means the contract is abstract
if not function.is_implemented:
continue
info = [function, " is never used and should be removed\n"]
info: DETECTOR_INFO = [function, " is never used and should be removed\n"]
res = self.generate_result(info)
results.append(res)

@ -6,7 +6,11 @@ are in the outermost scope, they do not guarantee a revert, so a
default value can still be returned.
"""
from typing import List
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.core.cfg.node import Node, NodeType
from slither.utils.output import Output
@ -82,7 +86,11 @@ If the condition in `myModif` is false, the execution of `get()` will return 0."
node = None
else:
# Nothing was found in the outer scope
info = ["Modifier ", mod, " does not always execute _; or revert"]
info: DETECTOR_INFO = [
"Modifier ",
mod,
" does not always execute _; or revert",
]
res = self.generate_result(info)
results.append(res)

@ -6,7 +6,11 @@ from typing import Union, List
from slither.core.declarations import Function
from slither.core.solidity_types.elementary_type import ElementaryType
from slither.core.variables.state_variable import StateVariable
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.utils.function import get_function_id
from slither.utils.output import Output
@ -63,7 +67,7 @@ contract Contract{
assert isinstance(func_or_var, StateVariable)
incorrect_return_type = func_or_var.type != ElementaryType("bytes32")
if hash_collision or incorrect_return_type:
info = [
info: DETECTOR_INFO = [
"The function signature of ",
func_or_var,
" collides with DOMAIN_SEPARATOR and should be renamed or removed.\n",

@ -6,7 +6,11 @@ A suicidal contract is an unprotected function that calls selfdestruct
from typing import List
from slither.core.declarations import Function, Contract
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.utils.output import Output
@ -58,7 +62,7 @@ contract Buggy{
self.logger.error(f"{function_sig} not found")
continue
if function_protection not in function.all_internal_calls():
info = [
info: DETECTOR_INFO = [
function,
" should have ",
function_protection,

@ -7,7 +7,11 @@ from typing import List
from slither.core.declarations.contract import Contract
from slither.core.declarations.function_contract import FunctionContract
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.utils.output import Output
@ -78,7 +82,7 @@ Bob calls `kill` and destructs the contract."""
functions = self.detect_suicidal(c)
for func in functions:
info = [func, " allows anyone to destruct the contract\n"]
info: DETECTOR_INFO = [func, " allows anyone to destruct the contract\n"]
res = self.generate_result(info)

@ -8,7 +8,13 @@ Consider public state variables as implemented functions
Do not consider fallback function or constructor
"""
from typing import List, Set
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.core.declarations import Function
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.core.declarations.contract import Contract
from slither.core.declarations.function_contract import FunctionContract
from slither.utils.output import Output
@ -62,7 +68,7 @@ All unimplemented functions must be implemented on a contract that is meant to b
def _match_state_variable(contract: Contract, f: FunctionContract) -> bool:
return any(s.full_name == f.full_name for s in contract.state_variables)
def _detect_unimplemented_function(self, contract: Contract) -> Set[FunctionContract]:
def _detect_unimplemented_function(self, contract: Contract) -> Set[Function]:
"""
Detects any function definitions which are not implemented in the given contract.
:param contract: The contract to search unimplemented functions for.
@ -77,6 +83,8 @@ All unimplemented functions must be implemented on a contract that is meant to b
# fallback function and constructor.
unimplemented = set()
for f in contract.all_functions_called:
if not isinstance(f, Function):
continue
if (
not f.is_implemented
and not f.is_constructor
@ -102,7 +110,7 @@ All unimplemented functions must be implemented on a contract that is meant to b
for contract in self.compilation_unit.contracts_derived:
functions = self._detect_unimplemented_function(contract)
if functions:
info = [contract, " does not implement functions:\n"]
info: DETECTOR_INFO = [contract, " does not implement functions:\n"]
for function in sorted(functions, key=lambda x: x.full_name):
info += ["\t- ", function, "\n"]

@ -1,6 +1,10 @@
import re
from typing import List
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.formatters.naming_convention.naming_convention import custom_format
from slither.utils.output import Output
@ -63,6 +67,7 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
def _detect(self) -> List[Output]:
results = []
info: DETECTOR_INFO
for contract in self.contracts:
if not self.is_cap_words(contract.name):

@ -50,14 +50,17 @@ def contains_bad_PRNG_sources(func: Function, blockhash_ret_values: List[Variabl
for node in func.nodes:
for ir in node.irs_ssa:
if isinstance(ir, Binary) and ir.type == BinaryType.MODULO:
var_left = ir.variable_left
if not isinstance(var_left, (Variable, SolidityVariable)):
continue
if is_dependent_ssa(
ir.variable_left, SolidityVariableComposed("block.timestamp"), func.contract
) or is_dependent_ssa(ir.variable_left, SolidityVariable("now"), func.contract):
var_left, SolidityVariableComposed("block.timestamp"), node
) or is_dependent_ssa(var_left, SolidityVariable("now"), node):
ret.add(node)
break
for ret_val in blockhash_ret_values:
if is_dependent_ssa(ir.variable_left, ret_val, func.contract):
if is_dependent_ssa(var_left, ret_val, node):
ret.add(node)
break
return list(ret)

@ -6,12 +6,17 @@ from typing import List, Tuple
from slither.analyses.data_dependency.data_dependency import is_dependent
from slither.core.cfg.node import Node
from slither.core.declarations import Function, Contract
from slither.core.declarations import Function, Contract, FunctionContract
from slither.core.declarations.solidity_variables import (
SolidityVariableComposed,
SolidityVariable,
)
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.core.variables import Variable
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.slithir.operations import Binary, BinaryType
from slither.utils.output import Output
@ -21,25 +26,25 @@ def _timestamp(func: Function) -> List[Node]:
for node in func.nodes:
if node.contains_require_or_assert():
for var in node.variables_read:
if is_dependent(var, SolidityVariableComposed("block.timestamp"), func.contract):
if is_dependent(var, SolidityVariableComposed("block.timestamp"), node):
ret.add(node)
if is_dependent(var, SolidityVariable("now"), func.contract):
if is_dependent(var, SolidityVariable("now"), node):
ret.add(node)
for ir in node.irs:
if isinstance(ir, Binary) and BinaryType.return_bool(ir.type):
for var in ir.read:
if is_dependent(
var, SolidityVariableComposed("block.timestamp"), func.contract
):
for var_read in ir.read:
if not isinstance(var_read, (Variable, SolidityVariable)):
continue
if is_dependent(var_read, SolidityVariableComposed("block.timestamp"), node):
ret.add(node)
if is_dependent(var, SolidityVariable("now"), func.contract):
if is_dependent(var_read, SolidityVariable("now"), node):
ret.add(node)
return sorted(list(ret), key=lambda x: x.node_id)
def _detect_dangerous_timestamp(
contract: Contract,
) -> List[Tuple[Function, List[Node]]]:
) -> List[Tuple[FunctionContract, List[Node]]]:
"""
Args:
contract (Contract)
@ -48,7 +53,7 @@ def _detect_dangerous_timestamp(
"""
ret = []
for f in [f for f in contract.functions if f.contract_declarer == contract]:
nodes = _timestamp(f)
nodes: List[Node] = _timestamp(f)
if nodes:
ret.append((f, nodes))
return ret
@ -78,7 +83,7 @@ class Timestamp(AbstractDetector):
dangerous_timestamp = _detect_dangerous_timestamp(c)
for (func, nodes) in dangerous_timestamp:
info = [func, " uses timestamp for comparisons\n"]
info: DETECTOR_INFO = [func, " uses timestamp for comparisons\n"]
info += ["\tDangerous comparisons:\n"]

@ -2,7 +2,11 @@
Module detecting usage of low level calls
"""
from typing import List, Tuple
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.slithir.operations import LowLevelCall
from slither.core.cfg.node import Node
from slither.core.declarations.contract import Contract
@ -52,7 +56,7 @@ class LowLevelCalls(AbstractDetector):
for c in self.contracts:
values = self.detect_low_level_calls(c)
for func, nodes in values:
info = ["Low level call in ", func, ":\n"]
info: DETECTOR_INFO = ["Low level call in ", func, ":\n"]
# sort the nodes to get deterministic results
nodes.sort(key=lambda x: x.node_id)

@ -11,7 +11,11 @@ from slither.core.declarations.function_contract import FunctionContract
from slither.core.declarations.modifier import Modifier
from slither.core.solidity_types.elementary_type import ElementaryType
from slither.core.variables.state_variable import StateVariable
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.slithir.operations.event_call import EventCall
from slither.utils.output import Output
@ -100,7 +104,7 @@ contract C {
for contract in self.compilation_unit.contracts_derived:
missing_events = self._detect_missing_events(contract)
for (function, nodes) in missing_events:
info = [function, " should emit an event for: \n"]
info: DETECTOR_INFO = [function, " should emit an event for: \n"]
for (node, _sv, _mod) in nodes:
info += ["\t- ", node, " \n"]
res = self.generate_result(info)

@ -10,7 +10,11 @@ from slither.core.declarations.contract import Contract
from slither.core.declarations.function_contract import FunctionContract
from slither.core.solidity_types.elementary_type import ElementaryType, Int, Uint
from slither.core.variables.state_variable import StateVariable
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.slithir.operations.event_call import EventCall
from slither.utils.output import Output
@ -122,7 +126,7 @@ contract C {
for contract in self.compilation_unit.contracts_derived:
missing_events = self._detect_missing_events(contract)
for (function, nodes) in missing_events:
info = [function, " should emit an event for: \n"]
info: DETECTOR_INFO = [function, " should emit an event for: \n"]
for (node, _) in nodes:
info += ["\t- ", node, " \n"]
res = self.generate_result(info)

@ -12,7 +12,11 @@ from slither.core.declarations.function import ModifierStatements
from slither.core.declarations.function_contract import FunctionContract
from slither.core.solidity_types.elementary_type import ElementaryType
from slither.core.variables.local_variable import LocalVariable
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.slithir.operations import Call
from slither.slithir.operations import Send, Transfer, LowLevelCall
from slither.utils.output import Output
@ -155,7 +159,7 @@ Bob calls `updateOwner` without specifying the `newOwner`, so Bob loses ownershi
missing_zero_address_validation = self._detect_missing_zero_address_validation(contract)
for (_, var_nodes) in missing_zero_address_validation:
for var, nodes in var_nodes.items():
info = [var, " lacks a zero-check on ", ":\n"]
info: DETECTOR_INFO = [var, " lacks a zero-check on ", ":\n"]
for node in nodes:
info += ["\t\t- ", node, "\n"]
res = self.generate_result(info)

@ -7,7 +7,11 @@ from slither.core.cfg.node import Node
from slither.core.declarations import Function
from slither.core.declarations.function_contract import FunctionContract
from slither.core.variables.state_variable import StateVariable
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.slithir.operations import HighLevelCall
from slither.slithir.operations.operation import Operation
from slither.utils.output import Output
@ -91,7 +95,7 @@ contract MyConc{
if unused_return:
for node in unused_return:
info = [f, " ignores return value by ", node, "\n"]
info: DETECTOR_INFO = [f, " ignores return value by ", node, "\n"]
res = self.generate_result(info)

@ -1,6 +1,10 @@
from typing import List
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.slithir.operations import Nop
from slither.utils.output import Output
@ -39,7 +43,7 @@ When reading `B`'s constructor definition, we might assume that `A()` initiates
for constructor_call in cst.explicit_base_constructor_calls_statements:
for node in constructor_call.nodes:
if any(isinstance(ir, Nop) for ir in node.irs):
info = ["Void constructor called in ", cst, ":\n"]
info: DETECTOR_INFO = ["Void constructor called in ", cst, ":\n"]
info += ["\t- ", node, "\n"]
res = self.generate_result(info)

@ -4,7 +4,11 @@ from typing import Dict, List
from slither.analyses.data_dependency.data_dependency import is_dependent
from slither.core.cfg.node import Node
from slither.core.declarations import Function, Contract, SolidityVariableComposed
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.slithir.operations import LowLevelCall, HighLevelCall
from slither.utils.output import Output
@ -88,7 +92,7 @@ If you do, ensure your users are aware of the potential issues."""
for contract in self.compilation_unit.contracts_derived:
vulns = _detect_token_reentrant(contract)
for function, nodes in vulns.items():
info = [function, " is an reentrancy unsafe token function:\n"]
info: DETECTOR_INFO = [function, " is an reentrancy unsafe token function:\n"]
for node in nodes:
info += ["\t-", node, "\n"]
json = self.generate_result(info)

@ -9,7 +9,11 @@ from slither.core.declarations.function_contract import FunctionContract
from slither.core.declarations.modifier import Modifier
from slither.core.variables import Variable
from slither.core.variables.local_variable import LocalVariable
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.utils.output import Output
@ -194,7 +198,7 @@ contract Bug {
shadow_type = shadow[0]
shadow_object = shadow[1]
info = [
info: DETECTOR_INFO = [
shadow_object,
f' ({shadow_type}) shadows built-in symbol"\n',
]

@ -9,7 +9,11 @@ from slither.core.declarations.function_contract import FunctionContract
from slither.core.declarations.modifier import Modifier
from slither.core.variables.local_variable import LocalVariable
from slither.core.variables.state_variable import StateVariable
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.utils.output import Output
@ -85,7 +89,7 @@ contract Bug {
] = []
# Loop through all functions + modifiers in this contract.
for function in contract.functions + contract.modifiers:
for function in contract.functions + list(contract.modifiers):
# We should only look for functions declared directly in this contract (not in a base contract).
if function.contract_declarer != contract:
continue
@ -144,7 +148,7 @@ contract Bug {
for shadow in shadows:
local_variable = shadow[0]
overshadowed = shadow[1]
info = [local_variable, " shadows:\n"]
info: DETECTOR_INFO = [local_variable, " shadows:\n"]
for overshadowed_entry in overshadowed:
info += [
"\t- ",

@ -6,7 +6,11 @@ from typing import List
from slither.core.declarations import Contract
from slither.core.variables.state_variable import StateVariable
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.detectors.shadowing.common import is_upgradable_gap_variable
from slither.utils.output import Output
@ -89,7 +93,7 @@ contract DerivedContract is BaseContract{
for all_variables in shadowing:
shadow = all_variables[0]
variables = all_variables[1:]
info = [shadow, " shadows:\n"]
info: DETECTOR_INFO = [shadow, " shadows:\n"]
for var in variables:
info += ["\t- ", var, "\n"]

@ -1,12 +1,17 @@
from collections import defaultdict
from typing import Any, List
from typing import List
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.core.declarations import Contract
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.utils.output import Output
def _find_missing_inheritance(compilation_unit: SlitherCompilationUnit) -> List[Any]:
def _find_missing_inheritance(compilation_unit: SlitherCompilationUnit) -> List[Contract]:
"""
Filter contracts with missing inheritance to return only the "most base" contracts
in the inheritance tree.
@ -80,7 +85,7 @@ As a result, the second contract cannot be analyzed.
inheritance_corrupted[father.name].append(contract)
for contract_name, files in names_reused.items():
info = [contract_name, " is re-used:\n"]
info: DETECTOR_INFO = [contract_name, " is re-used:\n"]
for file in files:
if file is None:
info += ["\t- In an file not found, most likely in\n"]

@ -1,7 +1,11 @@
import re
from typing import List
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.utils.output import Output
@ -78,7 +82,7 @@ contract Token
idx = start_index + result_index
relative = self.slither.crytic_compile.filename_lookup(filename).relative
info = f"{relative} contains a unicode right-to-left-override character at byte offset {idx}:\n"
info: DETECTOR_INFO = f"{relative} contains a unicode right-to-left-override character at byte offset {idx}:\n"
# We have a patch, so pattern.find will return at least one result

@ -1,7 +1,9 @@
"""
Module detecting assignment of array length
"""
from typing import List, Set
from typing import List, Set, Union
from slither.core.variables import Variable
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
@ -14,7 +16,7 @@ from slither.slithir.variables.reference import ReferenceVariable
from slither.slithir.operations.binary import Binary
from slither.analyses.data_dependency.data_dependency import is_tainted
from slither.core.declarations.contract import Contract
from slither.utils.output import Output
from slither.utils.output import Output, SupportedOutput
def detect_array_length_assignment(contract: Contract) -> Set[Node]:
@ -50,7 +52,7 @@ def detect_array_length_assignment(contract: Contract) -> Set[Node]:
elif isinstance(ir, (Assignment, Binary)):
if isinstance(ir.lvalue, ReferenceVariable):
if ir.lvalue in array_length_refs and any(
is_tainted(v, contract) for v in ir.read
is_tainted(v, contract) for v in ir.read if isinstance(v, Variable)
):
# the taint is not precise enough yet
# as a result, REF_0 = REF_0 + 1
@ -120,12 +122,16 @@ Otherwise, thoroughly review the contract to ensure a user-controlled variable c
for contract in self.contracts:
array_length_assignments = detect_array_length_assignment(contract)
if array_length_assignments:
contract_info = [
contract_info: List[Union[str, SupportedOutput]] = [
contract,
" contract sets array length with a user-controlled value:\n",
]
for node in array_length_assignments:
node_info = contract_info + ["\t- ", node, "\n"]
node_info: List[Union[str, SupportedOutput]] = contract_info + [
"\t- ",
node,
"\n",
]
res = self.generate_result(node_info)
results.append(res)

@ -6,7 +6,11 @@ from typing import List, Tuple
from slither.core.cfg.node import Node, NodeType
from slither.core.declarations.contract import Contract
from slither.core.declarations.function_contract import FunctionContract
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.utils.output import Output
@ -52,7 +56,7 @@ class Assembly(AbstractDetector):
for c in self.contracts:
values = self.detect_assembly(c)
for func, nodes in values:
info = [func, " uses assembly\n"]
info: DETECTOR_INFO = [func, " uses assembly\n"]
# sort the nodes to get deterministic results
nodes.sort(key=lambda x: x.node_id)

@ -6,7 +6,11 @@ from typing import List, Tuple
from slither.core.cfg.node import Node
from slither.core.declarations.contract import Contract
from slither.core.declarations.function_contract import FunctionContract
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.slithir.operations.internal_call import InternalCall
from slither.utils.output import Output
@ -25,7 +29,7 @@ def detect_assert_state_change(
results = []
# Loop for each function and modifier.
for function in contract.functions_declared + contract.modifiers_declared:
for function in contract.functions_declared + list(contract.modifiers_declared):
for node in function.nodes:
# Detect assert() calls
if any(c.name == "assert(bool)" for c in node.internal_calls) and (
@ -36,7 +40,9 @@ def detect_assert_state_change(
any(
ir
for ir in node.irs
if isinstance(ir, InternalCall) and ir.function.state_variables_written
if isinstance(ir, InternalCall)
and ir.function
and ir.function.state_variables_written
)
):
results.append((function, node))
@ -85,7 +91,10 @@ The assert in `bad()` increments the state variable `s_a` while checking for the
for contract in self.contracts:
assert_state_change = detect_assert_state_change(contract)
for (func, node) in assert_state_change:
info = [func, " has an assert() call which possibly changes state.\n"]
info: DETECTOR_INFO = [
func,
" has an assert() call which possibly changes state.\n",
]
info += ["\t-", node, "\n"]
info += [
"Consider using require() or change the invariant to not modify the state.\n"

@ -6,7 +6,11 @@ from typing import List, Set, Tuple
from slither.core.cfg.node import Node
from slither.core.declarations import Function
from slither.core.declarations.contract import Contract
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.slithir.operations import (
Binary,
BinaryType,
@ -84,7 +88,7 @@ Boolean constants can be used directly and do not need to be compare to `true` o
boolean_constant_misuses = self._detect_boolean_equality(contract)
for (func, nodes) in boolean_constant_misuses:
for node in nodes:
info = [
info: DETECTOR_INFO = [
func,
" compares to a boolean constant:\n\t-",
node,

@ -7,7 +7,11 @@ from slither.core.cfg.node import Node, NodeType
from slither.core.declarations import Function
from slither.core.declarations.contract import Contract
from slither.core.solidity_types import ElementaryType
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.slithir.operations import (
Assignment,
Call,
@ -120,7 +124,7 @@ Other uses (in complex expressions, as conditionals) indicate either an error or
boolean_constant_misuses = self._detect_boolean_constant_misuses(contract)
for (func, nodes) in boolean_constant_misuses:
for node in nodes:
info = [
info: DETECTOR_INFO = [
func,
" uses a Boolean constant improperly:\n\t-",
node,

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

Loading…
Cancel
Save