Merge pull request #823 from crytic/dev-multiple-compilation-units

Add the support for multiple compilation units.
pull/836/head
Feist Josselin 4 years ago committed by GitHub
commit 76c5cb2602
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .github/workflows/linter.yml
  2. 5
      examples/scripts/convert_to_ir.py
  3. 31
      examples/scripts/data_dependency.py
  4. 5
      examples/scripts/functions_called.py
  5. 5
      examples/scripts/functions_writing.py
  6. 6
      examples/scripts/possible_paths.py
  7. 4
      examples/scripts/taint_mapping.py
  8. 5
      examples/scripts/variable_in_condition.py
  9. 6
      setup.py
  10. 29
      slither/analyses/data_dependency/data_dependency.py
  11. 6
      slither/core/cfg/node.py
  12. 6
      slither/core/children/child_node.py
  13. 17
      slither/core/children/child_slither.py
  14. 267
      slither/core/compilation_unit.py
  15. 42
      slither/core/declarations/contract.py
  16. 23
      slither/core/declarations/function.py
  17. 300
      slither/core/slither_core.py
  18. 45
      slither/core/source_mapping/source_mapping.py
  19. 39
      slither/detectors/abstract_detector.py
  20. 6
      slither/detectors/attributes/const_functions_asm.py
  21. 2
      slither/detectors/attributes/const_functions_state.py
  22. 4
      slither/detectors/attributes/constant_pragma.py
  23. 27
      slither/detectors/attributes/incorrect_solc.py
  24. 4
      slither/detectors/attributes/locked_ether.py
  25. 4
      slither/detectors/attributes/unimplemented_interface.py
  26. 4
      slither/detectors/compiler_bugs/enum_conversion.py
  27. 6
      slither/detectors/compiler_bugs/public_mapping_nested.py
  28. 4
      slither/detectors/compiler_bugs/reused_base_constructor.py
  29. 4
      slither/detectors/compiler_bugs/storage_ABIEncoderV2_array.py
  30. 2
      slither/detectors/compiler_bugs/storage_signed_integer_array.py
  31. 4
      slither/detectors/compiler_bugs/uninitialized_function_ptr_in_constructor.py
  32. 2
      slither/detectors/erc/incorrect_erc20_interface.py
  33. 2
      slither/detectors/erc/incorrect_erc721_interface.py
  34. 2
      slither/detectors/examples/backdoor.py
  35. 4
      slither/detectors/functions/unimplemented.py
  36. 2
      slither/detectors/operations/bad_prng.py
  37. 2
      slither/detectors/operations/missing_events_access_control.py
  38. 2
      slither/detectors/operations/missing_events_arithmetic.py
  39. 2
      slither/detectors/operations/missing_zero_address_validation.py
  40. 2
      slither/detectors/operations/unused_return_values.py
  41. 2
      slither/detectors/reentrancy/token.py
  42. 15
      slither/detectors/slither/name_reused.py
  43. 2
      slither/detectors/source/rtlo.py
  44. 2
      slither/detectors/statements/array_length_assignment.py
  45. 2
      slither/detectors/statements/calls_in_loop.py
  46. 2
      slither/detectors/statements/controlled_delegatecall.py
  47. 2
      slither/detectors/statements/costly_operations_in_loop.py
  48. 2
      slither/detectors/statements/incorrect_strict_equality.py
  49. 2
      slither/detectors/statements/too_many_digits.py
  50. 2
      slither/detectors/statements/unprotected_upgradeable.py
  51. 10
      slither/detectors/variables/possible_const_state_variables.py
  52. 2
      slither/detectors/variables/uninitialized_local_variables.py
  53. 4
      slither/detectors/variables/uninitialized_state_variables.py
  54. 2
      slither/detectors/variables/uninitialized_storage_variables.py
  55. 8
      slither/detectors/variables/unused_state_variables.py
  56. 14
      slither/formatters/attributes/const_functions.py
  57. 14
      slither/formatters/functions/external_function.py
  58. 54
      slither/formatters/naming_convention/naming_convention.py
  59. 18
      slither/formatters/variables/possible_const_state_variables.py
  60. 9
      slither/formatters/variables/unused_state_variables.py
  61. 11
      slither/printers/call/call_graph.py
  62. 13
      slither/printers/guidance/echidna.py
  63. 19
      slither/printers/summary/evm.py
  64. 15
      slither/printers/summary/human_summary.py
  65. 29
      slither/printers/summary/slithir.py
  66. 115
      slither/slither.py
  67. 29
      slither/slithir/convert.py
  68. 2
      slither/slithir/operations/high_level_call.py
  69. 2
      slither/slithir/operations/new_contract.py
  70. 4
      slither/slithir/variables/reference.py
  71. 4
      slither/slithir/variables/temporary.py
  72. 4
      slither/slithir/variables/tuple.py
  73. 4
      slither/solc_parsing/cfg/node.py
  74. 42
      slither/solc_parsing/declarations/contract.py
  75. 4
      slither/solc_parsing/declarations/event.py
  76. 26
      slither/solc_parsing/declarations/function.py
  77. 4
      slither/solc_parsing/declarations/modifier.py
  78. 2
      slither/solc_parsing/declarations/structure_contract.py
  79. 6
      slither/solc_parsing/declarations/structure_top_level.py
  80. 63
      slither/solc_parsing/expressions/expression_parsing.py
  81. 106
      slither/solc_parsing/slither_compilation_unit_solc.py
  82. 32
      slither/solc_parsing/solidity_types/type_parsing.py
  83. 26
      slither/solc_parsing/yul/parse_yul.py
  84. 5
      slither/tools/erc_conformance/__main__.py
  85. 24
      slither/tools/flattening/flattening.py
  86. 6
      slither/tools/kspec_coverage/kspec_coverage.py
  87. 2
      slither/tools/mutator/utils/generic_patching.py
  88. 6
      slither/tools/possible_paths/possible_paths.py
  89. 6
      slither/tools/properties/__main__.py
  90. 18
      slither/tools/properties/properties/erc20.py
  91. 2
      slither/tools/properties/properties/ercs/erc20/unit_tests/truffle.py
  92. 15
      slither/tools/upgradeability/__main__.py
  93. 4
      slither/tools/upgradeability/checks/abstract_checks.py
  94. 8
      slither/tools/upgradeability/checks/initialization.py
  95. 11
      slither/utils/output.py
  96. 6
      tests/detectors/locked-ether/0.4.25/locked_ether.sol.0.4.25.LockedEther.json
  97. 6
      tests/detectors/locked-ether/0.5.16/locked_ether.sol.0.5.16.LockedEther.json
  98. 6
      tests/detectors/locked-ether/0.6.11/locked_ether.sol.0.6.11.LockedEther.json
  99. 6
      tests/detectors/locked-ether/0.7.6/locked_ether.sol.0.7.6.LockedEther.json
  100. 6
      tests/detectors/pragma/0.4.25/pragma.0.4.25.sol.0.4.25.ConstantPragma.json
  101. Some files were not shown because too many files have changed in this diff Show More

@ -56,4 +56,5 @@ jobs:
VALIDATE_DOCKERFILE_HADOLINT: false VALIDATE_DOCKERFILE_HADOLINT: false
VALIDATE_EDITORCONFIG: false VALIDATE_EDITORCONFIG: false
VALIDATE_JSCPD: false VALIDATE_JSCPD: false
VALIDATE_PYTHON_MYPY: false
SHELLCHECK_OPTS: "-e SC1090" SHELLCHECK_OPTS: "-e SC1090"

@ -11,8 +11,9 @@ if len(sys.argv) != 2:
slither = Slither(sys.argv[1]) slither = Slither(sys.argv[1])
# Get the contract # Get the contract
contract = slither.get_contract_from_name("Test") contracts = slither.get_contract_from_name("Test")
assert contract assert len(contracts) == 1
contract = contracts[0]
# Get the variable # Get the variable
test = contract.get_function_from_signature("one()") test = contract.get_function_from_signature("one()")
assert test assert test

@ -13,8 +13,9 @@ if len(sys.argv) != 2:
slither = Slither(sys.argv[1]) slither = Slither(sys.argv[1])
contract = slither.get_contract_from_name("Simple") contracts = slither.get_contract_from_name("Simple")
assert contract assert len(contracts) == 1
contract = contracts[0]
destination = contract.get_state_variable_from_name("destination") destination = contract.get_state_variable_from_name("destination")
source = contract.get_state_variable_from_name("source") source = contract.get_state_variable_from_name("source")
@ -35,8 +36,9 @@ assert not is_tainted(source, contract)
print("{} is tainted {}".format(destination, is_tainted(destination, contract))) print("{} is tainted {}".format(destination, is_tainted(destination, contract)))
assert is_tainted(destination, contract) assert is_tainted(destination, contract)
contract = slither.get_contract_from_name("Reference") contracts = slither.get_contract_from_name("Reference")
assert contract assert len(contracts) == 1
contract = contracts[0]
destination = contract.get_state_variable_from_name("destination") destination = contract.get_state_variable_from_name("destination")
assert destination assert destination
source = contract.get_state_variable_from_name("source") source = contract.get_state_variable_from_name("source")
@ -73,8 +75,9 @@ assert is_tainted(destination_indirect_2, contract)
print("SolidityVar contract") print("SolidityVar contract")
contract = slither.get_contract_from_name("SolidityVar") contracts = slither.get_contract_from_name("SolidityVar")
assert contract assert len(contracts) == 1
contract = contracts[0]
addr_1 = contract.get_state_variable_from_name("addr_1") addr_1 = contract.get_state_variable_from_name("addr_1")
assert addr_1 assert addr_1
addr_2 = contract.get_state_variable_from_name("addr_2") addr_2 = contract.get_state_variable_from_name("addr_2")
@ -91,8 +94,9 @@ assert not is_dependent(addr_2, msgsender, contract)
print("Intermediate contract") print("Intermediate contract")
contract = slither.get_contract_from_name("Intermediate") contracts = slither.get_contract_from_name("Intermediate")
assert contract assert len(contracts) == 1
contract = contracts[0]
destination = contract.get_state_variable_from_name("destination") destination = contract.get_state_variable_from_name("destination")
assert destination assert destination
source = contract.get_state_variable_from_name("source") source = contract.get_state_variable_from_name("source")
@ -106,8 +110,10 @@ print(
assert is_dependent(destination, source, contract) assert is_dependent(destination, source, contract)
print("Base Derived contract") print("Base Derived contract")
contract = slither.get_contract_from_name("Base") contracts = slither.get_contract_from_name("Base")
contract_derived = slither.get_contract_from_name("Derived") assert len(contracts) == 1
contract = contracts[0]
contract_derived = slither.get_contract_from_name("Derived")[0]
destination = contract.get_state_variable_from_name("destination") destination = contract.get_state_variable_from_name("destination")
source = contract.get_state_variable_from_name("source") source = contract.get_state_variable_from_name("source")
@ -125,8 +131,9 @@ print(
assert is_dependent(destination, source, contract_derived) assert is_dependent(destination, source, contract_derived)
print("PropagateThroughArguments contract") print("PropagateThroughArguments contract")
contract = slither.get_contract_from_name("PropagateThroughArguments") contracts = slither.get_contract_from_name("PropagateThroughArguments")
assert contract assert len(contracts) == 1
contract = contracts[0]
var_tainted = contract.get_state_variable_from_name("var_tainted") var_tainted = contract.get_state_variable_from_name("var_tainted")
assert var_tainted assert var_tainted
var_not_tainted = contract.get_state_variable_from_name("var_not_tainted") var_not_tainted = contract.get_state_variable_from_name("var_not_tainted")

@ -9,8 +9,9 @@ if len(sys.argv) != 2:
slither = Slither(sys.argv[1]) slither = Slither(sys.argv[1])
# Get the contract # Get the contract
contract = slither.get_contract_from_name("Contract") contracts = slither.get_contract_from_name("Contract")
assert contract assert len(contracts) == 1
contract = contracts[0]
# Get the variable # Get the variable
entry_point = contract.get_function_from_signature("entry_point()") entry_point = contract.get_function_from_signature("entry_point()")

@ -9,8 +9,9 @@ if len(sys.argv) != 2:
slither = Slither(sys.argv[1]) slither = Slither(sys.argv[1])
# Get the contract # Get the contract
contract = slither.get_contract_from_name("Contract") contracts = slither.get_contract_from_name("Contract")
assert len(contracts) == 1
contract = contracts[0]
# Get the variable # Get the variable
var_a = contract.get_state_variable_from_name("a") var_a = contract.get_state_variable_from_name("a")

@ -11,12 +11,12 @@ def resolve_function(contract_name, function_name):
:return: Returns the resolved function, raises an exception otherwise. :return: Returns the resolved function, raises an exception otherwise.
""" """
# Obtain the target contract # Obtain the target contract
contract = slither.get_contract_from_name(contract_name) contracts = slither.get_contract_from_name(contract_name)
# Verify the contract was resolved successfully # Verify the contract was resolved successfully
if contract is None: if len(contracts) != 1:
raise ValueError(f"Could not resolve target contract: {contract_name}") raise ValueError(f"Could not resolve target contract: {contract_name}")
contract = contracts[0]
# Obtain the target function # Obtain the target function
target_function = next( target_function = next(
(function for function in contract.functions if function.name == function_name), None (function for function in contract.functions if function.name == function_name), None

@ -14,7 +14,7 @@ def visit_node(node, visited):
return return
visited += [node] visited += [node]
taints = node.function.slither.context[KEY] taints = node.function.compilation_unit.context[KEY]
refs = {} refs = {}
for ir in node.irs: for ir in node.irs:
@ -41,7 +41,7 @@ def visit_node(node, visited):
taints = [v for v in taints if not isinstance(v, (TemporaryVariable, ReferenceVariable))] taints = [v for v in taints if not isinstance(v, (TemporaryVariable, ReferenceVariable))]
node.function.slither.context[KEY] = list(set(taints)) node.function.compilation_unit.context[KEY] = list(set(taints))
for son in node.sons: for son in node.sons:
visit_node(son, visited) visit_node(son, visited)

@ -9,8 +9,9 @@ if len(sys.argv) != 2:
slither = Slither(sys.argv[1]) slither = Slither(sys.argv[1])
# Get the contract # Get the contract
contract = slither.get_contract_from_name("Contract") contracts = slither.get_contract_from_name("Contract")
assert len(contracts) == 1
contract = contracts[0]
# Get the variable # Get the variable
var_a = contract.get_state_variable_from_name("a") var_a = contract.get_state_variable_from_name("a")

@ -11,10 +11,10 @@ setup(
install_requires=[ install_requires=[
"prettytable>=0.7.2", "prettytable>=0.7.2",
"pysha3>=1.0.2", "pysha3>=1.0.2",
"crytic-compile>=0.1.13", # "crytic-compile>=0.1.13",
# "crytic-compile", "crytic-compile",
], ],
# dependency_links=["git+https://github.com/crytic/crytic-compile.git@master#egg=crytic-compile"], dependency_links=["git+https://github.com/crytic/crytic-compile.git@master#egg=crytic-compile"],
license="AGPL-3.0", license="AGPL-3.0",
long_description=open("README.md").read(), long_description=open("README.md").read(),
entry_points={ entry_points={

@ -2,7 +2,7 @@
Compute the data depenency between all the SSA variables Compute the data depenency between all the SSA variables
""" """
from collections import defaultdict from collections import defaultdict
from typing import Union, Set, Dict from typing import Union, Set, Dict, TYPE_CHECKING
from slither.core.declarations import ( from slither.core.declarations import (
Contract, Contract,
@ -26,6 +26,9 @@ from slither.slithir.variables import (
) )
from slither.core.solidity_types.type import Type from slither.core.solidity_types.type import Type
if TYPE_CHECKING:
from slither.core.compilation_unit import SlitherCompilationUnit
################################################################################### ###################################################################################
################################################################################### ###################################################################################
@ -104,8 +107,8 @@ def is_tainted(variable, context, only_unprotected=False, ignore_generic_taint=F
assert isinstance(only_unprotected, bool) assert isinstance(only_unprotected, bool)
if isinstance(variable, Constant): if isinstance(variable, Constant):
return False return False
slither = context.slither compilation_unit = context.compilation_unit
taints = slither.context[KEY_INPUT] taints = compilation_unit.context[KEY_INPUT]
if not ignore_generic_taint: if not ignore_generic_taint:
taints |= GENERIC_TAINT taints |= GENERIC_TAINT
return variable in taints or any( return variable in taints or any(
@ -126,8 +129,8 @@ def is_tainted_ssa(variable, context, only_unprotected=False, ignore_generic_tai
assert isinstance(only_unprotected, bool) assert isinstance(only_unprotected, bool)
if isinstance(variable, Constant): if isinstance(variable, Constant):
return False return False
slither = context.slither compilation_unit = context.compilation_unit
taints = slither.context[KEY_INPUT_SSA] taints = compilation_unit.context[KEY_INPUT_SSA]
if not ignore_generic_taint: if not ignore_generic_taint:
taints |= GENERIC_TAINT taints |= GENERIC_TAINT
return variable in taints or any( return variable in taints or any(
@ -258,15 +261,15 @@ def pprint_dependency(context):
################################################################################### ###################################################################################
def compute_dependency(slither): def compute_dependency(compilation_unit: "SlitherCompilationUnit"):
slither.context[KEY_INPUT] = set() compilation_unit.context[KEY_INPUT] = set()
slither.context[KEY_INPUT_SSA] = set() compilation_unit.context[KEY_INPUT_SSA] = set()
for contract in slither.contracts: for contract in compilation_unit.contracts:
compute_dependency_contract(contract, slither) compute_dependency_contract(contract, compilation_unit)
def compute_dependency_contract(contract, slither): def compute_dependency_contract(contract, compilation_unit: "SlitherCompilationUnit"):
if KEY_SSA in contract.context: if KEY_SSA in contract.context:
return return
@ -281,8 +284,8 @@ def compute_dependency_contract(contract, slither):
# pylint: disable=expression-not-assigned # pylint: disable=expression-not-assigned
if function.visibility in ["public", "external"]: if function.visibility in ["public", "external"]:
[slither.context[KEY_INPUT].add(p) for p in function.parameters] [compilation_unit.context[KEY_INPUT].add(p) for p in function.parameters]
[slither.context[KEY_INPUT_SSA].add(p) for p in function.parameters_ssa] [compilation_unit.context[KEY_INPUT_SSA].add(p) for p in function.parameters_ssa]
propagate_contract(contract, KEY_SSA, KEY_NON_SSA) propagate_contract(contract, KEY_SSA, KEY_NON_SSA)
propagate_contract(contract, KEY_SSA_UNPROTECTED, KEY_NON_SSA_UNPROTECTED) propagate_contract(contract, KEY_SSA_UNPROTECTED, KEY_NON_SSA_UNPROTECTED)

@ -47,7 +47,7 @@ from slither.core.expressions.expression import Expression
if TYPE_CHECKING: if TYPE_CHECKING:
from slither.core.declarations import Function from slither.core.declarations import Function
from slither.slithir.variables.variable import SlithIRVariable from slither.slithir.variables.variable import SlithIRVariable
from slither.core.slither_core import SlitherCore from slither.core.compilation_unit import SlitherCompilationUnit
from slither.utils.type_helpers import ( from slither.utils.type_helpers import (
InternalCallType, InternalCallType,
HighLevelCallType, HighLevelCallType,
@ -227,8 +227,8 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
################################################################################### ###################################################################################
@property @property
def slither(self) -> "SlitherCore": def compilation_unit(self) -> "SlitherCompilationUnit":
return self.function.slither return self.function.compilation_unit
@property @property
def node_id(self) -> int: def node_id(self) -> int:

@ -1,7 +1,7 @@
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from slither import Slither from slither.core.compilation_unit import SlitherCompilationUnit
from slither.core.cfg.node import Node from slither.core.cfg.node import Node
from slither.core.declarations import Function, Contract from slither.core.declarations import Function, Contract
@ -27,5 +27,5 @@ class ChildNode:
return self.node.function.contract return self.node.function.contract
@property @property
def slither(self) -> "Slither": def compilation_unit(self) -> "SlitherCompilationUnit":
return self.contract.slither return self.contract.compilation_unit

@ -1,17 +0,0 @@
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from slither import Slither
class ChildSlither:
def __init__(self):
super().__init__()
self._slither = None
def set_slither(self, slither: "Slither"):
self._slither = slither
@property
def slither(self) -> "Slither":
return self._slither

@ -0,0 +1,267 @@
import math
from collections import defaultdict
from typing import Optional, Dict, List, Set, Union, TYPE_CHECKING, Tuple
from crytic_compile import CompilationUnit, CryticCompile
from crytic_compile.compiler.compiler import CompilerVersion
from slither.core.context.context import Context
from slither.core.declarations import (
Contract,
Pragma,
Import,
Function,
Modifier,
)
from slither.core.declarations.enum_top_level import EnumTopLevel
from slither.core.declarations.function_top_level import FunctionTopLevel
from slither.core.declarations.structure_top_level import StructureTopLevel
from slither.core.variables.state_variable import StateVariable
from slither.core.variables.top_level_variable import TopLevelVariable
from slither.slithir.operations import InternalCall
from slither.slithir.variables import Constant
if TYPE_CHECKING:
from slither.core.slither_core import SlitherCore
# pylint: disable=too-many-instance-attributes,too-many-public-methods
class SlitherCompilationUnit(Context):
def __init__(self, core: "SlitherCore", crytic_compilation_unit: CompilationUnit):
super().__init__()
self._core = core
self._crytic_compile_compilation_unit = crytic_compilation_unit
# Top level object
self._contracts: Dict[str, Contract] = {}
self._structures_top_level: List[StructureTopLevel] = []
self._enums_top_level: List[EnumTopLevel] = []
self._variables_top_level: List[TopLevelVariable] = []
self._functions_top_level: List[FunctionTopLevel] = []
self._pragma_directives: List[Pragma] = []
self._import_directives: List[Import] = []
self._all_functions: Set[Function] = set()
self._all_modifiers: Set[Modifier] = set()
# Memoize
self._all_state_variables: Optional[Set[StateVariable]] = None
self._storage_layouts: Dict[str, Dict[str, Tuple[int, int]]] = {}
self._contract_name_collisions = defaultdict(list)
self._contract_with_missing_inheritance = set()
self._source_units: Dict[int, str] = {}
self.counter_slithir_tuple = 0
self.counter_slithir_temporary = 0
self.counter_slithir_reference = 0
@property
def core(self) -> "SlitherCore":
return self._core
@property
def source_units(self) -> Dict[int, str]:
return self._source_units
# endregion
###################################################################################
###################################################################################
# region Compiler
###################################################################################
###################################################################################
@property
def compiler_version(self) -> CompilerVersion:
return self._crytic_compile_compilation_unit.compiler_version
@property
def solc_version(self) -> str:
return self._crytic_compile_compilation_unit.compiler_version.version
@property
def crytic_compile_compilation_unit(self) -> CompilationUnit:
return self._crytic_compile_compilation_unit
@property
def crytic_compile(self) -> CryticCompile:
return self._crytic_compile_compilation_unit.crytic_compile
# endregion
###################################################################################
###################################################################################
# region Pragma attributes
###################################################################################
###################################################################################
@property
def pragma_directives(self) -> List[Pragma]:
""" list(core.declarations.Pragma): Pragma directives."""
return self._pragma_directives
@property
def import_directives(self) -> List[Import]:
""" list(core.declarations.Import): Import directives"""
return self._import_directives
# endregion
###################################################################################
###################################################################################
# region Contracts
###################################################################################
###################################################################################
@property
def contracts(self) -> List[Contract]:
"""list(Contract): List of contracts."""
return list(self._contracts.values())
@property
def contracts_derived(self) -> List[Contract]:
"""list(Contract): List of contracts that are derived and not inherited."""
inheritances = [x.inheritance for x in self.contracts]
inheritance = [item for sublist in inheritances for item in sublist]
return [c for c in self._contracts.values() if c not in inheritance and not c.is_top_level]
@property
def contracts_as_dict(self) -> Dict[str, Contract]:
"""list(dict(str: Contract): List of contracts as dict: name -> Contract."""
return self._contracts
def get_contract_from_name(self, contract_name: Union[str, Constant]) -> Optional[Contract]:
"""
Return a contract from a name
Args:
contract_name (str): name of the contract
Returns:
Contract
"""
return next((c for c in self.contracts if c.name == contract_name), None)
# endregion
###################################################################################
###################################################################################
# region Functions and modifiers
###################################################################################
###################################################################################
@property
def functions(self) -> List[Function]:
return list(self._all_functions)
def add_function(self, func: Function):
self._all_functions.add(func)
@property
def modifiers(self) -> List[Modifier]:
return list(self._all_modifiers)
def add_modifier(self, modif: Modifier):
self._all_modifiers.add(modif)
@property
def functions_and_modifiers(self) -> List[Function]:
return self.functions + self.modifiers
def propagate_function_calls(self):
for f in self.functions_and_modifiers:
for node in f.nodes:
for ir in node.irs_ssa:
if isinstance(ir, InternalCall):
ir.function.add_reachable_from_node(node, ir)
# endregion
###################################################################################
###################################################################################
# region Variables
###################################################################################
###################################################################################
@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]
self._all_state_variables = set(state_variables)
return list(self._all_state_variables)
# endregion
###################################################################################
###################################################################################
# region Top level
###################################################################################
###################################################################################
@property
def structures_top_level(self) -> List[StructureTopLevel]:
return self._structures_top_level
@property
def enums_top_level(self) -> List[EnumTopLevel]:
return self._enums_top_level
@property
def variables_top_level(self) -> List[TopLevelVariable]:
return self._variables_top_level
@property
def functions_top_level(self) -> List[FunctionTopLevel]:
return self._functions_top_level
# endregion
###################################################################################
###################################################################################
# region Internals
###################################################################################
###################################################################################
@property
def contract_name_collisions(self) -> Dict:
return self._contract_name_collisions
@property
def contracts_with_missing_inheritance(self) -> Set:
return self._contract_with_missing_inheritance
# endregion
###################################################################################
###################################################################################
# region Storage Layouts
###################################################################################
###################################################################################
def compute_storage_layout(self):
for contract in self.contracts_derived:
self._storage_layouts[contract.name] = {}
slot = 0
offset = 0
for var in contract.state_variables_ordered:
if var.is_constant:
continue
size, new_slot = var.type.storage_size
if new_slot:
if offset > 0:
slot += 1
offset = 0
elif size + offset > 32:
slot += 1
offset = 0
self._storage_layouts[contract.name][var.canonical_name] = (
slot,
offset,
)
if new_slot:
slot += math.ceil(size / 32)
else:
offset += size
def storage_layout_of(self, contract, var) -> Tuple[int, int]:
return self._storage_layouts[contract.name][var.canonical_name]
# endregion

@ -7,7 +7,6 @@ from typing import Optional, List, Dict, Callable, Tuple, TYPE_CHECKING, Union
from crytic_compile.platform import Type as PlatformType from crytic_compile.platform import Type as PlatformType
from slither.core.children.child_slither import ChildSlither
from slither.core.solidity_types.type import Type from slither.core.solidity_types.type import Type
from slither.core.source_mapping.source_mapping import SourceMapping from slither.core.source_mapping.source_mapping import SourceMapping
@ -36,16 +35,18 @@ if TYPE_CHECKING:
from slither.slithir.variables.variable import SlithIRVariable from slither.slithir.variables.variable import SlithIRVariable
from slither.core.variables.variable import Variable from slither.core.variables.variable import Variable
from slither.core.variables.state_variable import StateVariable from slither.core.variables.state_variable import StateVariable
from slither.core.compilation_unit import SlitherCompilationUnit
LOGGER = logging.getLogger("Contract") LOGGER = logging.getLogger("Contract")
class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-methods class Contract(SourceMapping): # pylint: disable=too-many-public-methods
""" """
Contract class Contract class
""" """
def __init__(self): def __init__(self, compilation_unit: "SlitherCompilationUnit"):
super().__init__() super().__init__()
self._name: Optional[str] = None self._name: Optional[str] = None
@ -86,6 +87,8 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
self._available_functions_as_dict: Optional[Dict[str, "Function"]] = None self._available_functions_as_dict: Optional[Dict[str, "Function"]] = None
self._all_functions_called: Optional[List["InternalCallType"]] = None self._all_functions_called: Optional[List["InternalCallType"]] = None
self.compilation_unit: "SlitherCompilationUnit" = compilation_unit
################################################################################### ###################################################################################
################################################################################### ###################################################################################
# region General's properties # region General's properties
@ -572,7 +575,7 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
""" """
list(Contract): Return the list of contracts derived from self list(Contract): Return the list of contracts derived from self
""" """
candidates = self.slither.contracts 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]
# endregion # endregion
@ -982,9 +985,9 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
################################################################################### ###################################################################################
def is_from_dependency(self) -> bool: def is_from_dependency(self) -> bool:
if self.slither.crytic_compile is None: return self.compilation_unit.core.crytic_compile.is_dependency(
return False self.source_mapping["filename_absolute"]
return self.slither.crytic_compile.is_dependency(self.source_mapping["filename_absolute"]) )
# endregion # endregion
################################################################################### ###################################################################################
@ -999,12 +1002,11 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
Return true if the contract is the Migrations contract needed for Truffle Return true if the contract is the Migrations contract needed for Truffle
:return: :return:
""" """
if self.slither.crytic_compile: if self.compilation_unit.core.crytic_compile.platform == PlatformType.TRUFFLE:
if self.slither.crytic_compile.platform == PlatformType.TRUFFLE: if self.name == "Migrations":
if self.name == "Migrations": paths = Path(self.source_mapping["filename_absolute"]).parts
paths = Path(self.source_mapping["filename_absolute"]).parts if len(paths) >= 2:
if len(paths) >= 2: return paths[-2] == "contracts" and paths[-1] == "migrations.sol"
return paths[-2] == "contracts" and paths[-1] == "migrations.sol"
return False return False
@property @property
@ -1035,7 +1037,7 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
self._is_upgradeable = False self._is_upgradeable = False
if self.is_upgradeable_proxy: if self.is_upgradeable_proxy:
return False return False
initializable = self.slither.get_contract_from_name("Initializable") initializable = self.compilation_unit.get_contract_from_name("Initializable")
if initializable: if initializable:
if initializable in self.inheritance: if initializable in self.inheritance:
self._is_upgradeable = True self._is_upgradeable = True
@ -1102,14 +1104,14 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
for (idx, variable_candidate) in enumerate(self.state_variables): for (idx, variable_candidate) in enumerate(self.state_variables):
if variable_candidate.expression and not variable_candidate.is_constant: if variable_candidate.expression and not variable_candidate.is_constant:
constructor_variable = FunctionContract(self.slither) constructor_variable = FunctionContract(self.compilation_unit)
constructor_variable.set_function_type(FunctionType.CONSTRUCTOR_VARIABLES) constructor_variable.set_function_type(FunctionType.CONSTRUCTOR_VARIABLES)
constructor_variable.set_contract(self) constructor_variable.set_contract(self)
constructor_variable.set_contract_declarer(self) constructor_variable.set_contract_declarer(self)
constructor_variable.set_visibility("internal") constructor_variable.set_visibility("internal")
# For now, source mapping of the constructor variable is the whole contract # For now, source mapping of the constructor variable is the whole contract
# Could be improved with a targeted source mapping # Could be improved with a targeted source mapping
constructor_variable.set_offset(self.source_mapping, self.slither) constructor_variable.set_offset(self.source_mapping, self.compilation_unit)
self._functions[constructor_variable.canonical_name] = constructor_variable self._functions[constructor_variable.canonical_name] = constructor_variable
prev_node = self._create_node(constructor_variable, 0, variable_candidate) prev_node = self._create_node(constructor_variable, 0, variable_candidate)
@ -1128,7 +1130,7 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
for (idx, variable_candidate) in enumerate(self.state_variables): for (idx, variable_candidate) in enumerate(self.state_variables):
if variable_candidate.expression and variable_candidate.is_constant: if variable_candidate.expression and variable_candidate.is_constant:
constructor_variable = FunctionContract(self.slither) constructor_variable = FunctionContract(self.compilation_unit)
constructor_variable.set_function_type( constructor_variable.set_function_type(
FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES
) )
@ -1137,7 +1139,7 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
constructor_variable.set_visibility("internal") constructor_variable.set_visibility("internal")
# For now, source mapping of the constructor variable is the whole contract # For now, source mapping of the constructor variable is the whole contract
# Could be improved with a targeted source mapping # Could be improved with a targeted source mapping
constructor_variable.set_offset(self.source_mapping, self.slither) constructor_variable.set_offset(self.source_mapping, self.compilation_unit)
self._functions[constructor_variable.canonical_name] = constructor_variable self._functions[constructor_variable.canonical_name] = constructor_variable
prev_node = self._create_node(constructor_variable, 0, variable_candidate) prev_node = self._create_node(constructor_variable, 0, variable_candidate)
@ -1164,7 +1166,7 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
# Function uses to create node for state variable declaration statements # Function uses to create node for state variable declaration statements
node = Node(NodeType.OTHER_ENTRYPOINT, counter) node = Node(NodeType.OTHER_ENTRYPOINT, counter)
node.set_offset(variable.source_mapping, self.slither) node.set_offset(variable.source_mapping, self.compilation_unit)
node.set_function(func) node.set_function(func)
func.add_node(node) func.add_node(node)
assert variable.expression assert variable.expression
@ -1175,7 +1177,7 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
variable.type, variable.type,
) )
expression.set_offset(variable.source_mapping, self.slither) expression.set_offset(variable.source_mapping, self.compilation_unit)
node.add_expression(expression) node.add_expression(expression)
return node return node

@ -42,8 +42,7 @@ if TYPE_CHECKING:
from slither.slithir.variables import LocalIRVariable from slither.slithir.variables import LocalIRVariable
from slither.core.expressions.expression import Expression from slither.core.expressions.expression import Expression
from slither.slithir.operations import Operation from slither.slithir.operations import Operation
from slither.slither import Slither from slither.core.compilation_unit import SlitherCompilationUnit
from slither.core.slither_core import SlitherCore
LOGGER = logging.getLogger("Function") LOGGER = logging.getLogger("Function")
ReacheableNode = namedtuple("ReacheableNode", ["node", "ir"]) ReacheableNode = namedtuple("ReacheableNode", ["node", "ir"])
@ -109,7 +108,7 @@ class Function(metaclass=ABCMeta): # pylint: disable=too-many-public-methods
Function class Function class
""" """
def __init__(self, slither: "SlitherCore"): def __init__(self, compilation_unit: "SlitherCompilationUnit"):
super().__init__() super().__init__()
self._scope: List[str] = [] self._scope: List[str] = []
self._name: Optional[str] = None self._name: Optional[str] = None
@ -205,7 +204,7 @@ class Function(metaclass=ABCMeta): # pylint: disable=too-many-public-methods
self._canonical_name: Optional[str] = None self._canonical_name: Optional[str] = None
self._is_protected: Optional[bool] = None self._is_protected: Optional[bool] = None
self._slither: "SlitherCore" = slither self.compilation_unit: "SlitherCompilationUnit" = compilation_unit
################################################################################### ###################################################################################
################################################################################### ###################################################################################
@ -314,13 +313,13 @@ class Function(metaclass=ABCMeta): # pylint: disable=too-many-public-methods
return True return True
return self._can_send_eth return self._can_send_eth
@property # @property
def slither(self) -> "SlitherCore": # def slither(self) -> "SlitherCore":
return self._slither # return self._slither
#
@slither.setter # @slither.setter
def slither(self, sl: "SlitherCore"): # def slither(self, sl: "SlitherCore"):
self._slither = sl # self._slither = sl
# endregion # endregion
################################################################################### ###################################################################################
@ -1532,7 +1531,7 @@ class Function(metaclass=ABCMeta): # pylint: disable=too-many-public-methods
from slither.core.cfg.node import Node from slither.core.cfg.node import Node
node = Node(node_type, self._counter_nodes) node = Node(node_type, self._counter_nodes)
node.set_offset(src, self.slither) node.set_offset(src, self.compilation_unit)
self._counter_nodes += 1 self._counter_nodes += 1
node.set_function(self) node.set_function(self)
self._nodes.append(node) self._nodes.append(node)

@ -3,28 +3,15 @@
""" """
import json import json
import logging import logging
import math
import os import os
import re import re
from collections import defaultdict from typing import Optional, Dict, List, Set, Union
from typing import Optional, Dict, List, Set, Union, Tuple
from crytic_compile import CryticCompile from crytic_compile import CryticCompile
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.core.context.context import Context from slither.core.context.context import Context
from slither.core.declarations import ( from slither.core.declarations import Contract
Contract,
Pragma,
Import,
Function,
Modifier,
)
from slither.core.declarations.enum_top_level import EnumTopLevel
from slither.core.declarations.function_top_level import FunctionTopLevel
from slither.core.declarations.structure_top_level import StructureTopLevel
from slither.core.variables.state_variable import StateVariable
from slither.core.variables.top_level_variable import TopLevelVariable
from slither.slithir.operations import InternalCall
from slither.slithir.variables import Constant from slither.slithir.variables import Constant
from slither.utils.colors import red from slither.utils.colors import red
@ -39,7 +26,8 @@ def _relative_path_format(path: str) -> str:
return path.split("..")[-1].strip(".").strip("/") return path.split("..")[-1].strip(".").strip("/")
class SlitherCore(Context): # pylint: disable=too-many-instance-attributes,too-many-public-methods # pylint: disable=too-many-instance-attributes,too-many-public-methods
class SlitherCore(Context):
""" """
Slither static analyzer Slither static analyzer
""" """
@ -47,29 +35,19 @@ class SlitherCore(Context): # pylint: disable=too-many-instance-attributes,too-
def __init__(self): def __init__(self):
super().__init__() super().__init__()
# Top level object
self._contracts: Dict[str, Contract] = {}
self._structures_top_level: List[StructureTopLevel] = []
self._enums_top_level: List[EnumTopLevel] = []
self._variables_top_level: List[TopLevelVariable] = []
self._functions_top_level: List[FunctionTopLevel] = []
self._pragma_directives: List[Pragma] = []
self._import_directives: List[Import] = []
self._filename: Optional[str] = None self._filename: Optional[str] = None
self._source_units: Dict[int, str] = {}
self._solc_version: Optional[str] = None # '0.3' or '0.4':!
self._raw_source_code: Dict[str, str] = {} self._raw_source_code: Dict[str, str] = {}
self._source_code_to_line: Optional[Dict[str, List[str]]] = None self._source_code_to_line: Optional[Dict[str, List[str]]] = None
self._all_functions: Set[Function] = set()
self._all_modifiers: Set[Modifier] = set()
# Memoize
self._all_state_variables: Optional[Set[StateVariable]] = None
self._previous_results_filename: str = "slither.db.json" self._previous_results_filename: str = "slither.db.json"
self._results_to_hide: List = [] self._results_to_hide: List = []
self._previous_results: List = [] self._previous_results: List = []
# From triaged result
self._previous_results_ids: Set[str] = set() self._previous_results_ids: Set[str] = set()
# Every slither object has a list of result from detector
# Because of the multiple compilation support, we might analyze
# Multiple time the same result, so we remove dupplicate
self._currently_seen_resuts: Set[str] = set()
self._paths_to_filter: Set[str] = set() self._paths_to_filter: Set[str] = set()
self._crytic_compile: Optional[CryticCompile] = None self._crytic_compile: Optional[CryticCompile] = None
@ -79,87 +57,23 @@ class SlitherCore(Context): # pylint: disable=too-many-instance-attributes,too-
self._markdown_root = "" self._markdown_root = ""
self._contract_name_collisions = defaultdict(list)
self._contract_with_missing_inheritance = set()
self._storage_layouts: Dict[str, Dict[str, Tuple[int, int]]] = {}
# If set to true, slither will not catch errors during parsing # If set to true, slither will not catch errors during parsing
self._disallow_partial: bool = False self._disallow_partial: bool = False
self._skip_assembly: bool = False self._skip_assembly: bool = False
self._show_ignored_findings = False self._show_ignored_findings = False
self.counter_slithir_tuple = 0 self._compilation_units: List[SlitherCompilationUnit] = []
self.counter_slithir_temporary = 0
self.counter_slithir_reference = 0
################################################################################### self._contracts: List[Contract] = []
################################################################################### self._contracts_derived: List[Contract] = []
# region Source code
###################################################################################
###################################################################################
@property @property
def source_code(self) -> Dict[str, str]: def compilation_units(self) -> List[SlitherCompilationUnit]:
""" {filename: source_code (str)}: source code """ return list(self._compilation_units)
return self._raw_source_code
@property def add_compilation_unit(self, compilation_unit: SlitherCompilationUnit):
def source_units(self) -> Dict[int, str]: self._compilation_units.append(compilation_unit)
return self._source_units
@property
def filename(self) -> Optional[str]:
"""str: Filename."""
return self._filename
@filename.setter
def filename(self, filename: str):
self._filename = filename
def add_source_code(self, path):
"""
:param path:
:return:
"""
if self.crytic_compile and path in self.crytic_compile.src_content:
self.source_code[path] = self.crytic_compile.src_content[path]
else:
with open(path, encoding="utf8", newline="") as f:
self.source_code[path] = f.read()
@property
def markdown_root(self) -> str:
return self._markdown_root
# endregion
###################################################################################
###################################################################################
# region Pragma attributes
###################################################################################
###################################################################################
@property
def solc_version(self) -> str:
"""str: Solidity version."""
if self.crytic_compile:
return self.crytic_compile.compiler_version.version
return self._solc_version
@solc_version.setter
def solc_version(self, version: str):
self._solc_version = version
@property
def pragma_directives(self) -> List[Pragma]:
""" list(core.declarations.Pragma): Pragma directives."""
return self._pragma_directives
@property
def import_directives(self) -> List[Import]:
""" list(core.declarations.Import): Import directives"""
return self._import_directives
# endregion # endregion
################################################################################### ###################################################################################
@ -170,22 +84,23 @@ class SlitherCore(Context): # pylint: disable=too-many-instance-attributes,too-
@property @property
def contracts(self) -> List[Contract]: def contracts(self) -> List[Contract]:
"""list(Contract): List of contracts.""" if not self._contracts:
return list(self._contracts.values()) all_contracts = [
compilation_unit.contracts for compilation_unit in self._compilation_units
]
self._contracts = [item for sublist in all_contracts for item in sublist]
return self._contracts
@property @property
def contracts_derived(self) -> List[Contract]: def contracts_derived(self) -> List[Contract]:
"""list(Contract): List of contracts that are derived and not inherited.""" if not self._contracts_derived:
inheritance = (x.inheritance for x in self.contracts) all_contracts = [
inheritance = [item for sublist in inheritance for item in sublist] compilation_unit.contracts_derived for compilation_unit in self._compilation_units
return [c for c in self._contracts.values() if c not in inheritance and not c.is_top_level] ]
self._contracts_derived = [item for sublist in all_contracts for item in sublist]
@property return self._contracts_derived
def contracts_as_dict(self) -> Dict[str, Contract]:
"""list(dict(str: Contract): List of contracts as dict: name -> Contract.""" def get_contract_from_name(self, contract_name: Union[str, Constant]) -> List[Contract]:
return self._contracts
def get_contract_from_name(self, contract_name: Union[str, Constant]) -> Optional[Contract]:
""" """
Return a contract from a name Return a contract from a name
Args: Args:
@ -193,92 +108,56 @@ class SlitherCore(Context): # pylint: disable=too-many-instance-attributes,too-
Returns: Returns:
Contract Contract
""" """
return next((c for c in self.contracts if c.name == contract_name), None) contracts = []
for compilation_unit in self._compilation_units:
# endregion contract = compilation_unit.get_contract_from_name(contract_name)
################################################################################### if contract:
################################################################################### contracts.append(contract)
# region Functions and modifiers return contracts
###################################################################################
###################################################################################
@property
def functions(self) -> List[Function]:
return list(self._all_functions)
def add_function(self, func: Function):
self._all_functions.add(func)
@property
def modifiers(self) -> List[Modifier]:
return list(self._all_modifiers)
def add_modifier(self, modif: Modifier):
self._all_modifiers.add(modif)
@property
def functions_and_modifiers(self) -> List[Function]:
return self.functions + self.modifiers
def propagate_function_calls(self):
for f in self.functions_and_modifiers:
for node in f.nodes:
for ir in node.irs_ssa:
if isinstance(ir, InternalCall):
ir.function.add_reachable_from_node(node, ir)
# endregion
################################################################################### ###################################################################################
################################################################################### ###################################################################################
# region Variables # region Source code
################################################################################### ###################################################################################
################################################################################### ###################################################################################
@property @property
def state_variables(self) -> List[StateVariable]: def source_code(self) -> Dict[str, str]:
if self._all_state_variables is None: """ {filename: source_code (str)}: source code """
state_variables = [c.state_variables for c in self.contracts] return self._raw_source_code
state_variables = [item for sublist in state_variables for item in sublist]
self._all_state_variables = set(state_variables)
return list(self._all_state_variables)
# endregion
###################################################################################
###################################################################################
# region Top level
###################################################################################
###################################################################################
@property @property
def structures_top_level(self) -> List[StructureTopLevel]: def filename(self) -> Optional[str]:
return self._structures_top_level """str: Filename."""
return self._filename
@property @filename.setter
def enums_top_level(self) -> List[EnumTopLevel]: def filename(self, filename: str):
return self._enums_top_level self._filename = filename
@property def add_source_code(self, path):
def variables_top_level(self) -> List[TopLevelVariable]: """
return self._variables_top_level :param path:
:return:
"""
if self.crytic_compile and path in self.crytic_compile.src_content:
self.source_code[path] = self.crytic_compile.src_content[path]
else:
with open(path, encoding="utf8", newline="") as f:
self.source_code[path] = f.read()
@property @property
def functions_top_level(self) -> List[FunctionTopLevel]: def markdown_root(self) -> str:
return self._functions_top_level return self._markdown_root
# endregion
###################################################################################
###################################################################################
# region Export
###################################################################################
###################################################################################
def print_functions(self, d: str): def print_functions(self, d: str):
""" """
Export all the functions to dot files Export all the functions to dot files
""" """
for c in self.contracts: for compilation_unit in self._compilation_units:
for f in c.functions: for c in compilation_unit.contracts:
f.cfg_to_dot(os.path.join(d, "{}.{}.dot".format(c.name, f.name))) for f in c.functions:
f.cfg_to_dot(os.path.join(d, "{}.{}.dot".format(c.name, f.name)))
# endregion # endregion
################################################################################### ###################################################################################
@ -329,6 +208,12 @@ class SlitherCore(Context): # pylint: disable=too-many-instance-attributes,too-
- The --exclude-dependencies flag is set and results are only related to dependencies - The --exclude-dependencies flag is set and results are only related to dependencies
- There is an ignore comment on the preceding line - There is an ignore comment on the preceding line
""" """
# Remove dupplicate due to the multiple compilation support
if r["id"] in self._currently_seen_resuts:
return False
self._currently_seen_resuts.add(r["id"])
source_mapping_elements = [ source_mapping_elements = [
elem["source_mapping"].get("filename_absolute", "unknown") elem["source_mapping"].get("filename_absolute", "unknown")
for elem in r["elements"] for elem in r["elements"]
@ -433,14 +318,6 @@ class SlitherCore(Context): # pylint: disable=too-many-instance-attributes,too-
################################################################################### ###################################################################################
################################################################################### ###################################################################################
@property
def contract_name_collisions(self) -> Dict:
return self._contract_name_collisions
@property
def contracts_with_missing_inheritance(self) -> Set:
return self._contract_with_missing_inheritance
@property @property
def disallow_partial(self) -> bool: def disallow_partial(self) -> bool:
""" """
@ -460,42 +337,3 @@ class SlitherCore(Context): # pylint: disable=too-many-instance-attributes,too-
return self.show_ignore_findings return self.show_ignore_findings
# endregion # endregion
###################################################################################
###################################################################################
# region Storage Layouts
###################################################################################
###################################################################################
def compute_storage_layout(self):
for contract in self.contracts_derived:
self._storage_layouts[contract.name] = {}
slot = 0
offset = 0
for var in contract.state_variables_ordered:
if var.is_constant:
continue
size, new_slot = var.type.storage_size
if new_slot:
if offset > 0:
slot += 1
offset = 0
elif size + offset > 32:
slot += 1
offset = 0
self._storage_layouts[contract.name][var.canonical_name] = (
slot,
offset,
)
if new_slot:
slot += math.ceil(size / 32)
else:
offset += size
def storage_layout_of(self, contract, var) -> Tuple[int, int]:
return self._storage_layouts[contract.name][var.canonical_name]
# endregion

@ -1,8 +1,11 @@
import re import re
from typing import Dict, Union, Optional, List, Tuple from typing import Dict, Union, Optional, List, Tuple, TYPE_CHECKING
from slither.core.context.context import Context from slither.core.context.context import Context
if TYPE_CHECKING:
from slither.core.compilation_unit import SlitherCompilationUnit
class SourceMapping(Context): class SourceMapping(Context):
def __init__(self): def __init__(self):
@ -25,27 +28,33 @@ class SourceMapping(Context):
return self._source_mapping return self._source_mapping
@staticmethod @staticmethod
def _compute_line(slither, filename, start: int, length: int) -> Tuple[List[int], int, int]: def _compute_line(
compilation_unit: "SlitherCompilationUnit", filename, start: int, length: int
) -> Tuple[List[int], int, int]:
""" """
Compute line(s) numbers and starting/ending columns Compute line(s) numbers and starting/ending columns
from a start/end offset. All numbers start from 1. from a start/end offset. All numbers start from 1.
Not done in an efficient way Not done in an efficient way
""" """
start_line, starting_column = slither.crytic_compile.get_line_from_offset(filename, start) start_line, starting_column = compilation_unit.core.crytic_compile.get_line_from_offset(
end_line, ending_column = slither.crytic_compile.get_line_from_offset( filename, start
)
end_line, ending_column = compilation_unit.core.crytic_compile.get_line_from_offset(
filename, start + length filename, start + length
) )
return list(range(start_line, end_line + 1)), starting_column, ending_column return list(range(start_line, end_line + 1)), starting_column, ending_column
def _convert_source_mapping(self, offset: str, slither): # pylint: disable=too-many-locals def _convert_source_mapping(
self, offset: str, compilation_unit: "SlitherCompilationUnit"
): # pylint: disable=too-many-locals
""" """
Convert a text offset to a real offset Convert a text offset to a real offset
see https://solidity.readthedocs.io/en/develop/miscellaneous.html#source-mappings see https://solidity.readthedocs.io/en/develop/miscellaneous.html#source-mappings
Returns: Returns:
(dict): {'start':0, 'length':0, 'filename': 'file.sol'} (dict): {'start':0, 'length':0, 'filename': 'file.sol'}
""" """
sourceUnits = slither.source_units sourceUnits = compilation_unit.source_units
position = re.findall("([0-9]*):([0-9]*):([-]?[0-9]*)", offset) position = re.findall("([0-9]*):([0-9]*):([-]?[0-9]*)", offset)
if len(position) != 1: if len(position) != 1:
@ -66,30 +75,32 @@ class SourceMapping(Context):
is_dependency = False is_dependency = False
# If possible, convert the filename to its absolute/relative version # If possible, convert the filename to its absolute/relative version
if slither.crytic_compile: if compilation_unit.core.crytic_compile:
filenames = slither.crytic_compile.filename_lookup(filename_used) filenames = compilation_unit.core.crytic_compile.filename_lookup(filename_used)
filename_absolute = filenames.absolute filename_absolute = filenames.absolute
filename_relative = filenames.relative filename_relative = filenames.relative
filename_short = filenames.short filename_short = filenames.short
is_dependency = slither.crytic_compile.is_dependency(filename_absolute) is_dependency = compilation_unit.core.crytic_compile.is_dependency(filename_absolute)
if ( if (
filename_absolute in slither.source_code filename_absolute in compilation_unit.core.source_code
or filename_absolute in slither.crytic_compile.src_content or filename_absolute in compilation_unit.core.crytic_compile.src_content
): ):
filename = filename_absolute filename = filename_absolute
elif filename_relative in slither.source_code: elif filename_relative in compilation_unit.core.source_code:
filename = filename_relative filename = filename_relative
elif filename_short in slither.source_code: elif filename_short in compilation_unit.core.source_code:
filename = filename_short filename = filename_short
else: else:
filename = filename_used filename = filename_used
else: else:
filename = filename_used filename = filename_used
if slither.crytic_compile: if compilation_unit.core.crytic_compile:
(lines, starting_column, ending_column) = self._compute_line(slither, filename, s, l) (lines, starting_column, ending_column) = self._compute_line(
compilation_unit, filename, s, l
)
else: else:
(lines, starting_column, ending_column) = ([], None, None) (lines, starting_column, ending_column) = ([], None, None)
@ -106,11 +117,11 @@ class SourceMapping(Context):
"ending_column": ending_column, "ending_column": ending_column,
} }
def set_offset(self, offset: Union[Dict, str], slither): def set_offset(self, offset: Union[Dict, str], compilation_unit: "SlitherCompilationUnit"):
if isinstance(offset, dict): if isinstance(offset, dict):
self._source_mapping = offset self._source_mapping = offset
else: else:
self._source_mapping = self._convert_source_mapping(offset, slither) self._source_mapping = self._convert_source_mapping(offset, compilation_unit)
def _get_lines_str(self, line_descr=""): def _get_lines_str(self, line_descr=""):
lines = self.source_mapping.get("lines", None) lines = self.source_mapping.get("lines", None)

@ -1,13 +1,18 @@
import abc import abc
import re import re
from typing import Optional from typing import Optional, List, TYPE_CHECKING
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.core.declarations import Contract
from slither.utils.colors import green, yellow, red from slither.utils.colors import green, yellow, red
from slither.formatters.exceptions import FormatImpossible from slither.formatters.exceptions import FormatImpossible
from slither.formatters.utils.patches import apply_patch, create_diff from slither.formatters.utils.patches import apply_patch, create_diff
from slither.utils.comparable_enum import ComparableEnum from slither.utils.comparable_enum import ComparableEnum
from slither.utils.output import Output from slither.utils.output import Output
if TYPE_CHECKING:
from slither import Slither
class IncorrectDetectorInitialization(Exception): class IncorrectDetectorInitialization(Exception):
pass pass
@ -53,10 +58,11 @@ class AbstractDetector(metaclass=abc.ABCMeta):
STANDARD_JSON = True STANDARD_JSON = True
def __init__(self, slither, logger): def __init__(self, compilation_unit: SlitherCompilationUnit, slither, logger):
self.slither = slither self.compilation_unit: SlitherCompilationUnit = compilation_unit
self.contracts = slither.contracts self.contracts: List[Contract] = compilation_unit.contracts
self.filename = slither.filename self.slither: "Slither" = slither
# self.filename = slither.filename
self.logger = logger self.logger = logger
if not self.HELP: if not self.HELP:
@ -135,17 +141,12 @@ class AbstractDetector(metaclass=abc.ABCMeta):
# pylint: disable=too-many-branches # pylint: disable=too-many-branches
def detect(self): def detect(self):
all_results = self._detect()
# Keep only dictionaries
all_results = [r.data for r in all_results]
results = [] results = []
# only keep valid result, and remove dupplicate # only keep valid result, and remove dupplicate
# pylint: disable=expression-not-assigned # Keep only dictionaries
[ for r in [r.data for r in self._detect()]:
results.append(r) if self.compilation_unit.core.valid_result(r) and r not in results:
for r in all_results results.append(r)
if self.slither.valid_result(r) and r not in results
]
if results: if results:
if self.logger: if self.logger:
info = "\n" info = "\n"
@ -155,15 +156,15 @@ class AbstractDetector(metaclass=abc.ABCMeta):
info += result["description"] info += result["description"]
info += "Reference: {}".format(self.WIKI) info += "Reference: {}".format(self.WIKI)
self._log(info) self._log(info)
if self.slither.generate_patches: if self.compilation_unit.core.generate_patches:
for result in results: for result in results:
try: try:
self._format(self.slither, result) self._format(self.compilation_unit, result)
if not "patches" in result: if not "patches" in result:
continue continue
result["patches_diff"] = dict() result["patches_diff"] = dict()
for file in result["patches"]: for file in result["patches"]:
original_txt = self.slither.source_code[file].encode("utf8") original_txt = self.compilation_unit.core.source_code[file].encode("utf8")
patched_txt = original_txt patched_txt = original_txt
offset = 0 offset = 0
patches = result["patches"][file] patches = result["patches"][file]
@ -178,7 +179,7 @@ class AbstractDetector(metaclass=abc.ABCMeta):
continue continue
for patch in patches: for patch in patches:
patched_txt, offset = apply_patch(patched_txt, patch, offset) patched_txt, offset = apply_patch(patched_txt, patch, offset)
diff = create_diff(self.slither, original_txt, patched_txt, file) diff = create_diff(self.compilation_unit, original_txt, patched_txt, file)
if not diff: if not diff:
self._log(f"Impossible to generate patch; empty {result}") self._log(f"Impossible to generate patch; empty {result}")
else: else:
@ -232,6 +233,6 @@ class AbstractDetector(metaclass=abc.ABCMeta):
return output return output
@staticmethod @staticmethod
def _format(_slither, _result): def _format(_compilation_unit: SlitherCompilationUnit, _result):
"""Implement format""" """Implement format"""
return return

@ -52,7 +52,7 @@ All the calls to `get` revert, breaking Bob's smart contract execution."""
list: {'vuln', 'filename,'contract','func','#varsWritten'} list: {'vuln', 'filename,'contract','func','#varsWritten'}
""" """
results = [] results = []
if self.slither.solc_version and self.slither.solc_version >= "0.5.0": if self.compilation_unit.solc_version and self.compilation_unit.solc_version >= "0.5.0":
return results return results
for c in self.contracts: for c in self.contracts:
for f in c.functions: for f in c.functions:
@ -70,5 +70,5 @@ All the calls to `get` revert, breaking Bob's smart contract execution."""
return results return results
@staticmethod @staticmethod
def _format(slither, result): def _format(comilation_unit, result):
custom_format(slither, result) custom_format(comilation_unit, result)

@ -52,7 +52,7 @@ All the calls to `get` revert, breaking Bob's smart contract execution."""
list: {'vuln', 'filename,'contract','func','#varsWritten'} list: {'vuln', 'filename,'contract','func','#varsWritten'}
""" """
results = [] results = []
if self.slither.solc_version and self.slither.solc_version >= "0.5.0": if self.compilation_unit.solc_version and self.compilation_unit.solc_version >= "0.5.0":
return results return results
for c in self.contracts: for c in self.contracts:
for f in c.functions: for f in c.functions:

@ -24,12 +24,12 @@ class ConstantPragma(AbstractDetector):
def _detect(self): def _detect(self):
results = [] results = []
pragma = self.slither.pragma_directives pragma = self.compilation_unit.pragma_directives
versions = [p.version for p in pragma if p.is_solidity_version] versions = [p.version for p in pragma if p.is_solidity_version]
versions = sorted(list(set(versions))) versions = sorted(list(set(versions)))
if len(versions) > 1: if len(versions) > 1:
info = [f"Different versions of Solidity is used in {self.filename}:\n"] info = ["Different versions of Solidity is used:\n"]
info += [f"\t- Version used: {[str(v) for v in versions]}\n"] info += [f"\t- Version used: {[str(v) for v in versions]}\n"]
for p in pragma: for p in pragma:

@ -115,7 +115,7 @@ Consider using the latest version of Solidity for testing."""
""" """
# Detect all version related pragmas and check if they are disallowed. # Detect all version related pragmas and check if they are disallowed.
results = [] results = []
pragma = self.slither.pragma_directives pragma = self.compilation_unit.pragma_directives
disallowed_pragmas = [] disallowed_pragmas = []
for p in pragma: for p in pragma:
@ -137,24 +137,19 @@ Consider using the latest version of Solidity for testing."""
results.append(json) results.append(json)
if self.slither.crytic_compile: if self.compilation_unit.solc_version not in self.ALLOWED_VERSIONS:
if self.slither.crytic_compile.compiler_version: info = [
if ( "solc-",
self.slither.crytic_compile.compiler_version.version self.compilation_unit.solc_version,
not in self.ALLOWED_VERSIONS " is not recommended for deployment\n",
): ]
info = [
"solc-",
self.slither.crytic_compile.compiler_version.version,
" is not recommended for deployment\n",
]
json = self.generate_result(info) json = self.generate_result(info)
# TODO: Once crytic-compile adds config file info, add a source mapping element pointing to # TODO: Once crytic-compile adds config file info, add a source mapping element pointing to
# the line in the config that specifies the problematic version of solc # the line in the config that specifies the problematic version of solc
results.append(json) results.append(json)
return results return results

@ -73,13 +73,13 @@ Every Ether sent to `Locked` will be lost."""
def _detect(self): def _detect(self):
results = [] results = []
for contract in self.slither.contracts_derived: for contract in self.compilation_unit.contracts_derived:
if contract.is_signature_only(): if contract.is_signature_only():
continue continue
funcs_payable = [function for function in contract.functions if function.payable] funcs_payable = [function for function in contract.functions if function.payable]
if funcs_payable: if funcs_payable:
if self.do_no_send_ether(contract): if self.do_no_send_ether(contract):
info = [f"Contract locking ether found in {self.filename}:\n"] info = ["Contract locking ether found:\n"]
info += ["\tContract ", contract, " has payable functions:\n"] info += ["\tContract ", contract, " has payable functions:\n"]
for function in funcs_payable: for function in funcs_payable:
info += ["\t - ", function, "\n"] info += ["\t - ", function, "\n"]

@ -119,14 +119,14 @@ contract Something {
# Skip interfaces without functions # Skip interfaces without functions
interfaces = [ interfaces = [
contract contract
for contract in self.slither.contracts for contract in self.compilation_unit.contracts
if contract.is_signature_only() if contract.is_signature_only()
and any(not f.is_constructor_variables for f in contract.functions) and any(not f.is_constructor_variables for f in contract.functions)
] ]
# Check derived contracts for missing interface implementations # Check derived contracts for missing interface implementations
results = [] results = []
for contract in self.slither.contracts_derived: for contract in self.compilation_unit.contracts_derived:
# Skip interfaces # Skip interfaces
if contract in interfaces: if contract in interfaces:
continue continue

@ -68,10 +68,10 @@ Attackers can trigger unexpected behaviour by calling `bug(1)`."""
"""Detect dangerous conversion to enum""" """Detect dangerous conversion to enum"""
results = [] results = []
# If solc version >= 0.4.5 then return # If solc version >= 0.4.5 then return
if not _uses_vulnerable_solc_version(self.slither.solc_version): if not _uses_vulnerable_solc_version(self.compilation_unit.solc_version):
return results return results
for c in self.slither.contracts: for c in self.compilation_unit.contracts:
ret = _detect_dangerous_enum_conversions(c) ret = _detect_dangerous_enum_conversions(c)
for node, var in ret: for node, var in ret:
func_info = [node, " has a dangerous enum conversion\n"] func_info = [node, " has a dangerous enum conversion\n"]

@ -72,10 +72,12 @@ class PublicMappingNested(AbstractDetector):
""" """
results = [] results = []
for p in self.slither.pragma_directives: for p in self.compilation_unit.pragma_directives:
if "0.5.0" in p.version and not "<0.5.0" in p.version: if "0.5.0" in p.version and not "<0.5.0" in p.version:
return [] return []
if self.slither.solc_version and self.slither.solc_version.startswith("0.5."): if self.compilation_unit.solc_version and self.compilation_unit.solc_version.startswith(
"0.5."
):
return [] return []
for contract in self.contracts: for contract in self.contracts:

@ -94,7 +94,7 @@ The constructor of `A` is called multiple times in `D` and `E`:
# Leading to several FPs # Leading to several FPs
# As the result, we might miss some TPs if the reused is due to the constructor called # As the result, we might miss some TPs if the reused is due to the constructor called
# In the contract definition # In the contract definition
if self.slither.solc_version >= "0.4.22": if self.compilation_unit.solc_version >= "0.4.22":
# Find all base constructors explicitly called from the contract definition with arguments. # Find all base constructors explicitly called from the contract definition with arguments.
_add_constructors_with_args( _add_constructors_with_args(
current_contract.explicit_base_constructor_calls, current_contract.explicit_base_constructor_calls,
@ -123,7 +123,7 @@ The constructor of `A` is called multiple times in `D` and `E`:
results = [] results = []
# The bug is not possible with solc >= 0.5.0 # The bug is not possible with solc >= 0.5.0
if not self.slither.solc_version.startswith("0.4."): if not self.compilation_unit.solc_version.startswith("0.4."):
return [] return []
# Loop for each contract # Loop for each contract

@ -128,13 +128,13 @@ contract A {
results = [] results = []
# Check if vulnerable solc versions are used # Check if vulnerable solc versions are used
if self.slither.solc_version not in vulnerable_solc_versions: if self.compilation_unit.solc_version not in vulnerable_solc_versions:
return results return results
# Check if pragma experimental ABIEncoderV2 is used # Check if pragma experimental ABIEncoderV2 is used
if not any( if not any(
(p.directive[0] == "experimental" and p.directive[1] == "ABIEncoderV2") (p.directive[0] == "experimental" and p.directive[1] == "ABIEncoderV2")
for p in self.slither.pragma_directives for p in self.compilation_unit.pragma_directives
): ):
return results return results

@ -133,7 +133,7 @@ contract A {
Detect storage signed integer array init/assignment Detect storage signed integer array init/assignment
""" """
results = [] results = []
if self.slither.solc_version not in vulnerable_solc_versions: if self.compilation_unit.solc_version not in vulnerable_solc_versions:
return results return results
for contract in self.contracts: for contract in self.contracts:
storage_signed_integer_arrays = self.detect_storage_signed_integer_arrays(contract) storage_signed_integer_arrays = self.detect_storage_signed_integer_arrays(contract)

@ -131,10 +131,10 @@ The call to `a(10)` will lead to unexpected behavior because function pointer `a
results = [] results = []
# Check if vulnerable solc versions are used # Check if vulnerable solc versions are used
if self.slither.solc_version not in vulnerable_solc_versions: if self.compilation_unit.solc_version not in vulnerable_solc_versions:
return results return results
for contract in self.slither.contracts: for contract in self.compilation_unit.contracts:
contract_info = ["Contract ", contract, " \n"] contract_info = ["Contract ", contract, " \n"]
nodes = self._detect_uninitialized_function_ptr_in_constructor(contract) nodes = self._detect_uninitialized_function_ptr_in_constructor(contract)
for node in nodes: for node in nodes:

@ -97,7 +97,7 @@ contract Token{
dict: [contract name] = set(str) events dict: [contract name] = set(str) events
""" """
results = [] results = []
for c in self.slither.contracts_derived: for c in self.compilation_unit.contracts_derived:
functions = IncorrectERC20InterfaceDetection.detect_incorrect_erc20_interface(c) functions = IncorrectERC20InterfaceDetection.detect_incorrect_erc20_interface(c)
if functions: if functions:
for function in functions: for function in functions:

@ -106,7 +106,7 @@ contract Token{
dict: [contract name] = set(str) events dict: [contract name] = set(str) events
""" """
results = [] results = []
for c in self.slither.contracts_derived: for c in self.compilation_unit.contracts_derived:
functions = IncorrectERC721InterfaceDetection.detect_incorrect_erc721_interface(c) functions = IncorrectERC721InterfaceDetection.detect_incorrect_erc721_interface(c)
if functions: if functions:
for function in functions: for function in functions:

@ -20,7 +20,7 @@ class Backdoor(AbstractDetector):
def _detect(self): def _detect(self):
results = [] results = []
for contract in self.slither.contracts_derived: for contract in self.compilation_unit.contracts_derived:
# Check if a function has 'backdoor' in its name # Check if a function has 'backdoor' in its name
for f in contract.functions: for f in contract.functions:
if "backdoor" in f.name: if "backdoor" in f.name:

@ -76,7 +76,7 @@ All unimplemented functions must be implemented on a contract that is meant to b
and not f.is_fallback and not f.is_fallback
and not f.is_constructor_variables and not f.is_constructor_variables
): ):
if self.slither.solc_version not in older_solc_versions: if self.compilation_unit.solc_version not in older_solc_versions:
# Since 0.5.1, Solidity allows creating state variable matching a function signature # Since 0.5.1, Solidity allows creating state variable matching a function signature
if not self._match_state_variable(contract, f): if not self._match_state_variable(contract, f):
unimplemented.add(f) unimplemented.add(f)
@ -92,7 +92,7 @@ All unimplemented functions must be implemented on a contract that is meant to b
list: {'vuln', 'filename,'contract','func'} list: {'vuln', 'filename,'contract','func'}
""" """
results = [] results = []
for contract in self.slither.contracts_derived: for contract in self.compilation_unit.contracts_derived:
functions = self._detect_unimplemented_function(contract) functions = self._detect_unimplemented_function(contract)
if functions: if functions:
info = [contract, " does not implement functions:\n"] info = [contract, " does not implement functions:\n"]

@ -110,7 +110,7 @@ As a result, Eve wins the game."""
def _detect(self): def _detect(self):
"""Detect bad PRNG due to the use of block.timestamp, now or blockhash (block.blockhash) as a source of randomness""" """Detect bad PRNG due to the use of block.timestamp, now or blockhash (block.blockhash) as a source of randomness"""
results = [] results = []
for c in self.slither.contracts_derived: for c in self.compilation_unit.contracts_derived:
values = detect_bad_PRNG(c) values = detect_bad_PRNG(c)
for func, nodes in values: for func, nodes in values:

@ -85,7 +85,7 @@ contract C {
# Check derived contracts for missing events # Check derived contracts for missing events
results = [] results = []
for contract in self.slither.contracts_derived: for contract in self.compilation_unit.contracts_derived:
missing_events = self._detect_missing_events(contract) missing_events = self._detect_missing_events(contract)
for (function, nodes) in missing_events: for (function, nodes) in missing_events:
info = [function, " should emit an event for: \n"] info = [function, " should emit an event for: \n"]

@ -106,7 +106,7 @@ contract C {
# Check derived contracts for missing events # Check derived contracts for missing events
results = [] results = []
for contract in self.slither.contracts_derived: for contract in self.compilation_unit.contracts_derived:
missing_events = self._detect_missing_events(contract) missing_events = self._detect_missing_events(contract)
for (function, nodes) in missing_events: for (function, nodes) in missing_events:
info = [function, " should emit an event for: \n"] info = [function, " should emit an event for: \n"]

@ -135,7 +135,7 @@ Bob calls `updateOwner` without specifying the `newOwner`, soBob loses ownership
# Check derived contracts for missing zero address validation # Check derived contracts for missing zero address validation
results = [] results = []
for contract in self.slither.contracts_derived: for contract in self.compilation_unit.contracts_derived:
missing_zero_address_validation = self._detect_missing_zero_address_validation(contract) missing_zero_address_validation = self._detect_missing_zero_address_validation(contract)
for (_, var_nodes) in missing_zero_address_validation: for (_, var_nodes) in missing_zero_address_validation:
for var, nodes in var_nodes.items(): for var, nodes in var_nodes.items():

@ -73,7 +73,7 @@ contract MyConc{
def _detect(self): def _detect(self):
"""Detect high level calls which return a value that are never used""" """Detect high level calls which return a value that are never used"""
results = [] results = []
for c in self.slither.contracts: for c in self.compilation_unit.contracts:
for f in c.functions + c.modifiers: for f in c.functions + c.modifiers:
if f.contract_declarer != c: if f.contract_declarer != c:
continue continue

@ -76,7 +76,7 @@ If you do, ensure your users are aware of the potential issues."""
def _detect(self): def _detect(self):
results = [] results = []
for contract in self.slither.contracts_derived: for contract in self.compilation_unit.contracts_derived:
vulns = _detect_token_reentrant(contract) vulns = _detect_token_reentrant(contract)
for function, nodes in vulns.items(): for function, nodes in vulns.items():
info = [function, " is an reentrancy unsafe token function:\n"] info = [function, " is an reentrancy unsafe token function:\n"]

@ -1,16 +1,17 @@
from collections import defaultdict from collections import defaultdict
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
def _find_missing_inheritance(slither): def _find_missing_inheritance(compilation_unit: SlitherCompilationUnit):
""" """
Filter contracts with missing inheritance to return only the "most base" contracts Filter contracts with missing inheritance to return only the "most base" contracts
in the inheritance tree. in the inheritance tree.
:param slither: :param slither:
:return: :return:
""" """
missings = slither.contracts_with_missing_inheritance missings = compilation_unit.contracts_with_missing_inheritance
ret = [] ret = []
for b in missings: for b in missings:
@ -44,12 +45,14 @@ As a result, the second contract cannot be analyzed.
def _detect(self): # pylint: disable=too-many-locals,too-many-branches def _detect(self): # pylint: disable=too-many-locals,too-many-branches
results = [] results = []
compilation_unit = self.compilation_unit
names_reused = self.slither.contract_name_collisions names_reused = compilation_unit.contract_name_collisions
# First show the contracts that we know are missing # First show the contracts that we know are missing
incorrectly_constructed = [ incorrectly_constructed = [
contract for contract in self.contracts if contract.is_incorrectly_constructed contract
for contract in compilation_unit.contracts
if contract.is_incorrectly_constructed
] ]
inheritance_corrupted = defaultdict(list) inheritance_corrupted = defaultdict(list)
@ -74,7 +77,7 @@ As a result, the second contract cannot be analyzed.
# Then show the contracts for which one of the father was not found # Then show the contracts for which one of the father was not found
# Here we are not able to know # Here we are not able to know
most_base_with_missing_inheritance = _find_missing_inheritance(self.slither) most_base_with_missing_inheritance = _find_missing_inheritance(compilation_unit)
for b in most_base_with_missing_inheritance: for b in most_base_with_missing_inheritance:
info = [b, " inherits from a contract for which the name is reused.\n"] info = [b, " inherits from a contract for which the name is reused.\n"]

@ -79,7 +79,7 @@ contract Token
res.add_other( res.add_other(
"rtlo-character", "rtlo-character",
(filename, idx, len(self.RTLO_CHARACTER_ENCODED)), (filename, idx, len(self.RTLO_CHARACTER_ENCODED)),
self.slither, self.compilation_unit,
) )
results.append(res) results.append(res)

@ -104,7 +104,7 @@ Otherwise, thoroughly review the contract to ensure a user-controlled variable c
""" """
results = [] results = []
# Starting from 0.6 .length is read only # Starting from 0.6 .length is read only
if self.slither.solc_version >= "0.6.": if self.compilation_unit.solc_version >= "0.6.":
return results return results
for contract in self.contracts: for contract in self.contracts:
array_length_assignments = detect_array_length_assignment(contract) array_length_assignments = detect_array_length_assignment(contract)

@ -76,7 +76,7 @@ If one of the destinations has a fallback function that reverts, `bad` will alwa
def _detect(self): def _detect(self):
"""""" """"""
results = [] results = []
for c in self.slither.contracts_derived: for c in self.compilation_unit.contracts_derived:
values = self.detect_call_in_loop(c) values = self.detect_call_in_loop(c)
for node in values: for node in values:
func = node.function func = node.function

@ -42,7 +42,7 @@ Bob calls `delegate` and delegates the execution to his malicious contract. As a
def _detect(self): def _detect(self):
results = [] results = []
for contract in self.slither.contracts_derived: for contract in self.compilation_unit.contracts_derived:
for f in contract.functions: for f in contract.functions:
# If its an upgradeable proxy, do not report protected function # If its an upgradeable proxy, do not report protected function
# As functions to upgrades the destination lead to too many FPs # As functions to upgrades the destination lead to too many FPs

@ -82,7 +82,7 @@ Incrementing `state_variable` in a loop incurs a lot of gas because of expensive
def _detect(self): def _detect(self):
"""""" """"""
results = [] results = []
for c in self.slither.contracts_derived: for c in self.compilation_unit.contracts_derived:
values = self.detect_costly_operations_in_loop(c) values = self.detect_costly_operations_in_loop(c)
for node in values: for node in values:
func = node.function func = node.function

@ -128,7 +128,7 @@ contract Crowdsale{
def _detect(self): def _detect(self):
results = [] results = []
for c in self.slither.contracts_derived: for c in self.compilation_unit.contracts_derived:
ret = self.detect_strict_equality(c) ret = self.detect_strict_equality(c)
# sort ret to get deterministic results # sort ret to get deterministic results

@ -58,7 +58,7 @@ Use:
results = [] results = []
# iterate over all contracts # iterate over all contracts
for contract in self.slither.contracts_derived: for contract in self.compilation_unit.contracts_derived:
# iterate over all functions # iterate over all functions
for f in contract.functions: for f in contract.functions:
# iterate over all the nodes # iterate over all the nodes

@ -56,7 +56,7 @@ class UnprotectedUpgradeable(AbstractDetector):
def _detect(self): def _detect(self):
results = [] results = []
for contract in self.slither.contracts_derived: for contract in self.compilation_unit.contracts_derived:
if contract.is_upgradeable: if contract.is_upgradeable:
functions_that_can_destroy = _can_be_destroyed(contract) functions_that_can_destroy = _can_be_destroyed(contract)
if functions_that_can_destroy: if functions_that_can_destroy:

@ -1,7 +1,7 @@
""" """
Module detecting state variables that could be declared as constant Module detecting state variables that could be declared as constant
""" """
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.core.solidity_types.elementary_type import ElementaryType from slither.core.solidity_types.elementary_type import ElementaryType
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.visitors.expression.export_values import ExportValues from slither.visitors.expression.export_values import ExportValues
@ -70,13 +70,13 @@ class ConstCandidateStateVars(AbstractDetector):
"""Detect state variables that could be const""" """Detect state variables that could be const"""
results = [] results = []
all_variables = [c.state_variables for c in self.slither.contracts] all_variables = [c.state_variables for c in self.compilation_unit.contracts]
all_variables = {item for sublist in all_variables for item in sublist} all_variables = {item for sublist in all_variables for item in sublist}
all_non_constant_elementary_variables = { all_non_constant_elementary_variables = {
v for v in all_variables if self._valid_candidate(v) v for v in all_variables if self._valid_candidate(v)
} }
all_functions = [c.all_functions_called for c in self.slither.contracts] all_functions = [c.all_functions_called for c in self.compilation_unit.contracts]
all_functions = list({item for sublist in all_functions for item in sublist}) all_functions = list({item for sublist in all_functions for item in sublist})
all_variables_written = [ all_variables_written = [
@ -101,5 +101,5 @@ class ConstCandidateStateVars(AbstractDetector):
return results return results
@staticmethod @staticmethod
def _format(slither, result): def _format(compilation_unit: SlitherCompilationUnit, result):
custom_format(slither, result) custom_format(compilation_unit, result)

@ -83,7 +83,7 @@ Bob calls `transfer`. As a result, all Ether is sent to the address `0x0` and is
self.results = [] self.results = []
self.visited_all_paths = {} self.visited_all_paths = {}
for contract in self.slither.contracts: for contract in self.compilation_unit.contracts:
for function in contract.functions: for function in contract.functions:
if function.is_implemented and function.contract_declarer == contract: if function.is_implemented and function.contract_declarer == contract:
if function.contains_assembly: if function.contains_assembly:

@ -74,7 +74,7 @@ Initialize all the variables. If a variable is meant to be initialized to zero,
return self.__variables_written_in_proxy return self.__variables_written_in_proxy
variables_written_in_proxy = [] variables_written_in_proxy = []
for c in self.slither.contracts: for c in self.compilation_unit.contracts:
if c.is_upgradeable_proxy: if c.is_upgradeable_proxy:
variables_written_in_proxy += self._written_variables(c) variables_written_in_proxy += self._written_variables(c)
@ -122,7 +122,7 @@ Initialize all the variables. If a variable is meant to be initialized to zero,
dict: [contract name] = set(state variable uninitialized) dict: [contract name] = set(state variable uninitialized)
""" """
results = [] results = []
for c in self.slither.contracts_derived: for c in self.compilation_unit.contracts_derived:
ret = self._detect_uninitialized(c) ret = self._detect_uninitialized(c)
for variable, functions in ret: for variable, functions in ret:

@ -91,7 +91,7 @@ Bob calls `func`. As a result, `owner` is overridden to `0`.
self.results = [] self.results = []
self.visited_all_paths = {} self.visited_all_paths = {}
for contract in self.slither.contracts: for contract in self.compilation_unit.contracts:
for function in contract.functions: for function in contract.functions:
if function.is_implemented: if function.is_implemented:
uninitialized_storage_variables = [ uninitialized_storage_variables = [

@ -1,7 +1,7 @@
""" """
Module detecting unused state variables Module detecting unused state variables
""" """
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.core.solidity_types import ArrayType from slither.core.solidity_types import ArrayType
from slither.visitors.expression.export_values import ExportValues from slither.visitors.expression.export_values import ExportValues
@ -57,7 +57,7 @@ class UnusedStateVars(AbstractDetector):
def _detect(self): def _detect(self):
"""Detect unused state variables""" """Detect unused state variables"""
results = [] results = []
for c in self.slither.contracts_derived: for c in self.compilation_unit.contracts_derived:
unusedVars = detect_unused(c) unusedVars = detect_unused(c)
if unusedVars: if unusedVars:
for var in unusedVars: for var in unusedVars:
@ -68,5 +68,5 @@ class UnusedStateVars(AbstractDetector):
return results return results
@staticmethod @staticmethod
def _format(slither, result): def _format(compilation_unit: SlitherCompilationUnit, result):
custom_format(slither, result) custom_format(compilation_unit, result)

@ -1,15 +1,17 @@
import re import re
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.formatters.exceptions import FormatError from slither.formatters.exceptions import FormatError
from slither.formatters.utils.patches import create_patch from slither.formatters.utils.patches import create_patch
def custom_format(slither, result): def custom_format(comilation_unit: SlitherCompilationUnit, result):
elements = result["elements"] elements = result["elements"]
for element in elements: for element in elements:
if element["type"] != "function": if element["type"] != "function":
# Skip variable elements # Skip variable elements
continue continue
target_contract = slither.get_contract_from_name( target_contract = comilation_unit.get_contract_from_name(
element["type_specific_fields"]["parent"]["name"] element["type_specific_fields"]["parent"]["name"]
) )
if target_contract: if target_contract:
@ -18,7 +20,7 @@ def custom_format(slither, result):
) )
if function: if function:
_patch( _patch(
slither, comilation_unit,
result, result,
element["source_mapping"]["filename_absolute"], element["source_mapping"]["filename_absolute"],
int( int(
@ -29,8 +31,10 @@ def custom_format(slither, result):
) )
def _patch(slither, result, in_file, modify_loc_start, modify_loc_end): def _patch(
in_file_str = slither.source_code[in_file].encode("utf8") comilation_unit: SlitherCompilationUnit, result, in_file, modify_loc_start, modify_loc_end
):
in_file_str = comilation_unit.core.source_code[in_file].encode("utf8")
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end]
# Find the keywords view|pure|constant and remove them # Find the keywords view|pure|constant and remove them
m = re.search("(view|pure|constant)", old_str_of_interest.decode("utf-8")) m = re.search("(view|pure|constant)", old_str_of_interest.decode("utf-8"))

@ -1,11 +1,13 @@
import re import re
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.formatters.utils.patches import create_patch from slither.formatters.utils.patches import create_patch
def custom_format(slither, result): def custom_format(comilation_unit: SlitherCompilationUnit, result):
elements = result["elements"] elements = result["elements"]
for element in elements: for element in elements:
target_contract = slither.get_contract_from_name( target_contract = comilation_unit.get_contract_from_name(
element["type_specific_fields"]["parent"]["name"] element["type_specific_fields"]["parent"]["name"]
) )
if target_contract: if target_contract:
@ -14,7 +16,7 @@ def custom_format(slither, result):
) )
if function: if function:
_patch( _patch(
slither, comilation_unit,
result, result,
element["source_mapping"]["filename_absolute"], element["source_mapping"]["filename_absolute"],
int(function.parameters_src().source_mapping["start"]), int(function.parameters_src().source_mapping["start"]),
@ -22,8 +24,10 @@ def custom_format(slither, result):
) )
def _patch(slither, result, in_file, modify_loc_start, modify_loc_end): def _patch(
in_file_str = slither.source_code[in_file].encode("utf8") comilation_unit: SlitherCompilationUnit, result, in_file, modify_loc_start, modify_loc_end
):
in_file_str = comilation_unit.core.source_code[in_file].encode("utf8")
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end]
# Search for 'public' keyword which is in-between the function name and modifier name (if present) # Search for 'public' keyword which is in-between the function name and modifier name (if present)
# regex: 'public' could have spaces around or be at the end of the line # regex: 'public' could have spaces around or be at the end of the line

@ -1,5 +1,7 @@
import re import re
import logging import logging
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.slithir.operations import ( from slither.slithir.operations import (
Send, Send,
Transfer, Transfer,
@ -24,7 +26,7 @@ logger = logging.getLogger("Slither.Format")
# pylint: disable=anomalous-backslash-in-string # pylint: disable=anomalous-backslash-in-string
def custom_format(slither, result): def custom_format(compilation_unit: SlitherCompilationUnit, result):
elements = result["elements"] elements = result["elements"]
for element in elements: for element in elements:
target = element["additional_fields"]["target"] target = element["additional_fields"]["target"]
@ -38,7 +40,7 @@ def custom_format(slither, result):
) )
continue continue
_patch(slither, result, element, target) _patch(compilation_unit, result, element, target)
# endregion # endregion
@ -157,7 +159,7 @@ def _convert_CapWords(original_name, slither):
return name return name
def _convert_mixedCase(original_name, slither): def _convert_mixedCase(original_name, compilation_unit: SlitherCompilationUnit):
name = original_name name = original_name
if isinstance(name, bytes): if isinstance(name, bytes):
name = name.decode("utf8") name = name.decode("utf8")
@ -168,15 +170,15 @@ def _convert_mixedCase(original_name, slither):
name = name[0:offset] + name[offset + 1].upper() + name[offset + 2 :] name = name[0:offset] + name[offset + 1].upper() + name[offset + 2 :]
name = name[0].lower() + name[1:] name = name[0].lower() + name[1:]
if _name_already_use(slither, name): if _name_already_use(compilation_unit, name):
raise FormatImpossible(f"{original_name} cannot be converted to {name} (already used)") raise FormatImpossible(f"{original_name} cannot be converted to {name} (already used)")
if name in SOLIDITY_KEYWORDS: if name in SOLIDITY_KEYWORDS:
raise FormatImpossible(f"{original_name} cannot be converted to {name} (Solidity keyword)") raise FormatImpossible(f"{original_name} cannot be converted to {name} (Solidity keyword)")
return name return name
def _convert_UPPER_CASE_WITH_UNDERSCORES(name, slither): def _convert_UPPER_CASE_WITH_UNDERSCORES(name, compilation_unit: SlitherCompilationUnit):
if _name_already_use(slither, name.upper()): if _name_already_use(compilation_unit, name.upper()):
raise FormatImpossible(f"{name} cannot be converted to {name.upper()} (already used)") raise FormatImpossible(f"{name} cannot be converted to {name.upper()} (already used)")
if name.upper() in SOLIDITY_KEYWORDS: if name.upper() in SOLIDITY_KEYWORDS:
raise FormatImpossible(f"{name} cannot be converted to {name.upper()} (Solidity keyword)") raise FormatImpossible(f"{name} cannot be converted to {name.upper()} (Solidity keyword)")
@ -198,9 +200,9 @@ conventions = {
################################################################################### ###################################################################################
def _get_from_contract(slither, element, name, getter): def _get_from_contract(compilation_unit: SlitherCompilationUnit, element, name, getter):
contract_name = element["type_specific_fields"]["parent"]["name"] contract_name = element["type_specific_fields"]["parent"]["name"]
contract = slither.get_contract_from_name(contract_name) contract = compilation_unit.get_contract_from_name(contract_name)
return getattr(contract, getter)(name) return getattr(contract, getter)(name)
@ -212,27 +214,33 @@ def _get_from_contract(slither, element, name, getter):
################################################################################### ###################################################################################
def _patch(slither, result, element, _target): def _patch(compilation_unit: SlitherCompilationUnit, result, element, _target):
if _target == "contract": if _target == "contract":
target = slither.get_contract_from_name(element["name"]) target = compilation_unit.get_contract_from_name(element["name"])
elif _target == "structure": elif _target == "structure":
target = _get_from_contract(slither, element, element["name"], "get_structure_from_name") target = _get_from_contract(
compilation_unit, element, element["name"], "get_structure_from_name"
)
elif _target == "event": elif _target == "event":
target = _get_from_contract(slither, element, element["name"], "get_event_from_name") target = _get_from_contract(
compilation_unit, element, element["name"], "get_event_from_name"
)
elif _target == "function": elif _target == "function":
# Avoid constructor (FP?) # Avoid constructor (FP?)
if element["name"] != element["type_specific_fields"]["parent"]["name"]: if element["name"] != element["type_specific_fields"]["parent"]["name"]:
function_sig = element["type_specific_fields"]["signature"] function_sig = element["type_specific_fields"]["signature"]
target = _get_from_contract( target = _get_from_contract(
slither, element, function_sig, "get_function_from_signature" compilation_unit, element, function_sig, "get_function_from_signature"
) )
elif _target == "modifier": elif _target == "modifier":
modifier_sig = element["type_specific_fields"]["signature"] modifier_sig = element["type_specific_fields"]["signature"]
target = _get_from_contract(slither, element, modifier_sig, "get_modifier_from_signature") target = _get_from_contract(
compilation_unit, element, modifier_sig, "get_modifier_from_signature"
)
elif _target == "parameter": elif _target == "parameter":
contract_name = element["type_specific_fields"]["parent"]["type_specific_fields"]["parent"][ contract_name = element["type_specific_fields"]["parent"]["type_specific_fields"]["parent"][
@ -242,7 +250,7 @@ def _patch(slither, result, element, _target):
"signature" "signature"
] ]
param_name = element["name"] param_name = element["name"]
contract = slither.get_contract_from_name(contract_name) contract = compilation_unit.get_contract_from_name(contract_name)
function = contract.get_function_from_signature(function_sig) function = contract.get_function_from_signature(function_sig)
target = function.get_local_variable_from_name(param_name) target = function.get_local_variable_from_name(param_name)
@ -256,24 +264,26 @@ def _patch(slither, result, element, _target):
"signature" "signature"
] ]
var_name = element["name"] var_name = element["name"]
contract = slither.get_contract_from_name(contract_name) contract = compilation_unit.get_contract_from_name(contract_name)
function = contract.get_function_from_signature(function_sig) function = contract.get_function_from_signature(function_sig)
target = function.get_local_variable_from_name(var_name) target = function.get_local_variable_from_name(var_name)
# State variable # State variable
else: else:
target = _get_from_contract( target = _get_from_contract(
slither, element, element["name"], "get_state_variable_from_name" compilation_unit, element, element["name"], "get_state_variable_from_name"
) )
elif _target == "enum": elif _target == "enum":
target = _get_from_contract( target = _get_from_contract(
slither, element, element["name"], "get_enum_from_canonical_name" compilation_unit, element, element["name"], "get_enum_from_canonical_name"
) )
else: else:
raise FormatError("Unknown naming convention! " + _target) raise FormatError("Unknown naming convention! " + _target)
_explore(slither, result, target, conventions[element["additional_fields"]["convention"]]) _explore(
compilation_unit, result, target, conventions[element["additional_fields"]["convention"]]
)
# endregion # endregion
@ -661,9 +671,9 @@ def _explore_contract(slither, contract, result, target, convert):
create_patch(result, filename_source_code, loc_start, loc_end, old_str, new_str) create_patch(result, filename_source_code, loc_start, loc_end, old_str, new_str)
def _explore(slither, result, target, convert): def _explore(compilation_unit: SlitherCompilationUnit, result, target, convert):
for contract in slither.contracts_derived: for contract in compilation_unit.contracts_derived:
_explore_contract(slither, contract, result, target, convert) _explore_contract(compilation_unit, contract, result, target, convert)
# endregion # endregion

@ -1,21 +1,23 @@
import re import re
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.formatters.exceptions import FormatError, FormatImpossible from slither.formatters.exceptions import FormatError, FormatImpossible
from slither.formatters.utils.patches import create_patch from slither.formatters.utils.patches import create_patch
def custom_format(slither, result): def custom_format(compilation_unit: SlitherCompilationUnit, result):
elements = result["elements"] elements = result["elements"]
for element in elements: for element in elements:
# TODO: decide if this should be changed in the constant detector # TODO: decide if this should be changed in the constant detector
contract_name = element["type_specific_fields"]["parent"]["name"] contract_name = element["type_specific_fields"]["parent"]["name"]
contract = slither.get_contract_from_name(contract_name) contract = compilation_unit.get_contract_from_name(contract_name)
var = contract.get_state_variable_from_name(element["name"]) var = contract.get_state_variable_from_name(element["name"])
if not var.expression: if not var.expression:
raise FormatImpossible(f"{var.name} is uninitialized and cannot become constant.") raise FormatImpossible(f"{var.name} is uninitialized and cannot become constant.")
_patch( _patch(
slither, compilation_unit,
result, result,
element["source_mapping"]["filename_absolute"], element["source_mapping"]["filename_absolute"],
element["name"], element["name"],
@ -26,9 +28,15 @@ def custom_format(slither, result):
def _patch( # pylint: disable=too-many-arguments def _patch( # pylint: disable=too-many-arguments
slither, result, in_file, match_text, replace_text, modify_loc_start, modify_loc_end compilation_unit: SlitherCompilationUnit,
result,
in_file,
match_text,
replace_text,
modify_loc_start,
modify_loc_end,
): ):
in_file_str = slither.source_code[in_file].encode("utf8") in_file_str = compilation_unit.core.source_code[in_file].encode("utf8")
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end]
# Add keyword `constant` before the variable name # Add keyword `constant` before the variable name
(new_str_of_interest, num_repl) = re.subn( (new_str_of_interest, num_repl) = re.subn(

@ -1,20 +1,21 @@
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.formatters.utils.patches import create_patch from slither.formatters.utils.patches import create_patch
def custom_format(slither, result): def custom_format(compilation_unit: SlitherCompilationUnit, result):
elements = result["elements"] elements = result["elements"]
for element in elements: for element in elements:
if element["type"] == "variable": if element["type"] == "variable":
_patch( _patch(
slither, compilation_unit,
result, result,
element["source_mapping"]["filename_absolute"], element["source_mapping"]["filename_absolute"],
element["source_mapping"]["start"], element["source_mapping"]["start"],
) )
def _patch(slither, result, in_file, modify_loc_start): def _patch(compilation_unit: SlitherCompilationUnit, result, in_file, modify_loc_start):
in_file_str = slither.source_code[in_file].encode("utf8") in_file_str = compilation_unit.core.source_code[in_file].encode("utf8")
old_str_of_interest = in_file_str[modify_loc_start:] old_str_of_interest = in_file_str[modify_loc_start:]
old_str = ( old_str = (
old_str_of_interest.decode("utf-8").partition(";")[0] old_str_of_interest.decode("utf-8").partition(";")[0]

@ -226,8 +226,17 @@ class PrinterCallGraph(AbstractPrinter):
results = [] results = []
with open(all_contracts_filename, "w", encoding="utf8") as f: with open(all_contracts_filename, "w", encoding="utf8") as f:
info += f"Call Graph: {all_contracts_filename}\n" info += f"Call Graph: {all_contracts_filename}\n"
# Avoid dupplicate funcitons due to different compilation unit
all_functionss = [
compilation_unit.functions for compilation_unit in self.slither.compilation_units
]
all_functions = [item for sublist in all_functionss for item in sublist]
all_functions_as_dict = {
function.canonical_name: function for function in all_functions
}
content = "\n".join( content = "\n".join(
["strict digraph {"] + [_process_functions(self.slither.functions)] + ["}"] ["strict digraph {"] + [_process_functions(all_functions_as_dict.values())] + ["}"]
) )
f.write(content) f.write(content)
results.append((all_contracts_filename, content)) results.append((all_contracts_filename, content))

@ -79,9 +79,8 @@ def _is_constant(f: Function) -> bool: # pylint: disable=too-many-branches
:return: :return:
""" """
if f.view or f.pure: if f.view or f.pure:
if f.contract.slither.crytic_compile and f.contract.slither.crytic_compile.compiler_version: if not f.contract.compilation_unit.solc_version.startswith("0.4"):
if not f.contract.slither.crytic_compile.compiler_version.version.startswith("0.4"): return True
return True
if f.payable: if f.payable:
return False return False
if not f.is_implemented: if not f.is_implemented:
@ -103,12 +102,8 @@ def _is_constant(f: Function) -> bool: # pylint: disable=too-many-branches
if isinstance(ir, HighLevelCall): if isinstance(ir, HighLevelCall):
if isinstance(ir.function, Variable) or ir.function.view or ir.function.pure: if isinstance(ir.function, Variable) or ir.function.view or ir.function.pure:
# External call to constant functions are ensured to be constant only for solidity >= 0.5 # External call to constant functions are ensured to be constant only for solidity >= 0.5
if ( if f.contract.compilation_unit.solc_version.startswith("0.4"):
f.contract.slither.crytic_compile return False
and f.contract.slither.crytic_compile.compiler_version
):
if f.contract.slither.crytic_compile.compiler_version.version.startswith("0.4"):
return False
else: else:
return False return False
if isinstance(ir, InternalCall): if isinstance(ir, InternalCall):

@ -21,8 +21,14 @@ def _extract_evm_info(slither):
CFG = load_evm_cfg_builder() CFG = load_evm_cfg_builder()
for contract in slither.contracts_derived: for contract in slither.contracts_derived:
contract_bytecode_runtime = slither.crytic_compile.bytecode_runtime(contract.name) contract_bytecode_runtime = (
contract_srcmap_runtime = slither.crytic_compile.srcmap_runtime(contract.name) contract.compilation_unit.crytic_compile_compilation_unit.bytecode_runtime(
contract.name
)
)
contract_srcmap_runtime = (
contract.compilation_unit.crytic_compile_compilation_unit.srcmap_runtime(contract.name)
)
cfg = CFG(contract_bytecode_runtime) cfg = CFG(contract_bytecode_runtime)
evm_info["cfg", contract.name] = cfg evm_info["cfg", contract.name] = cfg
evm_info["mapping", contract.name] = generate_source_to_evm_ins_mapping( evm_info["mapping", contract.name] = generate_source_to_evm_ins_mapping(
@ -32,8 +38,12 @@ def _extract_evm_info(slither):
contract.source_mapping["filename_absolute"], contract.source_mapping["filename_absolute"],
) )
contract_bytecode_init = slither.crytic_compile.bytecode_init(contract.name) contract_bytecode_init = (
contract_srcmap_init = slither.crytic_compile.srcmap_init(contract.name) contract.compilation_unit.crytic_compile_compilation_unit.bytecode_init(contract.name)
)
contract_srcmap_init = (
contract.compilation_unit.crytic_compile_compilation_unit.srcmap_init(contract.name)
)
cfg_init = CFG(contract_bytecode_init) cfg_init = CFG(contract_bytecode_init)
evm_info["cfg_init", contract.name] = cfg_init evm_info["cfg_init", contract.name] = cfg_init
@ -61,7 +71,6 @@ class PrinterEVM(AbstractPrinter):
""" """
txt = "" txt = ""
if not self.slither.crytic_compile: if not self.slither.crytic_compile:
txt = "The EVM printer requires to compile with crytic-compile" txt = "The EVM printer requires to compile with crytic-compile"
self.info(red(txt)) self.info(red(txt))

@ -238,13 +238,14 @@ class PrinterHumanSummary(AbstractPrinter):
use_abi_encoder = False use_abi_encoder = False
for pragma in self.slither.pragma_directives: for compilation_unit in self.slither.compilation_units:
if ( for pragma in compilation_unit.pragma_directives:
pragma.source_mapping["filename_absolute"] if (
== contract.source_mapping["filename_absolute"] pragma.source_mapping["filename_absolute"]
): == contract.source_mapping["filename_absolute"]
if pragma.is_abi_encoder_v2: ):
use_abi_encoder = True if pragma.is_abi_encoder_v2:
use_abi_encoder = True
for function in contract.functions: for function in contract.functions:
if function.payable: if function.payable:

@ -34,21 +34,22 @@ class PrinterSlithIR(AbstractPrinter):
""" """
txt = "" txt = ""
for contract in self.contracts: for compilation_unit in self.slither.compilation_units:
if contract.is_top_level: for contract in compilation_unit.contracts:
continue if contract.is_top_level:
txt += "Contract {}\n".format(contract.name) continue
for function in contract.functions: txt += "Contract {}\n".format(contract.name)
txt += f'\tFunction {function.canonical_name} {"" if function.is_shadowed else "(*)"}\n' for function in contract.functions:
txt += f'\tFunction {function.canonical_name} {"" if function.is_shadowed else "(*)"}\n'
txt += _print_function(function)
for modifier in contract.modifiers:
txt += "\tModifier {}\n".format(modifier.canonical_name)
txt += _print_function(modifier)
if compilation_unit.functions_top_level:
txt += "Top level functions"
for function in compilation_unit.functions_top_level:
txt += f"\tFunction {function.canonical_name}\n"
txt += _print_function(function) txt += _print_function(function)
for modifier in contract.modifiers:
txt += "\tModifier {}\n".format(modifier.canonical_name)
txt += _print_function(modifier)
if self.slither.functions_top_level:
txt += "Top level functions"
for function in self.slither.functions_top_level:
txt += f"\tFunction {function.canonical_name}\n"
txt += _print_function(function)
self.info(txt) self.info(txt)
res = self.generate_output(txt) res = self.generate_output(txt)
return res return res

@ -1,14 +1,15 @@
import logging import logging
import os from typing import Union, List
from crytic_compile import CryticCompile, InvalidCompilation from crytic_compile import CryticCompile, InvalidCompilation
# pylint: disable= no-name-in-module # pylint: disable= no-name-in-module
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification from slither.core.compilation_unit import SlitherCompilationUnit
from slither.printers.abstract_printer import AbstractPrinter
from slither.core.slither_core import SlitherCore from slither.core.slither_core import SlitherCore
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.exceptions import SlitherError from slither.exceptions import SlitherError
from slither.solc_parsing.slitherSolc import SlitherSolc from slither.printers.abstract_printer import AbstractPrinter
from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc
logger = logging.getLogger("Slither") logger = logging.getLogger("Slither")
logging.basicConfig() logging.basicConfig()
@ -31,10 +32,10 @@ def _check_common_things(thing_name, cls, base_cls, instances_list):
class Slither(SlitherCore): # pylint: disable=too-many-instance-attributes class Slither(SlitherCore): # pylint: disable=too-many-instance-attributes
def __init__(self, target, **kwargs): def __init__(self, target: Union[str, CryticCompile], **kwargs):
""" """
Args: Args:
target (str | list(json) | CryticCompile) target (str | CryticCompile)
Keyword Args: Keyword Args:
solc (str): solc binary location (default 'solc') solc (str): solc binary location (default 'solc')
disable_solc_warnings (bool): True to disable solc warnings (default false) disable_solc_warnings (bool): True to disable solc warnings (default false)
@ -56,30 +57,28 @@ class Slither(SlitherCore): # pylint: disable=too-many-instance-attributes
""" """
super().__init__() super().__init__()
self._parser: SlitherSolc # This could be another parser, like SlitherVyper, interface needs to be determined
self._disallow_partial: bool = kwargs.get("disallow_partial", False) self._disallow_partial: bool = kwargs.get("disallow_partial", False)
self._skip_assembly: bool = kwargs.get("skip_assembly", False) self._skip_assembly: bool = kwargs.get("skip_assembly", False)
self._show_ignored_findings: bool = kwargs.get("show_ignored_findings", False) self._show_ignored_findings: bool = kwargs.get("show_ignored_findings", False)
# list of files provided (see --splitted option) self._parsers: List[SlitherCompilationUnitSolc] = []
if isinstance(target, list): try:
self._init_from_list(target) if isinstance(target, CryticCompile):
elif isinstance(target, str) and target.endswith(".json"): crytic_compile = target
self._init_from_raw_json(target) else:
else: crytic_compile = CryticCompile(target, **kwargs)
self._parser = SlitherSolc("", self) self._crytic_compile = crytic_compile
try: except InvalidCompilation as e:
if isinstance(target, CryticCompile): # pylint: disable=raise-missing-from
crytic_compile = target raise SlitherError(f"Invalid compilation: \n{str(e)}")
else: for compilation_unit in crytic_compile.compilation_units.values():
crytic_compile = CryticCompile(target, **kwargs) compilation_unit_slither = SlitherCompilationUnit(self, compilation_unit)
self._crytic_compile = crytic_compile self._compilation_units.append(compilation_unit_slither)
except InvalidCompilation as e: parser = SlitherCompilationUnitSolc(compilation_unit_slither)
# pylint: disable=raise-missing-from self._parsers.append(parser)
raise SlitherError(f"Invalid compilation: \n{str(e)}") for path, ast in compilation_unit.asts.items():
for path, ast in crytic_compile.asts.items(): parser.parse_top_level_from_loaded_json(ast, path)
self._parser.parse_top_level_from_loaded_json(ast, path)
self.add_source_code(path) self.add_source_code(path)
if kwargs.get("generate_patches", False): if kwargs.get("generate_patches", False):
@ -99,38 +98,40 @@ class Slither(SlitherCore): # pylint: disable=too-many-instance-attributes
triage_mode = kwargs.get("triage_mode", False) triage_mode = kwargs.get("triage_mode", False)
self._triage_mode = triage_mode self._triage_mode = triage_mode
self._parser.parse_contracts() for parser in self._parsers:
parser.parse_contracts()
# skip_analyze is only used for testing # skip_analyze is only used for testing
if not kwargs.get("skip_analyze", False): if not kwargs.get("skip_analyze", False):
self._parser.analyze_contracts() for parser in self._parsers:
parser.analyze_contracts()
def _init_from_raw_json(self, filename):
if not os.path.isfile(filename): # def _init_from_raw_json(self, filename):
raise SlitherError( # if not os.path.isfile(filename):
"{} does not exist (are you in the correct directory?)".format(filename) # raise SlitherError(
) # "{} does not exist (are you in the correct directory?)".format(filename)
assert filename.endswith("json") # )
with open(filename, encoding="utf8") as astFile: # assert filename.endswith("json")
stdout = astFile.read() # with open(filename, encoding="utf8") as astFile:
if not stdout: # stdout = astFile.read()
to_log = f"Empty AST file: {filename}" # if not stdout:
raise SlitherError(to_log) # to_log = f"Empty AST file: {filename}"
contracts_json = stdout.split("\n=") # raise SlitherError(to_log)
# contracts_json = stdout.split("\n=")
self._parser = SlitherSolc(filename, self) #
# self._parser = SlitherCompilationUnitSolc(filename, self)
for c in contracts_json: #
self._parser.parse_top_level_from_json(c) # for c in contracts_json:
# self._parser.parse_top_level_from_json(c)
def _init_from_list(self, contract):
self._parser = SlitherSolc("", self) # def _init_from_list(self, contract):
for c in contract: # self._parser = SlitherCompilationUnitSolc("", self)
if "absolutePath" in c: # for c in contract:
path = c["absolutePath"] # if "absolutePath" in c:
else: # path = c["absolutePath"]
path = c["attributes"]["absolutePath"] # else:
self._parser.parse_top_level_from_loaded_json(c, path) # path = c["attributes"]["absolutePath"]
# self._parser.parse_top_level_from_loaded_json(c, path)
@property @property
def detectors(self): def detectors(self):
@ -162,8 +163,9 @@ class Slither(SlitherCore): # pylint: disable=too-many-instance-attributes
""" """
_check_common_things("detector", detector_class, AbstractDetector, self._detectors) _check_common_things("detector", detector_class, AbstractDetector, self._detectors)
instance = detector_class(self, logger_detector) for compilation_unit in self.compilation_units:
self._detectors.append(instance) instance = detector_class(compilation_unit, self, logger_detector)
self._detectors.append(instance)
def register_printer(self, printer_class): def register_printer(self, printer_class):
""" """
@ -181,6 +183,7 @@ class Slither(SlitherCore): # pylint: disable=too-many-instance-attributes
self.load_previous_results() self.load_previous_results()
results = [d.detect() for d in self._detectors] results = [d.detect() for d in self._detectors]
self.write_results_to_hide() self.write_results_to_hide()
return results return results

@ -82,6 +82,7 @@ from slither.visitors.slithir.expression_to_slithir import ExpressionToSlithIR
if TYPE_CHECKING: if TYPE_CHECKING:
from slither.core.cfg.node import Node from slither.core.cfg.node import Node
from slither.core.compilation_unit import SlitherCompilationUnit
logger = logging.getLogger("ConvertToIR") logger = logging.getLogger("ConvertToIR")
@ -434,31 +435,19 @@ def propagate_type_and_convert_call(result, node):
return result return result
def _convert_type_contract(ir, slither): def _convert_type_contract(ir, compilation_unit: "SlitherCompilationUnit"):
assert isinstance(ir.variable_left.type, TypeInformation) assert isinstance(ir.variable_left.type, TypeInformation)
contract = ir.variable_left.type.type contract = ir.variable_left.type.type
if ir.variable_right == "creationCode": if ir.variable_right == "creationCode":
if slither.crytic_compile: bytecode = compilation_unit.crytic_compile_compilation_unit.bytecode_init(contract.name)
bytecode = slither.crytic_compile.bytecode_init(contract.name)
else:
logger.info(
"The codebase uses type(x).creationCode, but crytic-compile was not used. As a result, the bytecode cannot be found"
)
bytecode = "MISSING_BYTECODE"
assignment = Assignment(ir.lvalue, Constant(str(bytecode)), ElementaryType("bytes")) assignment = Assignment(ir.lvalue, Constant(str(bytecode)), ElementaryType("bytes"))
assignment.set_expression(ir.expression) assignment.set_expression(ir.expression)
assignment.set_node(ir.node) assignment.set_node(ir.node)
assignment.lvalue.set_type(ElementaryType("bytes")) assignment.lvalue.set_type(ElementaryType("bytes"))
return assignment return assignment
if ir.variable_right == "runtimeCode": if ir.variable_right == "runtimeCode":
if slither.crytic_compile: bytecode = compilation_unit.crytic_compile_compilation_unit.bytecode_runtime(contract.name)
bytecode = slither.crytic_compile.bytecode_runtime(contract.name)
else:
logger.info(
"The codebase uses type(x).runtimeCode, but crytic-compile was not used. As a result, the bytecode cannot be found"
)
bytecode = "MISSING_BYTECODE"
assignment = Assignment(ir.lvalue, Constant(str(bytecode)), ElementaryType("bytes")) assignment = Assignment(ir.lvalue, Constant(str(bytecode)), ElementaryType("bytes"))
assignment.set_expression(ir.expression) assignment.set_expression(ir.expression)
assignment.set_node(ir.node) assignment.set_node(ir.node)
@ -530,7 +519,7 @@ def propagate_types(ir, node: "Node"): # pylint: disable=too-many-locals
# UserdefinedType # UserdefinedType
t_type = t.type t_type = t.type
if isinstance(t_type, Contract): if isinstance(t_type, Contract):
contract = node.slither.get_contract_from_name(t_type.name) contract = node.compilation_unit.get_contract_from_name(t_type.name)
return convert_type_of_high_and_internal_level_call(ir, contract) return convert_type_of_high_and_internal_level_call(ir, contract)
# Convert HighLevelCall to LowLevelCall # Convert HighLevelCall to LowLevelCall
@ -634,7 +623,7 @@ def propagate_types(ir, node: "Node"): # pylint: disable=too-many-locals
if isinstance(ir.variable_left, TemporaryVariable) and isinstance( if isinstance(ir.variable_left, TemporaryVariable) and isinstance(
ir.variable_left.type, TypeInformation ir.variable_left.type, TypeInformation
): ):
return _convert_type_contract(ir, node.function.slither) return _convert_type_contract(ir, node.function.compilation_unit)
left = ir.variable_left left = ir.variable_left
t = None t = None
ir_func = ir.function ir_func = ir.function
@ -699,7 +688,7 @@ def propagate_types(ir, node: "Node"): # pylint: disable=too-many-locals
elif isinstance(ir, NewArray): elif isinstance(ir, NewArray):
ir.lvalue.set_type(ir.array_type) ir.lvalue.set_type(ir.array_type)
elif isinstance(ir, NewContract): elif isinstance(ir, NewContract):
contract = node.slither.get_contract_from_name(ir.contract_name) contract = node.compilation_unit.get_contract_from_name(ir.contract_name)
ir.lvalue.set_type(UserDefinedType(contract)) ir.lvalue.set_type(UserDefinedType(contract))
elif isinstance(ir, NewElementaryType): elif isinstance(ir, NewElementaryType):
ir.lvalue.set_type(ir.type) ir.lvalue.set_type(ir.type)
@ -1006,7 +995,7 @@ def convert_to_low_level(ir):
new_ir.call_gas = ir.call_gas new_ir.call_gas = ir.call_gas
new_ir.call_value = ir.call_value new_ir.call_value = ir.call_value
new_ir.arguments = ir.arguments new_ir.arguments = ir.arguments
if ir.slither.solc_version >= "0.5": if ir.node.compilation_unit.solc_version >= "0.5":
new_ir.lvalue.set_type([ElementaryType("bool"), ElementaryType("bytes")]) new_ir.lvalue.set_type([ElementaryType("bool"), ElementaryType("bytes")])
else: else:
new_ir.lvalue.set_type(ElementaryType("bool")) new_ir.lvalue.set_type(ElementaryType("bool"))
@ -1208,7 +1197,7 @@ def convert_to_pop(ir, node):
def look_for_library(contract, ir, using_for, t): def look_for_library(contract, ir, using_for, t):
for destination in using_for[t]: for destination in using_for[t]:
lib_contract = contract.slither.get_contract_from_name(str(destination)) lib_contract = contract.compilation_unit.get_contract_from_name(str(destination))
if lib_contract: if lib_contract:
lib_call = LibraryCall( lib_call = LibraryCall(
lib_contract, lib_contract,

@ -106,7 +106,7 @@ class HighLevelCall(Call, OperationWithLValue):
:return: bool :return: bool
""" """
# If solidity >0.5, STATICCALL is used # If solidity >0.5, STATICCALL is used
if self.slither.solc_version and self.slither.solc_version >= "0.5.0": if self.compilation_unit.solc_version and self.compilation_unit.solc_version >= "0.5.0":
if isinstance(self.function, Function) and (self.function.view or self.function.pure): if isinstance(self.function, Function) and (self.function.view or self.function.pure):
return False return False
if isinstance(self.function, Variable): if isinstance(self.function, Variable):

@ -50,7 +50,7 @@ class NewContract(Call, OperationWithLValue): # pylint: disable=too-many-instan
@property @property
def contract_created(self): def contract_created(self):
contract_name = self.contract_name contract_name = self.contract_name
contract_instance = self.slither.get_contract_from_name(contract_name) contract_instance = self.compilation_unit.get_contract_from_name(contract_name)
return contract_instance return contract_instance
################################################################################### ###################################################################################

@ -12,8 +12,8 @@ class ReferenceVariable(ChildNode, Variable):
def __init__(self, node: "Node", index=None): def __init__(self, node: "Node", index=None):
super().__init__() super().__init__()
if index is None: if index is None:
self._index = node.slither.counter_slithir_reference self._index = node.compilation_unit.counter_slithir_reference
node.slither.counter_slithir_reference += 1 node.compilation_unit.counter_slithir_reference += 1
else: else:
self._index = index self._index = index
self._points_to = None self._points_to = None

@ -11,8 +11,8 @@ class TemporaryVariable(ChildNode, Variable):
def __init__(self, node: "Node", index=None): def __init__(self, node: "Node", index=None):
super().__init__() super().__init__()
if index is None: if index is None:
self._index = node.slither.counter_slithir_temporary self._index = node.compilation_unit.counter_slithir_temporary
node.slither.counter_slithir_temporary += 1 node.compilation_unit.counter_slithir_temporary += 1
else: else:
self._index = index self._index = index
self._node = node self._node = node

@ -11,8 +11,8 @@ class TupleVariable(ChildNode, SlithIRVariable):
def __init__(self, node: "Node", index=None): def __init__(self, node: "Node", index=None):
super().__init__() super().__init__()
if index is None: if index is None:
self._index = node.slither.counter_slithir_tuple self._index = node.compilation_unit.counter_slithir_tuple
node.slither.counter_slithir_tuple += 1 node.compilation_unit.counter_slithir_tuple += 1
else: else:
self._index = index self._index = index

@ -44,7 +44,9 @@ class NodeSolc:
AssignmentOperationType.ASSIGN, AssignmentOperationType.ASSIGN,
self._node.variable_declaration.type, self._node.variable_declaration.type,
) )
_expression.set_offset(self._node.expression.source_mapping, self._node.slither) _expression.set_offset(
self._node.expression.source_mapping, self._node.compilation_unit
)
self._node.add_expression(_expression, bypass_verif_empty=True) self._node.add_expression(_expression, bypass_verif_empty=True)
expression = self._node.expression expression = self._node.expression

@ -16,18 +16,18 @@ from slither.solc_parsing.variables.state_variable import StateVariableSolc
LOGGER = logging.getLogger("ContractSolcParsing") LOGGER = logging.getLogger("ContractSolcParsing")
if TYPE_CHECKING: if TYPE_CHECKING:
from slither.solc_parsing.slitherSolc import SlitherSolc from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc
from slither.core.slither_core import SlitherCore from slither.core.slither_core import SlitherCore
from slither.core.compilation_unit import SlitherCompilationUnit
# pylint: disable=too-many-instance-attributes,import-outside-toplevel,too-many-nested-blocks,too-many-public-methods # pylint: disable=too-many-instance-attributes,import-outside-toplevel,too-many-nested-blocks,too-many-public-methods
class ContractSolc: class ContractSolc:
def __init__(self, slither_parser: "SlitherSolc", contract: Contract, data): def __init__(self, slither_parser: "SlitherCompilationUnitSolc", contract: Contract, data):
# assert slitherSolc.solc_version.startswith('0.4') # assert slitherSolc.solc_version.startswith('0.4')
self._contract = contract self._contract = contract
self._contract.set_slither(slither_parser.core)
self._slither_parser = slither_parser self._slither_parser = slither_parser
self._data = data self._data = data
@ -89,11 +89,11 @@ class ContractSolc:
return self._linearized_base_contracts return self._linearized_base_contracts
@property @property
def slither(self) -> "SlitherCore": def compilation_unit(self) -> "SlitherCompilationUnit":
return self._contract.slither return self._contract.compilation_unit
@property @property
def slither_parser(self) -> "SlitherSolc": def slither_parser(self) -> "SlitherCompilationUnitSolc":
return self._slither_parser return self._slither_parser
@property @property
@ -254,7 +254,7 @@ class ContractSolc:
st = StructureContract() st = StructureContract()
st.set_contract(self._contract) st.set_contract(self._contract)
st.set_offset(struct["src"], self._contract.slither) st.set_offset(struct["src"], self._contract.compilation_unit)
st_parser = StructureContractSolc(st, struct, self) st_parser = StructureContractSolc(st, struct, self)
self._contract.structures_as_dict[st.name] = st self._contract.structures_as_dict[st.name] = st
@ -281,7 +281,7 @@ class ContractSolc:
for varNotParsed in self._variablesNotParsed: for varNotParsed in self._variablesNotParsed:
var = StateVariable() var = StateVariable()
var.set_offset(varNotParsed["src"], self._contract.slither) var.set_offset(varNotParsed["src"], self._contract.compilation_unit)
var.set_contract(self._contract) var.set_contract(self._contract)
var_parser = StateVariableSolc(var, varNotParsed) var_parser = StateVariableSolc(var, varNotParsed)
@ -291,13 +291,13 @@ class ContractSolc:
self._contract.add_variables_ordered([var]) self._contract.add_variables_ordered([var])
def _parse_modifier(self, modifier_data: Dict): def _parse_modifier(self, modifier_data: Dict):
modif = Modifier(self.slither) modif = Modifier(self._contract.compilation_unit)
modif.set_offset(modifier_data["src"], self._contract.slither) modif.set_offset(modifier_data["src"], self._contract.compilation_unit)
modif.set_contract(self._contract) modif.set_contract(self._contract)
modif.set_contract_declarer(self._contract) modif.set_contract_declarer(self._contract)
modif_parser = ModifierSolc(modif, modifier_data, self, self.slither_parser) modif_parser = ModifierSolc(modif, modifier_data, self, self.slither_parser)
self._contract.slither.add_modifier(modif) self._contract.compilation_unit.add_modifier(modif)
self._modifiers_no_params.append(modif_parser) self._modifiers_no_params.append(modif_parser)
self._modifiers_parser.append(modif_parser) self._modifiers_parser.append(modif_parser)
@ -309,13 +309,13 @@ class ContractSolc:
self._modifiersNotParsed = None self._modifiersNotParsed = None
def _parse_function(self, function_data: Dict): def _parse_function(self, function_data: Dict):
func = FunctionContract(self.slither) func = FunctionContract(self._contract.compilation_unit)
func.set_offset(function_data["src"], self._contract.slither) func.set_offset(function_data["src"], self._contract.compilation_unit)
func.set_contract(self._contract) func.set_contract(self._contract)
func.set_contract_declarer(self._contract) func.set_contract_declarer(self._contract)
func_parser = FunctionSolc(func, function_data, self, self._slither_parser) func_parser = FunctionSolc(func, function_data, self, self._slither_parser)
self._contract.slither.add_function(func) self._contract.compilation_unit.add_function(func)
self._functions_no_params.append(func_parser) self._functions_no_params.append(func_parser)
self._functions_parser.append(func_parser) self._functions_parser.append(func_parser)
@ -336,7 +336,7 @@ class ContractSolc:
################################################################################### ###################################################################################
def log_incorrect_parsing(self, error): def log_incorrect_parsing(self, error):
if self._contract.slither.disallow_partial: if self._contract.compilation_unit.core.disallow_partial:
raise ParsingError(error) raise ParsingError(error)
LOGGER.error(error) LOGGER.error(error)
self._contract.is_incorrectly_parsed = True self._contract.is_incorrectly_parsed = True
@ -404,7 +404,7 @@ class ContractSolc:
parser: List[FunctionSolc], parser: List[FunctionSolc],
all_elements: Dict[str, Function], all_elements: Dict[str, Function],
): ):
elem = Cls(self.slither) elem = Cls(self._contract.compilation_unit)
elem.set_contract(self._contract) elem.set_contract(self._contract)
underlying_function = element_parser.underlying_function underlying_function = element_parser.underlying_function
# TopLevel function are not analyzed here # TopLevel function are not analyzed here
@ -412,7 +412,7 @@ class ContractSolc:
elem.set_contract_declarer(underlying_function.contract_declarer) elem.set_contract_declarer(underlying_function.contract_declarer)
elem.set_offset( elem.set_offset(
element_parser.function_not_parsed["src"], element_parser.function_not_parsed["src"],
self._contract.slither, self._contract.compilation_unit,
) )
elem_parser = Cls_parser( elem_parser = Cls_parser(
@ -428,9 +428,9 @@ class ContractSolc:
explored_reference_id.add(element_parser.referenced_declaration) explored_reference_id.add(element_parser.referenced_declaration)
elem_parser.analyze_params() elem_parser.analyze_params()
if isinstance(elem, Modifier): if isinstance(elem, Modifier):
self._contract.slither.add_modifier(elem) self._contract.compilation_unit.add_modifier(elem)
else: else:
self._contract.slither.add_function(elem) self._contract.compilation_unit.add_function(elem)
self._slither_parser.add_function_or_modifier_parser(elem_parser) self._slither_parser.add_function_or_modifier_parser(elem_parser)
@ -587,7 +587,7 @@ class ContractSolc:
new_enum = EnumContract(name, canonicalName, values) new_enum = EnumContract(name, canonicalName, values)
new_enum.set_contract(self._contract) new_enum.set_contract(self._contract)
new_enum.set_offset(enum["src"], self._contract.slither) new_enum.set_offset(enum["src"], self._contract.compilation_unit)
self._contract.enums_as_dict[canonicalName] = new_enum self._contract.enums_as_dict[canonicalName] = new_enum
def _analyze_struct(self, struct: StructureContractSolc): # pylint: disable=no-self-use def _analyze_struct(self, struct: StructureContractSolc): # pylint: disable=no-self-use
@ -608,7 +608,7 @@ class ContractSolc:
for event_to_parse in self._eventsNotParsed: for event_to_parse in self._eventsNotParsed:
event = Event() event = Event()
event.set_contract(self._contract) event.set_contract(self._contract)
event.set_offset(event_to_parse["src"], self._contract.slither) event.set_offset(event_to_parse["src"], self._contract.compilation_unit)
event_parser = EventSolc(event, event_to_parse, self) event_parser = EventSolc(event, event_to_parse, self)
event_parser.analyze(self) event_parser.analyze(self)

@ -48,7 +48,9 @@ class EventSolc:
elem = EventVariable() elem = EventVariable()
# Todo: check if the source offset is always here # Todo: check if the source offset is always here
if "src" in elem_to_parse: if "src" in elem_to_parse:
elem.set_offset(elem_to_parse["src"], self._parser_contract.slither) elem.set_offset(
elem_to_parse["src"], self._parser_contract.underlying_contract.compilation_unit
)
elem_parser = EventVariableSolc(elem, elem_to_parse) elem_parser = EventVariableSolc(elem, elem_to_parse)
elem_parser.analyze(contract) elem_parser.analyze(contract)

@ -30,8 +30,10 @@ from slither.solc_parsing.exceptions import ParsingError
if TYPE_CHECKING: if TYPE_CHECKING:
from slither.core.expressions.expression import Expression from slither.core.expressions.expression import Expression
from slither.solc_parsing.declarations.contract import ContractSolc from slither.solc_parsing.declarations.contract import ContractSolc
from slither.solc_parsing.slitherSolc import SlitherSolc from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc
from slither.core.slither_core import SlitherCore from slither.core.slither_core import SlitherCore
from slither.core.compilation_unit import SlitherCompilationUnit
LOGGER = logging.getLogger("FunctionSolc") LOGGER = logging.getLogger("FunctionSolc")
@ -52,9 +54,9 @@ class FunctionSolc:
function: Function, function: Function,
function_data: Dict, function_data: Dict,
contract_parser: Optional["ContractSolc"], contract_parser: Optional["ContractSolc"],
slither_parser: "SlitherSolc", slither_parser: "SlitherCompilationUnitSolc",
): ):
self._slither_parser: "SlitherSolc" = slither_parser self._slither_parser: "SlitherCompilationUnitSolc" = slither_parser
self._contract_parser = contract_parser self._contract_parser = contract_parser
self._function = function self._function = function
@ -100,12 +102,12 @@ class FunctionSolc:
return self._contract_parser return self._contract_parser
@property @property
def slither_parser(self) -> "SlitherSolc": def slither_parser(self) -> "SlitherCompilationUnitSolc":
return self._slither_parser return self._slither_parser
@property @property
def slither(self) -> "SlitherCore": def compilation_unit(self) -> "SlitherCompilationUnit":
return self._function.slither return self._function.compilation_unit
################################################################################### ###################################################################################
################################################################################### ###################################################################################
@ -647,7 +649,7 @@ class FunctionSolc:
try: try:
local_var = LocalVariable() local_var = LocalVariable()
local_var.set_function(self._function) local_var.set_function(self._function)
local_var.set_offset(statement["src"], self._function.slither) local_var.set_offset(statement["src"], self._function.compilation_unit)
local_var_parser = LocalVariableSolc(local_var, statement) local_var_parser = LocalVariableSolc(local_var, statement)
self._add_local_variable(local_var_parser) self._add_local_variable(local_var_parser)
@ -827,7 +829,7 @@ class FunctionSolc:
) -> NodeSolc: ) -> NodeSolc:
local_var = LocalVariableInitFromTuple() local_var = LocalVariableInitFromTuple()
local_var.set_function(self._function) local_var.set_function(self._function)
local_var.set_offset(statement["src"], self._function.slither) local_var.set_offset(statement["src"], self._function.compilation_unit)
local_var_parser = LocalVariableInitFromTupleSolc(local_var, statement, index) local_var_parser = LocalVariableInitFromTupleSolc(local_var, statement, index)
@ -861,7 +863,7 @@ class FunctionSolc:
node = self._parse_block(statement, node) node = self._parse_block(statement, node)
elif name == "InlineAssembly": elif name == "InlineAssembly":
# Added with solc 0.6 - the yul code is an AST # Added with solc 0.6 - the yul code is an AST
if "AST" in statement and not self.slither.skip_assembly: if "AST" in statement and not self.compilation_unit.core.skip_assembly:
self._function.contains_assembly = True self._function.contains_assembly = True
yul_object = self._new_yul_block(statement["src"]) yul_object = self._new_yul_block(statement["src"])
entrypoint = yul_object.entrypoint entrypoint = yul_object.entrypoint
@ -1069,7 +1071,7 @@ class FunctionSolc:
local_var = LocalVariable() local_var = LocalVariable()
local_var.set_function(self._function) local_var.set_function(self._function)
local_var.set_offset(param["src"], self._function.slither) local_var.set_offset(param["src"], self._function.compilation_unit)
local_var_parser = LocalVariableSolc(local_var, param) local_var_parser = LocalVariableSolc(local_var, param)
@ -1085,7 +1087,7 @@ class FunctionSolc:
def _parse_params(self, params: Dict): def _parse_params(self, params: Dict):
assert params[self.get_key()] == "ParameterList" assert params[self.get_key()] == "ParameterList"
self._function.parameters_src().set_offset(params["src"], self._function.slither) self._function.parameters_src().set_offset(params["src"], self._function.compilation_unit)
if self.is_compact_ast: if self.is_compact_ast:
params = params["parameters"] params = params["parameters"]
@ -1101,7 +1103,7 @@ class FunctionSolc:
assert returns[self.get_key()] == "ParameterList" assert returns[self.get_key()] == "ParameterList"
self._function.returns_src().set_offset(returns["src"], self._function.slither) self._function.returns_src().set_offset(returns["src"], self._function.compilation_unit)
if self.is_compact_ast: if self.is_compact_ast:
returns = returns["parameters"] returns = returns["parameters"]

@ -11,7 +11,7 @@ from slither.solc_parsing.declarations.function import FunctionSolc
if TYPE_CHECKING: if TYPE_CHECKING:
from slither.solc_parsing.declarations.contract import ContractSolc from slither.solc_parsing.declarations.contract import ContractSolc
from slither.solc_parsing.slitherSolc import SlitherSolc from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc
class ModifierSolc(FunctionSolc): class ModifierSolc(FunctionSolc):
@ -20,7 +20,7 @@ class ModifierSolc(FunctionSolc):
modifier: Modifier, modifier: Modifier,
function_data: Dict, function_data: Dict,
contract_parser: "ContractSolc", contract_parser: "ContractSolc",
slither_parser: "SlitherSolc", slither_parser: "SlitherCompilationUnitSolc",
): ):
super().__init__(modifier, function_data, contract_parser, slither_parser) super().__init__(modifier, function_data, contract_parser, slither_parser)
# _modifier is equal to _function, but keep it here to prevent # _modifier is equal to _function, but keep it here to prevent

@ -49,7 +49,7 @@ class StructureContractSolc: # pylint: disable=too-few-public-methods
for elem_to_parse in self._elemsNotParsed: for elem_to_parse in self._elemsNotParsed:
elem = StructureVariable() elem = StructureVariable()
elem.set_structure(self._structure) elem.set_structure(self._structure)
elem.set_offset(elem_to_parse["src"], self._structure.contract.slither) elem.set_offset(elem_to_parse["src"], self._structure.contract.compilation_unit)
elem_parser = StructureVariableSolc(elem, elem_to_parse) elem_parser = StructureVariableSolc(elem, elem_to_parse)
elem_parser.analyze(self._contract_parser) elem_parser.analyze(self._contract_parser)

@ -8,7 +8,7 @@ from slither.core.variables.structure_variable import StructureVariable
from slither.solc_parsing.variables.structure_variable import StructureVariableSolc from slither.solc_parsing.variables.structure_variable import StructureVariableSolc
if TYPE_CHECKING: if TYPE_CHECKING:
from slither.solc_parsing.slitherSolc import SlitherSolc from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc
class StructureTopLevelSolc: # pylint: disable=too-few-public-methods class StructureTopLevelSolc: # pylint: disable=too-few-public-methods
@ -22,7 +22,7 @@ class StructureTopLevelSolc: # pylint: disable=too-few-public-methods
self, self,
st: Structure, st: Structure,
struct: Dict, struct: Dict,
slither_parser: "SlitherSolc", slither_parser: "SlitherCompilationUnitSolc",
): ):
if slither_parser.is_compact_ast: if slither_parser.is_compact_ast:
@ -49,7 +49,7 @@ class StructureTopLevelSolc: # pylint: disable=too-few-public-methods
for elem_to_parse in self._elemsNotParsed: for elem_to_parse in self._elemsNotParsed:
elem = StructureVariable() elem = StructureVariable()
elem.set_structure(self._structure) elem.set_structure(self._structure)
elem.set_offset(elem_to_parse["src"], self._slither_parser.core) elem.set_offset(elem_to_parse["src"], self._slither_parser.compilation_unit)
elem_parser = StructureVariableSolc(elem, elem_to_parse) elem_parser = StructureVariableSolc(elem, elem_to_parse)
elem_parser.analyze(self._slither_parser) elem_parser.analyze(self._slither_parser)

@ -53,8 +53,8 @@ if TYPE_CHECKING:
from slither.core.expressions.expression import Expression from slither.core.expressions.expression import Expression
from slither.solc_parsing.declarations.function import FunctionSolc from slither.solc_parsing.declarations.function import FunctionSolc
from slither.solc_parsing.declarations.contract import ContractSolc from slither.solc_parsing.declarations.contract import ContractSolc
from slither.core.slither_core import SlitherCore from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc
from slither.solc_parsing.slitherSolc import SlitherSolc from slither.core.compilation_unit import SlitherCompilationUnit
logger = logging.getLogger("ExpressionParsing") logger = logging.getLogger("ExpressionParsing")
@ -135,7 +135,7 @@ def _find_variable_in_function_parser(
def _find_top_level( def _find_top_level(
var_name: str, sl: "SlitherCore" var_name: str, sl: "SlitherCompilationUnit"
) -> Optional[Union[Enum, Structure, SolidityVariable]]: ) -> Optional[Union[Enum, Structure, SolidityVariable]]:
structures_top_level = sl.structures_top_level structures_top_level = sl.structures_top_level
for st in structures_top_level: for st in structures_top_level:
@ -224,23 +224,28 @@ def _find_in_contract(
def _find_variable_init( def _find_variable_init(
caller_context: CallerContext, caller_context: CallerContext,
) -> Tuple[List[Contract], Union[List["FunctionSolc"]], "SlitherCore", "SlitherSolc"]: ) -> Tuple[
from slither.solc_parsing.slitherSolc import SlitherSolc List[Contract],
Union[List["FunctionSolc"]],
"SlitherCompilationUnit",
"SlitherCompilationUnitSolc",
]:
from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc
from slither.solc_parsing.declarations.contract import ContractSolc from slither.solc_parsing.declarations.contract import ContractSolc
from slither.solc_parsing.declarations.function import FunctionSolc from slither.solc_parsing.declarations.function import FunctionSolc
direct_contracts: List[Contract] direct_contracts: List[Contract]
direct_functions_parser: List[FunctionSolc] direct_functions_parser: List[FunctionSolc]
if isinstance(caller_context, SlitherSolc): if isinstance(caller_context, SlitherCompilationUnitSolc):
direct_contracts = [] direct_contracts = []
direct_functions_parser = [] direct_functions_parser = []
sl = caller_context.core sl = caller_context.compilation_unit
sl_parser = caller_context sl_parser = caller_context
elif isinstance(caller_context, ContractSolc): elif isinstance(caller_context, ContractSolc):
direct_contracts = [caller_context.underlying_contract] direct_contracts = [caller_context.underlying_contract]
direct_functions_parser = caller_context.functions_parser + caller_context.modifiers_parser direct_functions_parser = caller_context.functions_parser + caller_context.modifiers_parser
sl = caller_context.slither sl = caller_context.slither_parser.compilation_unit
sl_parser = caller_context.slither_parser sl_parser = caller_context.slither_parser
elif isinstance(caller_context, FunctionSolc): elif isinstance(caller_context, FunctionSolc):
if caller_context.contract_parser: if caller_context.contract_parser:
@ -253,7 +258,7 @@ def _find_variable_init(
# Top level functions # Top level functions
direct_contracts = [] direct_contracts = []
direct_functions_parser = [] direct_functions_parser = []
sl = caller_context.slither sl = caller_context.underlying_function.compilation_unit
sl_parser = caller_context.slither_parser sl_parser = caller_context.slither_parser
else: else:
raise SlitherError( raise SlitherError(
@ -452,7 +457,7 @@ def parse_call(expression: Dict, caller_context): # pylint: disable=too-many-st
expression = parse_expression(expression_to_parse, caller_context) expression = parse_expression(expression_to_parse, caller_context)
t = TypeConversion(expression, type_call) t = TypeConversion(expression, type_call)
t.set_offset(src, caller_context.slither) t.set_offset(src, caller_context.compilation_unit)
return t return t
call_gas = None call_gas = None
@ -485,10 +490,10 @@ def parse_call(expression: Dict, caller_context): # pylint: disable=too-many-st
if isinstance(called, SuperCallExpression): if isinstance(called, SuperCallExpression):
sp = SuperCallExpression(called, arguments, type_return) sp = SuperCallExpression(called, arguments, type_return)
sp.set_offset(expression["src"], caller_context.slither) sp.set_offset(expression["src"], caller_context.compilation_unit)
return sp return sp
call_expression = CallExpression(called, arguments, type_return) call_expression = CallExpression(called, arguments, type_return)
call_expression.set_offset(src, caller_context.slither) call_expression.set_offset(src, caller_context.compilation_unit)
# Only available if the syntax {gas:, value:} was used # Only available if the syntax {gas:, value:} was used
call_expression.call_gas = call_gas call_expression.call_gas = call_gas
@ -536,7 +541,7 @@ def _parse_elementary_type_name_expression(
else: else:
t = parse_type(UnknownType(value), caller_context) t = parse_type(UnknownType(value), caller_context)
e = ElementaryTypeNameExpression(t) e = ElementaryTypeNameExpression(t)
e.set_offset(expression["src"], caller_context.slither) e.set_offset(expression["src"], caller_context.compilation_unit)
return e return e
@ -588,7 +593,7 @@ def parse_expression(expression: Dict, caller_context: CallerContext) -> "Expres
assert len(expression["children"]) == 1 assert len(expression["children"]) == 1
expression = parse_expression(expression["children"][0], caller_context) expression = parse_expression(expression["children"][0], caller_context)
unary_op = UnaryOperation(expression, operation_type) unary_op = UnaryOperation(expression, operation_type)
unary_op.set_offset(src, caller_context.slither) unary_op.set_offset(src, caller_context.compilation_unit)
return unary_op return unary_op
if name == "BinaryOperation": if name == "BinaryOperation":
@ -606,7 +611,7 @@ def parse_expression(expression: Dict, caller_context: CallerContext) -> "Expres
left_expression = parse_expression(expression["children"][0], caller_context) left_expression = parse_expression(expression["children"][0], caller_context)
right_expression = parse_expression(expression["children"][1], caller_context) right_expression = parse_expression(expression["children"][1], caller_context)
binary_op = BinaryOperation(left_expression, right_expression, operation_type) binary_op = BinaryOperation(left_expression, right_expression, operation_type)
binary_op.set_offset(src, caller_context.slither) binary_op.set_offset(src, caller_context.compilation_unit)
return binary_op return binary_op
if name in "FunctionCall": if name in "FunctionCall":
@ -655,7 +660,7 @@ def parse_expression(expression: Dict, caller_context: CallerContext) -> "Expres
if elems[idx] == "": if elems[idx] == "":
expressions.insert(idx, None) expressions.insert(idx, None)
t = TupleExpression(expressions) t = TupleExpression(expressions)
t.set_offset(src, caller_context.slither) t.set_offset(src, caller_context.compilation_unit)
return t return t
if name == "Conditional": if name == "Conditional":
@ -670,7 +675,7 @@ def parse_expression(expression: Dict, caller_context: CallerContext) -> "Expres
then_expression = parse_expression(children[1], caller_context) then_expression = parse_expression(children[1], caller_context)
else_expression = parse_expression(children[2], caller_context) else_expression = parse_expression(children[2], caller_context)
conditional = ConditionalExpression(if_expression, then_expression, else_expression) conditional = ConditionalExpression(if_expression, then_expression, else_expression)
conditional.set_offset(src, caller_context.slither) conditional.set_offset(src, caller_context.compilation_unit)
return conditional return conditional
if name == "Assignment": if name == "Assignment":
@ -694,7 +699,7 @@ def parse_expression(expression: Dict, caller_context: CallerContext) -> "Expres
assignement = AssignmentOperation( assignement = AssignmentOperation(
left_expression, right_expression, operation_type, operation_return_type left_expression, right_expression, operation_type, operation_return_type
) )
assignement.set_offset(src, caller_context.slither) assignement.set_offset(src, caller_context.compilation_unit)
return assignement return assignement
if name == "Literal": if name == "Literal":
@ -745,7 +750,7 @@ def parse_expression(expression: Dict, caller_context: CallerContext) -> "Expres
else: else:
type_candidate = ElementaryType("string") type_candidate = ElementaryType("string")
literal = Literal(value, type_candidate, subdenomination) literal = Literal(value, type_candidate, subdenomination)
literal.set_offset(src, caller_context.slither) literal.set_offset(src, caller_context.compilation_unit)
return literal return literal
if name == "Identifier": if name == "Identifier":
@ -776,7 +781,7 @@ def parse_expression(expression: Dict, caller_context: CallerContext) -> "Expres
var = find_variable(value, caller_context, referenced_declaration) var = find_variable(value, caller_context, referenced_declaration)
identifier = Identifier(var) identifier = Identifier(var)
identifier.set_offset(src, caller_context.slither) identifier.set_offset(src, caller_context.compilation_unit)
return identifier return identifier
if name == "IndexAccess": if name == "IndexAccess":
@ -803,7 +808,7 @@ def parse_expression(expression: Dict, caller_context: CallerContext) -> "Expres
left_expression = parse_expression(left, caller_context) left_expression = parse_expression(left, caller_context)
right_expression = parse_expression(right, caller_context) right_expression = parse_expression(right, caller_context)
index = IndexAccess(left_expression, right_expression, index_type) index = IndexAccess(left_expression, right_expression, index_type)
index.set_offset(src, caller_context.slither) index.set_offset(src, caller_context.compilation_unit)
return index return index
if name == "MemberAccess": if name == "MemberAccess":
@ -827,13 +832,13 @@ def parse_expression(expression: Dict, caller_context: CallerContext) -> "Expres
if var is None: if var is None:
raise VariableNotFound("Variable not found: {}".format(super_name)) raise VariableNotFound("Variable not found: {}".format(super_name))
sup = SuperIdentifier(var) sup = SuperIdentifier(var)
sup.set_offset(src, caller_context.slither) sup.set_offset(src, caller_context.compilation_unit)
return sup return sup
member_access = MemberAccess(member_name, member_type, member_expression) member_access = MemberAccess(member_name, member_type, member_expression)
member_access.set_offset(src, caller_context.slither) member_access.set_offset(src, caller_context.compilation_unit)
if str(member_access) in SOLIDITY_VARIABLES_COMPOSED: if str(member_access) in SOLIDITY_VARIABLES_COMPOSED:
id_idx = Identifier(SolidityVariableComposed(str(member_access))) id_idx = Identifier(SolidityVariableComposed(str(member_access)))
id_idx.set_offset(src, caller_context.slither) id_idx.set_offset(src, caller_context.compilation_unit)
return id_idx return id_idx
return member_access return member_access
@ -877,7 +882,7 @@ def parse_expression(expression: Dict, caller_context: CallerContext) -> "Expres
else: else:
raise ParsingError("Incorrect type array {}".format(type_name)) raise ParsingError("Incorrect type array {}".format(type_name))
array = NewArray(depth, array_type) array = NewArray(depth, array_type)
array.set_offset(src, caller_context.slither) array.set_offset(src, caller_context.compilation_unit)
return array return array
if type_name[caller_context.get_key()] == "ElementaryTypeName": if type_name[caller_context.get_key()] == "ElementaryTypeName":
@ -886,7 +891,7 @@ def parse_expression(expression: Dict, caller_context: CallerContext) -> "Expres
else: else:
elem_type = ElementaryType(type_name["attributes"]["name"]) elem_type = ElementaryType(type_name["attributes"]["name"])
new_elem = NewElementaryType(elem_type) new_elem = NewElementaryType(elem_type)
new_elem.set_offset(src, caller_context.slither) new_elem.set_offset(src, caller_context.compilation_unit)
return new_elem return new_elem
assert type_name[caller_context.get_key()] == "UserDefinedTypeName" assert type_name[caller_context.get_key()] == "UserDefinedTypeName"
@ -905,7 +910,7 @@ def parse_expression(expression: Dict, caller_context: CallerContext) -> "Expres
else: else:
contract_name = type_name["attributes"]["name"] contract_name = type_name["attributes"]["name"]
new = NewContract(contract_name) new = NewContract(contract_name)
new.set_offset(src, caller_context.slither) new.set_offset(src, caller_context.compilation_unit)
return new return new
if name == "ModifierInvocation": if name == "ModifierInvocation":
@ -921,7 +926,7 @@ def parse_expression(expression: Dict, caller_context: CallerContext) -> "Expres
arguments = [parse_expression(a, caller_context) for a in children[1::]] arguments = [parse_expression(a, caller_context) for a in children[1::]]
call = CallExpression(called, arguments, "Modifier") call = CallExpression(called, arguments, "Modifier")
call.set_offset(src, caller_context.slither) call.set_offset(src, caller_context.compilation_unit)
return call return call
if name == "IndexRangeAccess": if name == "IndexRangeAccess":
@ -947,7 +952,7 @@ def parse_expression(expression: Dict, caller_context: CallerContext) -> "Expres
var = find_variable(value, caller_context, referenced_declaration) var = find_variable(value, caller_context, referenced_declaration)
identifier = Identifier(var) identifier = Identifier(var)
identifier.set_offset(src, caller_context.slither) identifier.set_offset(src, caller_context.compilation_unit)
return identifier return identifier
raise ParsingError("IdentifierPath not currently supported for the legacy ast") raise ParsingError("IdentifierPath not currently supported for the legacy ast")

@ -4,19 +4,18 @@ import os
import re import re
from typing import List, Dict from typing import List, Dict
from slither.analyses.data_dependency.data_dependency import compute_dependency
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.core.declarations import Contract from slither.core.declarations import Contract
from slither.core.declarations.enum_top_level import EnumTopLevel from slither.core.declarations.enum_top_level import EnumTopLevel
from slither.core.declarations.function_top_level import FunctionTopLevel from slither.core.declarations.function_top_level import FunctionTopLevel
from slither.core.declarations.import_directive import Import
from slither.core.declarations.pragma_directive import Pragma
from slither.core.declarations.structure_top_level import StructureTopLevel from slither.core.declarations.structure_top_level import StructureTopLevel
from slither.core.variables.top_level_variable import TopLevelVariable from slither.core.variables.top_level_variable import TopLevelVariable
from slither.exceptions import SlitherException from slither.exceptions import SlitherException
from slither.solc_parsing.declarations.contract import ContractSolc from slither.solc_parsing.declarations.contract import ContractSolc
from slither.solc_parsing.declarations.function import FunctionSolc from slither.solc_parsing.declarations.function import FunctionSolc
from slither.core.slither_core import SlitherCore
from slither.core.declarations.pragma_directive import Pragma
from slither.core.declarations.import_directive import Import
from slither.analyses.data_dependency.data_dependency import compute_dependency
from slither.solc_parsing.declarations.structure_top_level import StructureTopLevelSolc from slither.solc_parsing.declarations.structure_top_level import StructureTopLevelSolc
from slither.solc_parsing.exceptions import VariableNotFound from slither.solc_parsing.exceptions import VariableNotFound
from slither.solc_parsing.variables.top_level_variable import TopLevelVariableSolc from slither.solc_parsing.variables.top_level_variable import TopLevelVariableSolc
@ -26,11 +25,11 @@ logger = logging.getLogger("SlitherSolcParsing")
logger.setLevel(logging.INFO) logger.setLevel(logging.INFO)
class SlitherSolc: class SlitherCompilationUnitSolc:
# pylint: disable=no-self-use,too-many-instance-attributes # pylint: disable=no-self-use,too-many-instance-attributes
def __init__(self, filename: str, core: SlitherCore): def __init__(self, compilation_unit: SlitherCompilationUnit):
super().__init__() super().__init__()
core.filename = filename
self._contracts_by_id: Dict[int, ContractSolc] = {} self._contracts_by_id: Dict[int, ContractSolc] = {}
self._parsed = False self._parsed = False
self._analyzed = False self._analyzed = False
@ -41,15 +40,16 @@ class SlitherSolc:
self._functions_top_level_parser: List[FunctionSolc] = [] self._functions_top_level_parser: List[FunctionSolc] = []
self._is_compact_ast = False self._is_compact_ast = False
self._core: SlitherCore = core # self._core: SlitherCore = core
self._compilation_unit = compilation_unit
self._all_functions_and_modifier_parser: List[FunctionSolc] = [] self._all_functions_and_modifier_parser: List[FunctionSolc] = []
self._top_level_contracts_counter = 0 self._top_level_contracts_counter = 0
@property @property
def core(self): def compilation_unit(self) -> SlitherCompilationUnit:
return self._core return self._compilation_unit
@property @property
def all_functions_and_modifiers_parser(self) -> List[FunctionSolc]: def all_functions_and_modifiers_parser(self) -> List[FunctionSolc]:
@ -140,8 +140,8 @@ class SlitherSolc:
values.append(child["attributes"][self.get_key()]) values.append(child["attributes"][self.get_key()])
enum = EnumTopLevel(name, canonicalName, values) enum = EnumTopLevel(name, canonicalName, values)
enum.set_offset(top_level_data["src"], self._core) enum.set_offset(top_level_data["src"], self._compilation_unit)
self.core.enums_top_level.append(enum) self._compilation_unit.enums_top_level.append(enum)
def parse_top_level_from_loaded_json( def parse_top_level_from_loaded_json(
self, data_loaded: Dict, filename: str self, data_loaded: Dict, filename: str
@ -152,14 +152,12 @@ class SlitherSolc:
if "sourcePaths" in data_loaded: if "sourcePaths" in data_loaded:
for sourcePath in data_loaded["sourcePaths"]: for sourcePath in data_loaded["sourcePaths"]:
if os.path.isfile(sourcePath): if os.path.isfile(sourcePath):
self._core.add_source_code(sourcePath) self._compilation_unit.core.add_source_code(sourcePath)
if data_loaded[self.get_key()] == "root": if data_loaded[self.get_key()] == "root":
self._core.solc_version = "0.3"
logger.error("solc <0.4 is not supported") logger.error("solc <0.4 is not supported")
return return
if data_loaded[self.get_key()] == "SourceUnit": if data_loaded[self.get_key()] == "SourceUnit":
self._core.solc_version = "0.4"
self._parse_source_unit(data_loaded, filename) self._parse_source_unit(data_loaded, filename)
else: else:
logger.error("solc version is not supported") logger.error("solc version is not supported")
@ -167,10 +165,10 @@ class SlitherSolc:
for top_level_data in data_loaded[self.get_children()]: for top_level_data in data_loaded[self.get_children()]:
if top_level_data[self.get_key()] == "ContractDefinition": if top_level_data[self.get_key()] == "ContractDefinition":
contract = Contract() contract = Contract(self._compilation_unit)
contract_parser = ContractSolc(self, contract, top_level_data) contract_parser = ContractSolc(self, contract, top_level_data)
if "src" in top_level_data: if "src" in top_level_data:
contract.set_offset(top_level_data["src"], self._core) contract.set_offset(top_level_data["src"], self._compilation_unit)
self._underlying_contract_to_parser[contract] = contract_parser self._underlying_contract_to_parser[contract] = contract_parser
@ -179,8 +177,8 @@ class SlitherSolc:
pragma = Pragma(top_level_data["literals"]) pragma = Pragma(top_level_data["literals"])
else: else:
pragma = Pragma(top_level_data["attributes"]["literals"]) pragma = Pragma(top_level_data["attributes"]["literals"])
pragma.set_offset(top_level_data["src"], self._core) pragma.set_offset(top_level_data["src"], self._compilation_unit)
self._core.pragma_directives.append(pragma) self._compilation_unit.pragma_directives.append(pragma)
elif top_level_data[self.get_key()] == "ImportDirective": elif top_level_data[self.get_key()] == "ImportDirective":
if self.is_compact_ast: if self.is_compact_ast:
import_directive = Import(top_level_data["absolutePath"]) import_directive = Import(top_level_data["absolutePath"])
@ -189,15 +187,15 @@ class SlitherSolc:
import_directive.alias = top_level_data["unitAlias"] import_directive.alias = top_level_data["unitAlias"]
else: else:
import_directive = Import(top_level_data["attributes"].get("absolutePath", "")) import_directive = Import(top_level_data["attributes"].get("absolutePath", ""))
import_directive.set_offset(top_level_data["src"], self._core) import_directive.set_offset(top_level_data["src"], self._compilation_unit)
self._core.import_directives.append(import_directive) self._compilation_unit.import_directives.append(import_directive)
elif top_level_data[self.get_key()] == "StructDefinition": elif top_level_data[self.get_key()] == "StructDefinition":
st = StructureTopLevel() st = StructureTopLevel()
st.set_offset(top_level_data["src"], self._core) st.set_offset(top_level_data["src"], self._compilation_unit)
st_parser = StructureTopLevelSolc(st, top_level_data, self) st_parser = StructureTopLevelSolc(st, top_level_data, self)
self._core.structures_top_level.append(st) self._compilation_unit.structures_top_level.append(st)
self._structures_top_level_parser.append(st_parser) self._structures_top_level_parser.append(st_parser)
elif top_level_data[self.get_key()] == "EnumDefinition": elif top_level_data[self.get_key()] == "EnumDefinition":
@ -207,15 +205,15 @@ class SlitherSolc:
elif top_level_data[self.get_key()] == "VariableDeclaration": elif top_level_data[self.get_key()] == "VariableDeclaration":
var = TopLevelVariable() var = TopLevelVariable()
var_parser = TopLevelVariableSolc(var, top_level_data) var_parser = TopLevelVariableSolc(var, top_level_data)
var.set_offset(top_level_data["src"], self._core) var.set_offset(top_level_data["src"], self._compilation_unit)
self._core.variables_top_level.append(var) self._compilation_unit.variables_top_level.append(var)
self._variables_top_level_parser.append(var_parser) self._variables_top_level_parser.append(var_parser)
elif top_level_data[self.get_key()] == "FunctionDefinition": elif top_level_data[self.get_key()] == "FunctionDefinition":
func = FunctionTopLevel(self.core) func = FunctionTopLevel(self._compilation_unit)
func_parser = FunctionSolc(func, top_level_data, None, self) func_parser = FunctionSolc(func, top_level_data, None, self)
self._core.functions_top_level.append(func) self._compilation_unit.functions_top_level.append(func)
self._functions_top_level_parser.append(func_parser) self._functions_top_level_parser.append(func_parser)
self.add_function_or_modifier_parser(func_parser) self.add_function_or_modifier_parser(func_parser)
@ -246,16 +244,16 @@ class SlitherSolc:
# This works only for crytic compile. # This works only for crytic compile.
# which used --combined-json ast, rather than --ast-json # which used --combined-json ast, rather than --ast-json
# As a result -1 is not used as index # As a result -1 is not used as index
if self._core.crytic_compile is not None: if self._compilation_unit.core.crytic_compile is not None:
sourceUnit = len(self._core.source_code) sourceUnit = len(self._compilation_unit.core.source_code)
self._core.source_units[sourceUnit] = name self._compilation_unit.source_units[sourceUnit] = name
if os.path.isfile(name) and not name in self._core.source_code: if os.path.isfile(name) and not name in self._compilation_unit.core.source_code:
self._core.add_source_code(name) self._compilation_unit.core.add_source_code(name)
else: else:
lib_name = os.path.join("node_modules", name) lib_name = os.path.join("node_modules", name)
if os.path.isfile(lib_name) and not name in self._core.source_code: if os.path.isfile(lib_name) and not name in self._compilation_unit.core.source_code:
self._core.add_source_code(lib_name) self._compilation_unit.core.add_source_code(lib_name)
# endregion # endregion
################################################################################### ###################################################################################
@ -275,7 +273,7 @@ class SlitherSolc:
def parse_contracts(self): # pylint: disable=too-many-statements,too-many-branches def parse_contracts(self): # pylint: disable=too-many-statements,too-many-branches
if not self._underlying_contract_to_parser: if not self._underlying_contract_to_parser:
logger.info( logger.info(
f"No contract were found in {self._core.filename}, check the correct compilation" f"No contract were found in {self._compilation_unit.core.filename}, check the correct compilation"
) )
if self._parsed: if self._parsed:
raise Exception("Contract analysis can be run only once!") raise Exception("Contract analysis can be run only once!")
@ -291,17 +289,17 @@ class SlitherSolc:
"""Your codebase has a contract named 'SlitherInternalTopLevelContract'. """Your codebase has a contract named 'SlitherInternalTopLevelContract'.
Please rename it, this name is reserved for Slither's internals""" Please rename it, this name is reserved for Slither's internals"""
) )
if contract.name in self._core.contracts_as_dict: if contract.name in self._compilation_unit.contracts_as_dict:
if contract.id != self._core.contracts_as_dict[contract.name].id: if contract.id != self._compilation_unit.contracts_as_dict[contract.name].id:
self._core.contract_name_collisions[contract.name].append( self._compilation_unit.contract_name_collisions[contract.name].append(
contract.source_mapping_str contract.source_mapping_str
) )
self._core.contract_name_collisions[contract.name].append( self._compilation_unit.contract_name_collisions[contract.name].append(
self._core.contracts_as_dict[contract.name].source_mapping_str self._compilation_unit.contracts_as_dict[contract.name].source_mapping_str
) )
else: else:
self._contracts_by_id[contract.id] = contract self._contracts_by_id[contract.id] = contract
self._core.contracts_as_dict[contract.name] = contract self._compilation_unit.contracts_as_dict[contract.name] = contract
# Update of the inheritance # Update of the inheritance
for contract_parser in self._underlying_contract_to_parser.values(): for contract_parser in self._underlying_contract_to_parser.values():
@ -316,7 +314,7 @@ Please rename it, this name is reserved for Slither's internals"""
for i in contract_parser.linearized_base_contracts[1:]: for i in contract_parser.linearized_base_contracts[1:]:
if i in contract_parser.remapping: if i in contract_parser.remapping:
ancestors.append( ancestors.append(
self._core.get_contract_from_name(contract_parser.remapping[i]) self._compilation_unit.get_contract_from_name(contract_parser.remapping[i])
) )
elif i in self._contracts_by_id: elif i in self._contracts_by_id:
ancestors.append(self._contracts_by_id[i]) ancestors.append(self._contracts_by_id[i])
@ -326,7 +324,9 @@ Please rename it, this name is reserved for Slither's internals"""
# Resolve immediate base contracts # Resolve immediate base contracts
for i in contract_parser.baseContracts: for i in contract_parser.baseContracts:
if i in contract_parser.remapping: if i in contract_parser.remapping:
fathers.append(self._core.get_contract_from_name(contract_parser.remapping[i])) fathers.append(
self._compilation_unit.get_contract_from_name(contract_parser.remapping[i])
)
elif i in self._contracts_by_id: elif i in self._contracts_by_id:
fathers.append(self._contracts_by_id[i]) fathers.append(self._contracts_by_id[i])
else: else:
@ -336,7 +336,7 @@ Please rename it, this name is reserved for Slither's internals"""
for i in contract_parser.baseConstructorContractsCalled: for i in contract_parser.baseConstructorContractsCalled:
if i in contract_parser.remapping: if i in contract_parser.remapping:
father_constructors.append( father_constructors.append(
self._core.get_contract_from_name(contract_parser.remapping[i]) self._compilation_unit.get_contract_from_name(contract_parser.remapping[i])
) )
elif i in self._contracts_by_id: elif i in self._contracts_by_id:
father_constructors.append(self._contracts_by_id[i]) father_constructors.append(self._contracts_by_id[i])
@ -348,7 +348,7 @@ Please rename it, this name is reserved for Slither's internals"""
) )
if missing_inheritance: if missing_inheritance:
self._core.contracts_with_missing_inheritance.add( self._compilation_unit.contracts_with_missing_inheritance.add(
contract_parser.underlying_contract contract_parser.underlying_contract
) )
contract_parser.log_incorrect_parsing(f"Missing inheritance {contract_parser}") contract_parser.log_incorrect_parsing(f"Missing inheritance {contract_parser}")
@ -390,8 +390,8 @@ Please rename it, this name is reserved for Slither's internals"""
raise SlitherException("Parse the contract before running analyses") raise SlitherException("Parse the contract before running analyses")
self._convert_to_slithir() self._convert_to_slithir()
compute_dependency(self._core) compute_dependency(self._compilation_unit)
self._core.compute_storage_layout() self._compilation_unit.compute_storage_layout()
self._analyzed = True self._analyzed = True
def _analyze_all_enums(self, contracts_to_be_analyzed: List[ContractSolc]): def _analyze_all_enums(self, contracts_to_be_analyzed: List[ContractSolc]):
@ -534,7 +534,7 @@ Please rename it, this name is reserved for Slither's internals"""
def _analyze_params_top_level_function(self): def _analyze_params_top_level_function(self):
for func_parser in self._functions_top_level_parser: for func_parser in self._functions_top_level_parser:
func_parser.analyze_params() func_parser.analyze_params()
self.core.add_function(func_parser.underlying_function) self._compilation_unit.add_function(func_parser.underlying_function)
def _analyze_content_top_level_function(self): def _analyze_content_top_level_function(self):
try: try:
@ -560,7 +560,7 @@ Please rename it, this name is reserved for Slither's internals"""
def _convert_to_slithir(self): def _convert_to_slithir(self):
for contract in self._core.contracts: for contract in self._compilation_unit.contracts:
contract.add_constructor_variables() contract.add_constructor_variables()
for func in contract.functions + contract.modifiers: for func in contract.functions + contract.modifiers:
@ -576,11 +576,11 @@ Please rename it, this name is reserved for Slither's internals"""
contract.convert_expression_to_slithir_ssa() contract.convert_expression_to_slithir_ssa()
for func in self.core.functions_top_level: for func in self._compilation_unit.functions_top_level:
func.generate_slithir_and_analyze() func.generate_slithir_and_analyze()
func.generate_slithir_ssa(dict()) func.generate_slithir_ssa(dict())
self._core.propagate_function_calls() self._compilation_unit.propagate_function_calls()
for contract in self._core.contracts: for contract in self._compilation_unit.contracts:
contract.fix_phi() contract.fix_phi()
contract.update_read_write_using_ssa() contract.update_read_write_using_ssa()

@ -20,6 +20,7 @@ from slither.solc_parsing.exceptions import ParsingError
if TYPE_CHECKING: if TYPE_CHECKING:
from slither.core.declarations import Structure, Enum from slither.core.declarations import Structure, Enum
from slither.core.declarations.contract import Contract from slither.core.declarations.contract import Contract
from slither.core.compilation_unit import SlitherCompilationUnit
logger = logging.getLogger("TypeParsing") logger = logging.getLogger("TypeParsing")
@ -197,19 +198,20 @@ def parse_type(t: Union[Dict, UnknownType], caller_context):
from slither.solc_parsing.variables.function_type_variable import FunctionTypeVariableSolc from slither.solc_parsing.variables.function_type_variable import FunctionTypeVariableSolc
from slither.solc_parsing.declarations.contract import ContractSolc from slither.solc_parsing.declarations.contract import ContractSolc
from slither.solc_parsing.declarations.function import FunctionSolc from slither.solc_parsing.declarations.function import FunctionSolc
from slither.solc_parsing.slitherSolc import SlitherSolc from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc
sl: "SlitherCompilationUnit"
# Note: for convenicence top level functions use the same parser than function in contract # Note: for convenicence top level functions use the same parser than function in contract
# but contract_parser is set to None # but contract_parser is set to None
if isinstance(caller_context, SlitherSolc) or ( if isinstance(caller_context, SlitherCompilationUnitSolc) or (
isinstance(caller_context, FunctionSolc) and caller_context.contract_parser is None isinstance(caller_context, FunctionSolc) and caller_context.contract_parser is None
): ):
if isinstance(caller_context, SlitherSolc): if isinstance(caller_context, SlitherCompilationUnitSolc):
sl = caller_context.core sl = caller_context.compilation_unit
next_context = caller_context next_context = caller_context
else: else:
assert isinstance(caller_context, FunctionSolc) assert isinstance(caller_context, FunctionSolc)
sl = caller_context.underlying_function.slither sl = caller_context.underlying_function.compilation_unit
next_context = caller_context.slither_parser next_context = caller_context.slither_parser
structures_direct_access = sl.structures_top_level structures_direct_access = sl.structures_top_level
all_structuress = [c.structures for c in sl.contracts] all_structuress = [c.structures for c in sl.contracts]
@ -233,15 +235,17 @@ def parse_type(t: Union[Dict, UnknownType], caller_context):
contract = caller_context.underlying_contract contract = caller_context.underlying_contract
next_context = caller_context next_context = caller_context
structures_direct_access = contract.structures + contract.slither.structures_top_level structures_direct_access = (
all_structuress = [c.structures for c in contract.slither.contracts] contract.structures + contract.compilation_unit.structures_top_level
)
all_structuress = [c.structures for c in contract.compilation_unit.contracts]
all_structures = [item for sublist in all_structuress for item in sublist] all_structures = [item for sublist in all_structuress for item in sublist]
all_structures += contract.slither.structures_top_level all_structures += contract.compilation_unit.structures_top_level
enums_direct_access = contract.enums + contract.slither.enums_top_level enums_direct_access = contract.enums + contract.compilation_unit.enums_top_level
all_enumss = [c.enums for c in contract.slither.contracts] all_enumss = [c.enums for c in contract.compilation_unit.contracts]
all_enums = [item for sublist in all_enumss for item in sublist] all_enums = [item for sublist in all_enumss for item in sublist]
all_enums += contract.slither.enums_top_level all_enums += contract.compilation_unit.enums_top_level
contracts = contract.slither.contracts contracts = contract.compilation_unit.contracts
functions = contract.functions + contract.modifiers functions = contract.functions + contract.modifiers
else: else:
raise ParsingError(f"Incorrect caller context: {type(caller_context)}") raise ParsingError(f"Incorrect caller context: {type(caller_context)}")
@ -353,7 +357,7 @@ def parse_type(t: Union[Dict, UnknownType], caller_context):
return_values_vars: List[FunctionTypeVariable] = [] return_values_vars: List[FunctionTypeVariable] = []
for p in params[index]: for p in params[index]:
var = FunctionTypeVariable() var = FunctionTypeVariable()
var.set_offset(p["src"], caller_context.slither) var.set_offset(p["src"], caller_context.compilation_unit)
var_parser = FunctionTypeVariableSolc(var, p) var_parser = FunctionTypeVariableSolc(var, p)
var_parser.analyze(caller_context) var_parser.analyze(caller_context)
@ -361,7 +365,7 @@ def parse_type(t: Union[Dict, UnknownType], caller_context):
params_vars.append(var) params_vars.append(var)
for p in return_values[index]: for p in return_values[index]:
var = FunctionTypeVariable() var = FunctionTypeVariable()
var.set_offset(p["src"], caller_context.slither) var.set_offset(p["src"], caller_context.compilation_unit)
var_parser = FunctionTypeVariableSolc(var, p) var_parser = FunctionTypeVariableSolc(var, p)
var_parser.analyze(caller_context) var_parser.analyze(caller_context)

@ -3,6 +3,7 @@ import json
from typing import Optional, Dict, List, Union from typing import Optional, Dict, List, Union
from slither.core.cfg.node import NodeType, Node, link_nodes from slither.core.cfg.node import NodeType, Node, link_nodes
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.core.declarations import ( from slither.core.declarations import (
Function, Function,
SolidityFunction, SolidityFunction,
@ -20,7 +21,6 @@ from slither.core.expressions import (
UnaryOperation, UnaryOperation,
) )
from slither.core.expressions.expression import Expression from slither.core.expressions.expression import Expression
from slither.core.slither_core import SlitherCore
from slither.core.solidity_types import ElementaryType from slither.core.solidity_types import ElementaryType
from slither.core.variables.local_variable import LocalVariable from slither.core.variables.local_variable import LocalVariable
from slither.exceptions import SlitherException from slither.exceptions import SlitherException
@ -66,7 +66,9 @@ class YulNode:
AssignmentOperationType.ASSIGN, AssignmentOperationType.ASSIGN,
self._node.variable_declaration.type, self._node.variable_declaration.type,
) )
_expression.set_offset(self._node.expression.source_mapping, self._node.slither) _expression.set_offset(
self._node.expression.source_mapping, self._node.compilation_unit
)
self._node.add_expression(_expression, bypass_verif_empty=True) self._node.add_expression(_expression, bypass_verif_empty=True)
expression = self._node.expression expression = self._node.expression
@ -132,8 +134,8 @@ class YulScope(metaclass=abc.ABCMeta):
return self._contract return self._contract
@property @property
def slither(self) -> SlitherCore: def compilation_unit(self) -> SlitherCompilationUnit:
return self._contract.slither return self._contract.compilation_unit
@property @property
def parent_func(self) -> Optional[Function]: def parent_func(self) -> Optional[Function]:
@ -182,7 +184,7 @@ class YulLocalVariable: # pylint: disable=too-few-public-methods
# start initializing the underlying variable # start initializing the underlying variable
var.set_function(root.function) var.set_function(root.function)
var.set_offset(ast["src"], root.slither) var.set_offset(ast["src"], root.compilation_unit)
var.name = _name_to_yul_name(ast["name"], root.id) var.name = _name_to_yul_name(ast["name"], root.id)
var.set_type(ElementaryType("uint256")) var.set_type(ElementaryType("uint256"))
@ -209,9 +211,9 @@ class YulFunction(YulScope):
func.name = ast["name"] func.name = ast["name"]
func.set_visibility("private") func.set_visibility("private")
func.set_offset(ast["src"], root.slither) func.set_offset(ast["src"], root.compilation_unit)
func.set_contract(root.contract) func.set_contract(root.contract)
func.slither = root.slither func.compilation_unit = root.compilation_unit
func.set_contract_declarer(root.contract) func.set_contract_declarer(root.contract)
func.scope = root.id func.scope = root.id
func.is_implemented = True func.is_implemented = True
@ -226,10 +228,6 @@ class YulFunction(YulScope):
def underlying(self) -> Function: def underlying(self) -> Function:
return self._function return self._function
@property
def slither(self) -> SlitherCore:
return self._entrypoint.underlying_node.slither
@property @property
def function(self) -> Function: def function(self) -> Function:
return self._function return self._function
@ -336,11 +334,11 @@ def convert_yul_block(root: YulScope, parent: YulNode, ast: Dict) -> YulNode:
def convert_yul_function_definition(root: YulScope, parent: YulNode, ast: Dict) -> YulNode: def convert_yul_function_definition(root: YulScope, parent: YulNode, ast: Dict) -> YulNode:
func = FunctionContract(root.slither) func = FunctionContract(root.compilation_unit)
yul_function = YulFunction(func, root, ast) yul_function = YulFunction(func, root, ast)
root.contract.add_function(func) root.contract.add_function(func)
root.slither.add_function(func) root.compilation_unit.add_function(func)
root.add_yul_local_function(yul_function) root.add_yul_local_function(yul_function)
yul_function.convert_body() yul_function.convert_body()
@ -739,7 +737,7 @@ def parse_yul_unsupported(_root: YulScope, _node: YulNode, ast: Dict) -> Optiona
def parse_yul(root: YulScope, node: YulNode, ast: Dict) -> Optional[Expression]: def parse_yul(root: YulScope, node: YulNode, ast: Dict) -> Optional[Expression]:
op = parsers.get(ast["nodeType"], parse_yul_unsupported)(root, node, ast) op = parsers.get(ast["nodeType"], parse_yul_unsupported)(root, node, ast)
if op: if op:
op.set_offset(ast["src"], root.slither) op.set_offset(ast["src"], root.compilation_unit)
return op return op

@ -79,12 +79,13 @@ def main():
if args.erc.upper() in ERCS: if args.erc.upper() in ERCS:
contract = slither.get_contract_from_name(args.contract_name) contracts = slither.get_contract_from_name(args.contract_name)
if not contract: if len(contracts) != 1:
err = f"Contract not found: {args.contract_name}" err = f"Contract not found: {args.contract_name}"
_log_error(err, args) _log_error(err, args)
return return
contract = contracts[0]
# First elem is the function, second is the event # First elem is the function, second is the event
erc = ERCS[args.erc.upper()] erc = ERCS[args.erc.upper()]
generic_erc_checks(contract, erc[0], erc[1], ret) generic_erc_checks(contract, erc[0], erc[1], ret)

@ -7,6 +7,7 @@ from typing import List, Set, Dict, Optional
from slither.core.declarations import SolidityFunction, EnumContract, StructureContract from slither.core.declarations import SolidityFunction, EnumContract, StructureContract
from slither.core.declarations.contract import Contract from slither.core.declarations.contract import Contract
from slither.core.slither_core import SlitherCore
from slither.core.solidity_types import MappingType, ArrayType from slither.core.solidity_types import MappingType, ArrayType
from slither.core.solidity_types.user_defined_type import UserDefinedType from slither.core.solidity_types.user_defined_type import UserDefinedType
from slither.exceptions import SlitherException from slither.exceptions import SlitherException
@ -43,7 +44,7 @@ class Flattening:
# pylint: disable=too-many-instance-attributes,too-many-arguments,too-many-locals,too-few-public-methods # pylint: disable=too-many-instance-attributes,too-many-arguments,too-many-locals,too-few-public-methods
def __init__( def __init__(
self, self,
slither, slither: SlitherCore,
external_to_public=False, external_to_public=False,
remove_assert=False, remove_assert=False,
private_to_internal=False, private_to_internal=False,
@ -51,7 +52,7 @@ class Flattening:
pragma_solidity: Optional[str] = None, pragma_solidity: Optional[str] = None,
): ):
self._source_codes: Dict[Contract, str] = {} self._source_codes: Dict[Contract, str] = {}
self._slither = slither self._slither: SlitherCore = slither
self._external_to_public = external_to_public self._external_to_public = external_to_public
self._remove_assert = remove_assert self._remove_assert = remove_assert
self._use_abi_encoder_v2 = False self._use_abi_encoder_v2 = False
@ -71,10 +72,11 @@ class Flattening:
Set _use_abi_encorder_v2 Set _use_abi_encorder_v2
:return: :return:
""" """
for p in self._slither.pragma_directives: for compilation_unit in self._slither.compilation_units:
if "ABIEncoderV2" in str(p.directive): for p in compilation_unit.pragma_directives:
self._use_abi_encoder_v2 = True if "ABIEncoderV2" in str(p.directive):
return self._use_abi_encoder_v2 = True
return
def _get_source_code( def _get_source_code(
self, contract: Contract self, contract: Contract
@ -187,8 +189,9 @@ class Flattening:
ret = "" ret = ""
if self._pragma_solidity: if self._pragma_solidity:
ret += f"pragma solidity {self._pragma_solidity};\n" ret += f"pragma solidity {self._pragma_solidity};\n"
elif self._slither.solc_version: else:
ret += f"pragma solidity {self._slither.solc_version};\n" # TODO support multiple compiler version
ret += f"pragma solidity {list(self._slither.crytic_compile.compilation_units.values())[0].compiler_version.version};\n"
if self._use_abi_encoder_v2: if self._use_abi_encoder_v2:
ret += "pragma experimental ABIEncoderV2;\n" ret += "pragma experimental ABIEncoderV2;\n"
@ -338,10 +341,11 @@ class Flattening:
elif strategy == Strategy.LocalImport: elif strategy == Strategy.LocalImport:
exports = self._export_with_import() exports = self._export_with_import()
else: else:
contract = self._slither.get_contract_from_name(target) contracts = self._slither.get_contract_from_name(target)
if contract is None: if len(contracts) != 1:
logger.error(f"{target} not found") logger.error(f"{target} not found")
return return
contract = contracts[0]
exports = [self._export_contract_with_inheritance(contract)] exports = [self._export_contract_with_inheritance(contract)]
if json: if json:

@ -9,5 +9,9 @@ def kspec_coverage(args):
slither = Slither(contract, **vars(args)) slither = Slither(contract, **vars(args))
compilation_units = slither.compilation_units
if len(compilation_units) != 1:
print("Only single compilation unit supported")
return
# Run the analysis on the Klab specs # Run the analysis on the Klab specs
run_analysis(args, slither, kspec) run_analysis(args, compilation_units[0], kspec)

@ -17,7 +17,7 @@ def remove_assignement(variable: Variable, contract: Contract, result: Dict):
# Retrieve the file # Retrieve the file
in_file = contract.source_mapping["filename_absolute"] in_file = contract.source_mapping["filename_absolute"]
# Retrieve the source code # Retrieve the source code
in_file_str = contract.slither.source_code[in_file] in_file_str = contract.compilation_unit.core.source_code[in_file]
# Get the string # Get the string
start = variable.source_mapping["start"] start = variable.source_mapping["start"]

@ -10,12 +10,12 @@ def resolve_function(slither, contract_name, function_name):
:return: Returns the resolved function, raises an exception otherwise. :return: Returns the resolved function, raises an exception otherwise.
""" """
# Obtain the target contract # Obtain the target contract
contract = slither.get_contract_from_name(contract_name) contracts = slither.get_contract_from_name(contract_name)
# Verify the contract was resolved successfully # Verify the contract was resolved successfully
if contract is None: if len(contracts) != 1:
raise ResolveFunctionException(f"Could not resolve target contract: {contract_name}") raise ResolveFunctionException(f"Could not resolve target contract: {contract_name}")
contract = contracts[0]
# Obtain the target function # Obtain the target function
target_function = next( target_function = next(
(function for function in contract.functions if function.name == function_name), (function for function in contract.functions if function.name == function_name),

@ -126,8 +126,8 @@ def main():
# Perform slither analysis on the given filename # Perform slither analysis on the given filename
slither = Slither(args.filename, **vars(args)) slither = Slither(args.filename, **vars(args))
contract = slither.get_contract_from_name(args.contract) contracts = slither.get_contract_from_name(args.contract)
if not contract: if len(contracts) != 1:
if len(slither.contracts) == 1: if len(slither.contracts) == 1:
contract = slither.contracts[0] contract = slither.contracts[0]
else: else:
@ -137,6 +137,8 @@ def main():
to_log = f"{args.contract} not found" to_log = f"{args.contract} not found"
logger.error(to_log) logger.error(to_log)
return return
else:
contract = contracts[0]
addresses = Addresses(args.address_owner, args.address_user, args.address_attacker) addresses = Addresses(args.address_owner, args.address_user, args.address_attacker)

@ -69,14 +69,16 @@ def generate_erc20(
:param type_property: One of ERC20_PROPERTIES.keys() :param type_property: One of ERC20_PROPERTIES.keys()
:return: :return:
""" """
if contract.slither.crytic_compile is None: if contract.compilation_unit.core.crytic_compile is None:
logging.error("Please compile with crytic-compile") logging.error("Please compile with crytic-compile")
return return
if contract.slither.crytic_compile.type not in [ if contract.compilation_unit.core.crytic_compile.type not in [
PlatformType.TRUFFLE, PlatformType.TRUFFLE,
PlatformType.SOLC, PlatformType.SOLC,
]: ]:
logging.error(f"{contract.slither.crytic_compile.type} not yet supported by slither-prop") logging.error(
f"{contract.compilation_unit.core.crytic_compile.type} not yet supported by slither-prop"
)
return return
# Check if the contract is an ERC20 contract and if the functions have the correct visibility # Check if the contract is an ERC20 contract and if the functions have the correct visibility
@ -92,7 +94,7 @@ def generate_erc20(
properties = erc_properties.properties properties = erc_properties.properties
# Generate the output directory # Generate the output directory
output_dir = _platform_to_output_dir(contract.slither.crytic_compile.platform) output_dir = _platform_to_output_dir(contract.compilation_unit.core.crytic_compile.platform)
output_dir.mkdir(exist_ok=True) output_dir.mkdir(exist_ok=True)
# Get the properties # Get the properties
@ -116,13 +118,13 @@ def generate_erc20(
# Generate Echidna config file # Generate Echidna config file
echidna_config_filename = generate_echidna_config( echidna_config_filename = generate_echidna_config(
Path(contract.slither.crytic_compile.target).parent, addresses Path(contract.compilation_unit.core.crytic_compile.target).parent, addresses
) )
unit_test_info = "" unit_test_info = ""
# If truffle, generate unit tests # If truffle, generate unit tests
if contract.slither.crytic_compile.type == PlatformType.TRUFFLE: if contract.compilation_unit.core.crytic_compile.type == PlatformType.TRUFFLE:
unit_test_info = generate_truffle_test(contract, type_property, unit_tests, addresses) unit_test_info = generate_truffle_test(contract, type_property, unit_tests, addresses)
logger.info("################################################") logger.info("################################################")
@ -132,7 +134,7 @@ def generate_erc20(
logger.info(green(unit_test_info)) logger.info(green(unit_test_info))
logger.info(green("To run Echidna:")) logger.info(green("To run Echidna:"))
txt = f"\t echidna-test {contract.slither.crytic_compile.target} " txt = f"\t echidna-test {contract.compilation_unit.core.crytic_compile.target} "
txt += f"--contract {contract_name} --config {echidna_config_filename}" txt += f"--contract {contract_name} --config {echidna_config_filename}"
logger.info(green(txt)) logger.info(green(txt))
@ -193,7 +195,7 @@ def _check_compatibility(contract):
def _get_properties(contract, properties: List[Property]) -> Tuple[str, List[Property]]: def _get_properties(contract, properties: List[Property]) -> Tuple[str, List[Property]]:
solidity_properties = "" solidity_properties = ""
if contract.slither.crytic_compile.type == PlatformType.TRUFFLE: if contract.compilation_unit.crytic_compile.type == PlatformType.TRUFFLE:
solidity_properties += "\n".join([property_to_solidity(p) for p in ERC20_CONFIG]) solidity_properties += "\n".join([property_to_solidity(p) for p in ERC20_CONFIG])
solidity_properties += "\n".join([property_to_solidity(p) for p in properties]) solidity_properties += "\n".join([property_to_solidity(p) for p in properties])

@ -24,7 +24,7 @@ def generate_truffle_test(
filename_init = f"Initialization{test_contract}.js" filename_init = f"Initialization{test_contract}.js"
filename = f"{test_contract}.js" filename = f"{test_contract}.js"
output_dir = Path(contract.slither.crytic_compile.target) output_dir = Path(contract.compilation_unit.core.crytic_compile.target)
generate_migration(test_contract, output_dir, addresses.owner) generate_migration(test_contract, output_dir, addresses.owner)

@ -185,13 +185,14 @@ def main():
# Analyze logic contract # Analyze logic contract
v1_name = args.ContractName v1_name = args.ContractName
v1_contract = variable1.get_contract_from_name(v1_name) v1_contracts = variable1.get_contract_from_name(v1_name)
if v1_contract is None: if len(v1_contracts) != 1:
info = "Contract {} not found in {}".format(v1_name, variable1.filename) info = "Contract {} not found in {}".format(v1_name, variable1.filename)
logger.error(red(info)) logger.error(red(info))
if args.json: if args.json:
output_to_json(args.json, str(info), json_results) output_to_json(args.json, str(info), json_results)
return return
v1_contract = v1_contracts[0]
detectors_results, number_detectors = _checks_on_contract(detectors, v1_contract) detectors_results, number_detectors = _checks_on_contract(detectors, v1_contract)
json_results["detectors"] += detectors_results json_results["detectors"] += detectors_results
@ -205,13 +206,14 @@ def main():
else: else:
proxy = variable1 proxy = variable1
proxy_contract = proxy.get_contract_from_name(args.proxy_name) proxy_contracts = proxy.get_contract_from_name(args.proxy_name)
if proxy_contract is None: if len(proxy_contracts) != 1:
info = "Proxy {} not found in {}".format(args.proxy_name, proxy.filename) info = "Proxy {} not found in {}".format(args.proxy_name, proxy.filename)
logger.error(red(info)) logger.error(red(info))
if args.json: if args.json:
output_to_json(args.json, str(info), json_results) output_to_json(args.json, str(info), json_results)
return return
proxy_contract = proxy_contracts[0]
json_results["proxy-present"] = True json_results["proxy-present"] = True
detectors_results, number_detectors = _checks_on_contract_and_proxy( detectors_results, number_detectors = _checks_on_contract_and_proxy(
@ -226,8 +228,8 @@ def main():
else: else:
variable2 = variable1 variable2 = variable1
v2_contract = variable2.get_contract_from_name(args.new_contract_name) v2_contracts = variable2.get_contract_from_name(args.new_contract_name)
if v2_contract is None: if len(v2_contracts) != 1:
info = "New logic contract {} not found in {}".format( info = "New logic contract {} not found in {}".format(
args.new_contract_name, variable2.filename args.new_contract_name, variable2.filename
) )
@ -235,6 +237,7 @@ def main():
if args.json: if args.json:
output_to_json(args.json, str(info), json_results) output_to_json(args.json, str(info), json_results)
return return
v2_contract = v2_contracts[0]
json_results["contract_v2-present"] = True json_results["contract_v2-present"] = True
if proxy_contract: if proxy_contract:

@ -138,7 +138,9 @@ class AbstractCheck(metaclass=abc.ABCMeta):
return all_results return all_results
def generate_result(self, info, additional_fields=None): def generate_result(self, info, additional_fields=None):
output = Output(info, additional_fields, markdown_root=self.contract.slither.markdown_root) output = Output(
info, additional_fields, markdown_root=self.contract.compilation_unit.core.markdown_root
)
output.data["check"] = self.ARGUMENT output.data["check"] = self.ARGUMENT

@ -55,7 +55,7 @@ Consider using a `Initializable` contract to follow [standard practice](https://
""" """
def _check(self): def _check(self):
initializable = self.contract.slither.get_contract_from_name("Initializable") initializable = self.contract.compilation_unit.get_contract_from_name("Initializable")
if initializable is None: if initializable is None:
info = [ info = [
"Initializable contract not found, the contract does not follow a standard initalization schema.\n" "Initializable contract not found, the contract does not follow a standard initalization schema.\n"
@ -84,7 +84,7 @@ Review manually the contract's initialization. Consider inheriting `Initializabl
REQUIRE_CONTRACT = True REQUIRE_CONTRACT = True
def _check(self): def _check(self):
initializable = self.contract.slither.get_contract_from_name("Initializable") initializable = self.contract.compilation_unit.get_contract_from_name("Initializable")
# See InitializablePresent # See InitializablePresent
if initializable is None: if initializable is None:
return [] return []
@ -114,7 +114,7 @@ Review manually the contract's initialization. Consider inheriting a `Initializa
REQUIRE_CONTRACT = True REQUIRE_CONTRACT = True
def _check(self): def _check(self):
initializable = self.contract.slither.get_contract_from_name("Initializable") initializable = self.contract.compilation_unit.get_contract_from_name("Initializable")
# See InitializablePresent # See InitializablePresent
if initializable is None: if initializable is None:
return [] return []
@ -160,7 +160,7 @@ Use `Initializable.initializer()`.
REQUIRE_CONTRACT = True REQUIRE_CONTRACT = True
def _check(self): def _check(self):
initializable = self.contract.slither.get_contract_from_name("Initializable") initializable = self.contract.compilation_unit.get_contract_from_name("Initializable")
# See InitializablePresent # See InitializablePresent
if initializable is None: if initializable is None:
return [] return []

@ -4,7 +4,7 @@ import json
import logging import logging
import zipfile import zipfile
from collections import OrderedDict from collections import OrderedDict
from typing import Optional, Dict, List, Union, Any from typing import Optional, Dict, List, Union, Any, TYPE_CHECKING
from zipfile import ZipFile from zipfile import ZipFile
from slither.core.cfg.node import Node from slither.core.cfg.node import Node
@ -15,6 +15,9 @@ from slither.exceptions import SlitherError
from slither.utils.colors import yellow from slither.utils.colors import yellow
from slither.utils.myprettytable import MyPrettyTable from slither.utils.myprettytable import MyPrettyTable
if TYPE_CHECKING:
from slither.core.compilation_unit import SlitherCompilationUnit
logger = logging.getLogger("Slither") logger = logging.getLogger("Slither")
@ -508,7 +511,7 @@ class Output:
self, self,
name: str, name: str,
source_mapping, source_mapping,
slither, compilation_unit: "SlitherCompilationUnit",
additional_fields: Optional[Dict] = None, additional_fields: Optional[Dict] = None,
): ):
# If this a tuple with (filename, start, end), convert it to a source mapping. # If this a tuple with (filename, start, end), convert it to a source mapping.
@ -523,7 +526,7 @@ class Output:
for ( for (
source_unit_id, source_unit_id,
source_unit_filename, source_unit_filename,
) in slither.source_units.items() ) in compilation_unit.source_units.items()
if source_unit_filename == filename if source_unit_filename == filename
), ),
-1, -1,
@ -536,7 +539,7 @@ class Output:
if isinstance(source_mapping, str): if isinstance(source_mapping, str):
source_mapping_str = source_mapping source_mapping_str = source_mapping
source_mapping = SourceMapping() source_mapping = SourceMapping()
source_mapping.set_offset(source_mapping_str, slither) source_mapping.set_offset(source_mapping_str, compilation_unit)
# If this is a source mapping object, get the underlying source mapping dictionary # If this is a source mapping object, get the underlying source mapping dictionary
if isinstance(source_mapping, SourceMapping): if isinstance(source_mapping, SourceMapping):

@ -68,10 +68,10 @@
} }
} }
], ],
"description": "Contract locking ether found in :\n\tContract OnlyLocked (tests/detectors/locked-ether/0.4.25/locked_ether.sol#26) has payable functions:\n\t - Locked.receive() (tests/detectors/locked-ether/0.4.25/locked_ether.sol#4-6)\n\tBut does not have a function to withdraw the ether\n", "description": "Contract locking ether found:\n\tContract OnlyLocked (tests/detectors/locked-ether/0.4.25/locked_ether.sol#26) has payable functions:\n\t - Locked.receive() (tests/detectors/locked-ether/0.4.25/locked_ether.sol#4-6)\n\tBut does not have a function to withdraw the ether\n",
"markdown": "Contract locking ether found in :\n\tContract [OnlyLocked](tests/detectors/locked-ether/0.4.25/locked_ether.sol#L26) has payable functions:\n\t - [Locked.receive()](tests/detectors/locked-ether/0.4.25/locked_ether.sol#L4-L6)\n\tBut does not have a function to withdraw the ether\n", "markdown": "Contract locking ether found:\n\tContract [OnlyLocked](tests/detectors/locked-ether/0.4.25/locked_ether.sol#L26) has payable functions:\n\t - [Locked.receive()](tests/detectors/locked-ether/0.4.25/locked_ether.sol#L4-L6)\n\tBut does not have a function to withdraw the ether\n",
"first_markdown_element": "tests/detectors/locked-ether/0.4.25/locked_ether.sol#L26", "first_markdown_element": "tests/detectors/locked-ether/0.4.25/locked_ether.sol#L26",
"id": "38b2d47301e59ffa598ec48a8e874e6a7070d6cf4e4ab2909f33a8b72fc28a4b", "id": "a2fad0e9c8a75e55b8c548dd184dc33d78142ea5bac9c36cdc5eb174e56d689c",
"check": "locked-ether", "check": "locked-ether",
"impact": "Medium", "impact": "Medium",
"confidence": "High" "confidence": "High"

@ -68,10 +68,10 @@
} }
} }
], ],
"description": "Contract locking ether found in :\n\tContract OnlyLocked (tests/detectors/locked-ether/0.5.16/locked_ether.sol#26) has payable functions:\n\t - Locked.receive() (tests/detectors/locked-ether/0.5.16/locked_ether.sol#4-6)\n\tBut does not have a function to withdraw the ether\n", "description": "Contract locking ether found:\n\tContract OnlyLocked (tests/detectors/locked-ether/0.5.16/locked_ether.sol#26) has payable functions:\n\t - Locked.receive() (tests/detectors/locked-ether/0.5.16/locked_ether.sol#4-6)\n\tBut does not have a function to withdraw the ether\n",
"markdown": "Contract locking ether found in :\n\tContract [OnlyLocked](tests/detectors/locked-ether/0.5.16/locked_ether.sol#L26) has payable functions:\n\t - [Locked.receive()](tests/detectors/locked-ether/0.5.16/locked_ether.sol#L4-L6)\n\tBut does not have a function to withdraw the ether\n", "markdown": "Contract locking ether found:\n\tContract [OnlyLocked](tests/detectors/locked-ether/0.5.16/locked_ether.sol#L26) has payable functions:\n\t - [Locked.receive()](tests/detectors/locked-ether/0.5.16/locked_ether.sol#L4-L6)\n\tBut does not have a function to withdraw the ether\n",
"first_markdown_element": "tests/detectors/locked-ether/0.5.16/locked_ether.sol#L26", "first_markdown_element": "tests/detectors/locked-ether/0.5.16/locked_ether.sol#L26",
"id": "38b2d47301e59ffa598ec48a8e874e6a7070d6cf4e4ab2909f33a8b72fc28a4b", "id": "a2fad0e9c8a75e55b8c548dd184dc33d78142ea5bac9c36cdc5eb174e56d689c",
"check": "locked-ether", "check": "locked-ether",
"impact": "Medium", "impact": "Medium",
"confidence": "High" "confidence": "High"

@ -68,10 +68,10 @@
} }
} }
], ],
"description": "Contract locking ether found in :\n\tContract OnlyLocked (tests/detectors/locked-ether/0.6.11/locked_ether.sol#26) has payable functions:\n\t - Locked.receive_eth() (tests/detectors/locked-ether/0.6.11/locked_ether.sol#4-6)\n\tBut does not have a function to withdraw the ether\n", "description": "Contract locking ether found:\n\tContract OnlyLocked (tests/detectors/locked-ether/0.6.11/locked_ether.sol#26) has payable functions:\n\t - Locked.receive_eth() (tests/detectors/locked-ether/0.6.11/locked_ether.sol#4-6)\n\tBut does not have a function to withdraw the ether\n",
"markdown": "Contract locking ether found in :\n\tContract [OnlyLocked](tests/detectors/locked-ether/0.6.11/locked_ether.sol#L26) has payable functions:\n\t - [Locked.receive_eth()](tests/detectors/locked-ether/0.6.11/locked_ether.sol#L4-L6)\n\tBut does not have a function to withdraw the ether\n", "markdown": "Contract locking ether found:\n\tContract [OnlyLocked](tests/detectors/locked-ether/0.6.11/locked_ether.sol#L26) has payable functions:\n\t - [Locked.receive_eth()](tests/detectors/locked-ether/0.6.11/locked_ether.sol#L4-L6)\n\tBut does not have a function to withdraw the ether\n",
"first_markdown_element": "tests/detectors/locked-ether/0.6.11/locked_ether.sol#L26", "first_markdown_element": "tests/detectors/locked-ether/0.6.11/locked_ether.sol#L26",
"id": "1eb31cab3df894265d213a6a2e85c7fe840a4021a598ab09f1052a86c897d886", "id": "1818e759217cfcf6787001194bba24ad45470b123962a72d39062edb19447022",
"check": "locked-ether", "check": "locked-ether",
"impact": "Medium", "impact": "Medium",
"confidence": "High" "confidence": "High"

@ -68,10 +68,10 @@
} }
} }
], ],
"description": "Contract locking ether found in :\n\tContract OnlyLocked (tests/detectors/locked-ether/0.7.6/locked_ether.sol#26) has payable functions:\n\t - Locked.receive_eth() (tests/detectors/locked-ether/0.7.6/locked_ether.sol#4-6)\n\tBut does not have a function to withdraw the ether\n", "description": "Contract locking ether found:\n\tContract OnlyLocked (tests/detectors/locked-ether/0.7.6/locked_ether.sol#26) has payable functions:\n\t - Locked.receive_eth() (tests/detectors/locked-ether/0.7.6/locked_ether.sol#4-6)\n\tBut does not have a function to withdraw the ether\n",
"markdown": "Contract locking ether found in :\n\tContract [OnlyLocked](tests/detectors/locked-ether/0.7.6/locked_ether.sol#L26) has payable functions:\n\t - [Locked.receive_eth()](tests/detectors/locked-ether/0.7.6/locked_ether.sol#L4-L6)\n\tBut does not have a function to withdraw the ether\n", "markdown": "Contract locking ether found:\n\tContract [OnlyLocked](tests/detectors/locked-ether/0.7.6/locked_ether.sol#L26) has payable functions:\n\t - [Locked.receive_eth()](tests/detectors/locked-ether/0.7.6/locked_ether.sol#L4-L6)\n\tBut does not have a function to withdraw the ether\n",
"first_markdown_element": "tests/detectors/locked-ether/0.7.6/locked_ether.sol#L26", "first_markdown_element": "tests/detectors/locked-ether/0.7.6/locked_ether.sol#L26",
"id": "1eb31cab3df894265d213a6a2e85c7fe840a4021a598ab09f1052a86c897d886", "id": "1818e759217cfcf6787001194bba24ad45470b123962a72d39062edb19447022",
"check": "locked-ether", "check": "locked-ether",
"impact": "Medium", "impact": "Medium",
"confidence": "High" "confidence": "High"

@ -55,10 +55,10 @@
} }
} }
], ],
"description": "Different versions of Solidity is used in :\n\t- Version used: ['^0.4.24', '^0.4.25']\n\t- ^0.4.24 (tests/detectors/pragma/0.4.25/pragma.0.4.24.sol#1)\n\t- ^0.4.25 (tests/detectors/pragma/0.4.25/pragma.0.4.25.sol#1)\n", "description": "Different versions of Solidity is used:\n\t- Version used: ['^0.4.24', '^0.4.25']\n\t- ^0.4.24 (tests/detectors/pragma/0.4.25/pragma.0.4.24.sol#1)\n\t- ^0.4.25 (tests/detectors/pragma/0.4.25/pragma.0.4.25.sol#1)\n",
"markdown": "Different versions of Solidity is used in :\n\t- Version used: ['^0.4.24', '^0.4.25']\n\t- [^0.4.24](tests/detectors/pragma/0.4.25/pragma.0.4.24.sol#L1)\n\t- [^0.4.25](tests/detectors/pragma/0.4.25/pragma.0.4.25.sol#L1)\n", "markdown": "Different versions of Solidity is used:\n\t- Version used: ['^0.4.24', '^0.4.25']\n\t- [^0.4.24](tests/detectors/pragma/0.4.25/pragma.0.4.24.sol#L1)\n\t- [^0.4.25](tests/detectors/pragma/0.4.25/pragma.0.4.25.sol#L1)\n",
"first_markdown_element": "tests/detectors/pragma/0.4.25/pragma.0.4.24.sol#L1", "first_markdown_element": "tests/detectors/pragma/0.4.25/pragma.0.4.24.sol#L1",
"id": "54735fb882b1f9775c73514464a814ee233d22cfb9eb38621a7fd5a80b93deca", "id": "4c6393f838949def61fef871f6771fee22a8783f10fcc084c78287fd2f1d60ed",
"check": "pragma", "check": "pragma",
"impact": "Informational", "impact": "Informational",
"confidence": "High" "confidence": "High"

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

Loading…
Cancel
Save