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_EDITORCONFIG: false
VALIDATE_JSCPD: false
VALIDATE_PYTHON_MYPY: false
SHELLCHECK_OPTS: "-e SC1090"

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

@ -13,8 +13,9 @@ if len(sys.argv) != 2:
slither = Slither(sys.argv[1])
contract = slither.get_contract_from_name("Simple")
assert contract
contracts = slither.get_contract_from_name("Simple")
assert len(contracts) == 1
contract = contracts[0]
destination = contract.get_state_variable_from_name("destination")
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)))
assert is_tainted(destination, contract)
contract = slither.get_contract_from_name("Reference")
assert contract
contracts = slither.get_contract_from_name("Reference")
assert len(contracts) == 1
contract = contracts[0]
destination = contract.get_state_variable_from_name("destination")
assert destination
source = contract.get_state_variable_from_name("source")
@ -73,8 +75,9 @@ assert is_tainted(destination_indirect_2, contract)
print("SolidityVar contract")
contract = slither.get_contract_from_name("SolidityVar")
assert contract
contracts = slither.get_contract_from_name("SolidityVar")
assert len(contracts) == 1
contract = contracts[0]
addr_1 = contract.get_state_variable_from_name("addr_1")
assert addr_1
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")
contract = slither.get_contract_from_name("Intermediate")
assert contract
contracts = slither.get_contract_from_name("Intermediate")
assert len(contracts) == 1
contract = contracts[0]
destination = contract.get_state_variable_from_name("destination")
assert destination
source = contract.get_state_variable_from_name("source")
@ -106,8 +110,10 @@ print(
assert is_dependent(destination, source, contract)
print("Base Derived contract")
contract = slither.get_contract_from_name("Base")
contract_derived = slither.get_contract_from_name("Derived")
contracts = slither.get_contract_from_name("Base")
assert len(contracts) == 1
contract = contracts[0]
contract_derived = slither.get_contract_from_name("Derived")[0]
destination = contract.get_state_variable_from_name("destination")
source = contract.get_state_variable_from_name("source")
@ -125,8 +131,9 @@ print(
assert is_dependent(destination, source, contract_derived)
print("PropagateThroughArguments contract")
contract = slither.get_contract_from_name("PropagateThroughArguments")
assert contract
contracts = slither.get_contract_from_name("PropagateThroughArguments")
assert len(contracts) == 1
contract = contracts[0]
var_tainted = contract.get_state_variable_from_name("var_tainted")
assert var_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])
# Get the contract
contract = slither.get_contract_from_name("Contract")
assert contract
contracts = slither.get_contract_from_name("Contract")
assert len(contracts) == 1
contract = contracts[0]
# Get the variable
entry_point = contract.get_function_from_signature("entry_point()")

@ -9,8 +9,9 @@ if len(sys.argv) != 2:
slither = Slither(sys.argv[1])
# 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
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.
"""
# 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
if contract is None:
if len(contracts) != 1:
raise ValueError(f"Could not resolve target contract: {contract_name}")
contract = contracts[0]
# Obtain the target function
target_function = next(
(function for function in contract.functions if function.name == function_name), None

@ -14,7 +14,7 @@ def visit_node(node, visited):
return
visited += [node]
taints = node.function.slither.context[KEY]
taints = node.function.compilation_unit.context[KEY]
refs = {}
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))]
node.function.slither.context[KEY] = list(set(taints))
node.function.compilation_unit.context[KEY] = list(set(taints))
for son in node.sons:
visit_node(son, visited)

@ -9,8 +9,9 @@ if len(sys.argv) != 2:
slither = Slither(sys.argv[1])
# 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
var_a = contract.get_state_variable_from_name("a")

@ -11,10 +11,10 @@ setup(
install_requires=[
"prettytable>=0.7.2",
"pysha3>=1.0.2",
"crytic-compile>=0.1.13",
# "crytic-compile",
# "crytic-compile>=0.1.13",
"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",
long_description=open("README.md").read(),
entry_points={

@ -2,7 +2,7 @@
Compute the data depenency between all the SSA variables
"""
from collections import defaultdict
from typing import Union, Set, Dict
from typing import Union, Set, Dict, TYPE_CHECKING
from slither.core.declarations import (
Contract,
@ -26,6 +26,9 @@ from slither.slithir.variables import (
)
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)
if isinstance(variable, Constant):
return False
slither = context.slither
taints = slither.context[KEY_INPUT]
compilation_unit = context.compilation_unit
taints = compilation_unit.context[KEY_INPUT]
if not ignore_generic_taint:
taints |= GENERIC_TAINT
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)
if isinstance(variable, Constant):
return False
slither = context.slither
taints = slither.context[KEY_INPUT_SSA]
compilation_unit = context.compilation_unit
taints = compilation_unit.context[KEY_INPUT_SSA]
if not ignore_generic_taint:
taints |= GENERIC_TAINT
return variable in taints or any(
@ -258,15 +261,15 @@ def pprint_dependency(context):
###################################################################################
def compute_dependency(slither):
slither.context[KEY_INPUT] = set()
slither.context[KEY_INPUT_SSA] = set()
def compute_dependency(compilation_unit: "SlitherCompilationUnit"):
compilation_unit.context[KEY_INPUT] = set()
compilation_unit.context[KEY_INPUT_SSA] = set()
for contract in slither.contracts:
compute_dependency_contract(contract, slither)
for contract in compilation_unit.contracts:
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:
return
@ -281,8 +284,8 @@ def compute_dependency_contract(contract, slither):
# pylint: disable=expression-not-assigned
if function.visibility in ["public", "external"]:
[slither.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].add(p) for p in function.parameters]
[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_UNPROTECTED, KEY_NON_SSA_UNPROTECTED)

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

@ -1,7 +1,7 @@
from typing import 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.declarations import Function, Contract
@ -27,5 +27,5 @@ class ChildNode:
return self.node.function.contract
@property
def slither(self) -> "Slither":
return self.contract.slither
def compilation_unit(self) -> "SlitherCompilationUnit":
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 slither.core.children.child_slither import ChildSlither
from slither.core.solidity_types.type import Type
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.core.variables.variable import Variable
from slither.core.variables.state_variable import StateVariable
from slither.core.compilation_unit import SlitherCompilationUnit
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
"""
def __init__(self):
def __init__(self, compilation_unit: "SlitherCompilationUnit"):
super().__init__()
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._all_functions_called: Optional[List["InternalCallType"]] = None
self.compilation_unit: "SlitherCompilationUnit" = compilation_unit
###################################################################################
###################################################################################
# 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
"""
candidates = self.slither.contracts
candidates = self.compilation_unit.contracts
return [c for c in candidates if self in c.inheritance]
# endregion
@ -982,9 +985,9 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
###################################################################################
def is_from_dependency(self) -> bool:
if self.slither.crytic_compile is None:
return False
return self.slither.crytic_compile.is_dependency(self.source_mapping["filename_absolute"])
return self.compilation_unit.core.crytic_compile.is_dependency(
self.source_mapping["filename_absolute"]
)
# 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:
"""
if self.slither.crytic_compile:
if self.slither.crytic_compile.platform == PlatformType.TRUFFLE:
if self.name == "Migrations":
paths = Path(self.source_mapping["filename_absolute"]).parts
if len(paths) >= 2:
return paths[-2] == "contracts" and paths[-1] == "migrations.sol"
if self.compilation_unit.core.crytic_compile.platform == PlatformType.TRUFFLE:
if self.name == "Migrations":
paths = Path(self.source_mapping["filename_absolute"]).parts
if len(paths) >= 2:
return paths[-2] == "contracts" and paths[-1] == "migrations.sol"
return False
@property
@ -1035,7 +1037,7 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
self._is_upgradeable = False
if self.is_upgradeable_proxy:
return False
initializable = self.slither.get_contract_from_name("Initializable")
initializable = self.compilation_unit.get_contract_from_name("Initializable")
if initializable:
if initializable in self.inheritance:
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):
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_contract(self)
constructor_variable.set_contract_declarer(self)
constructor_variable.set_visibility("internal")
# For now, source mapping of the constructor variable is the whole contract
# Could be improved with a targeted source mapping
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
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):
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(
FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES
)
@ -1137,7 +1139,7 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
constructor_variable.set_visibility("internal")
# For now, source mapping of the constructor variable is the whole contract
# Could be improved with a targeted source mapping
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
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
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)
func.add_node(node)
assert variable.expression
@ -1175,7 +1177,7 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
variable.type,
)
expression.set_offset(variable.source_mapping, self.slither)
expression.set_offset(variable.source_mapping, self.compilation_unit)
node.add_expression(expression)
return node

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

@ -3,28 +3,15 @@
"""
import json
import logging
import math
import os
import re
from collections import defaultdict
from typing import Optional, Dict, List, Set, Union, Tuple
from typing import Optional, Dict, List, Set, Union
from crytic_compile import CryticCompile
from slither.core.compilation_unit import SlitherCompilationUnit
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.core.declarations import Contract
from slither.slithir.variables import Constant
from slither.utils.colors import red
@ -39,7 +26,8 @@ def _relative_path_format(path: str) -> str:
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
"""
@ -47,29 +35,19 @@ class SlitherCore(Context): # pylint: disable=too-many-instance-attributes,too-
def __init__(self):
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._source_units: Dict[int, str] = {}
self._solc_version: Optional[str] = None # '0.3' or '0.4':!
self._raw_source_code: Dict[str, str] = {}
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._results_to_hide: List = []
self._previous_results: List = []
# From triaged result
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._crytic_compile: Optional[CryticCompile] = None
@ -79,87 +57,23 @@ class SlitherCore(Context): # pylint: disable=too-many-instance-attributes,too-
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
self._disallow_partial: bool = False
self._skip_assembly: bool = False
self._show_ignored_findings = False
self.counter_slithir_tuple = 0
self.counter_slithir_temporary = 0
self.counter_slithir_reference = 0
self._compilation_units: List[SlitherCompilationUnit] = []
###################################################################################
###################################################################################
# region Source code
###################################################################################
###################################################################################
self._contracts: List[Contract] = []
self._contracts_derived: List[Contract] = []
@property
def source_code(self) -> Dict[str, str]:
""" {filename: source_code (str)}: source code """
return self._raw_source_code
def compilation_units(self) -> List[SlitherCompilationUnit]:
return list(self._compilation_units)
@property
def source_units(self) -> Dict[int, str]:
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
def add_compilation_unit(self, compilation_unit: SlitherCompilationUnit):
self._compilation_units.append(compilation_unit)
# endregion
###################################################################################
@ -170,22 +84,23 @@ class SlitherCore(Context): # pylint: disable=too-many-instance-attributes,too-
@property
def contracts(self) -> List[Contract]:
"""list(Contract): List of contracts."""
return list(self._contracts.values())
if not self._contracts:
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
def contracts_derived(self) -> List[Contract]:
"""list(Contract): List of contracts that are derived and not inherited."""
inheritance = (x.inheritance for x in self.contracts)
inheritance = [item for sublist in inheritance 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]:
if not self._contracts_derived:
all_contracts = [
compilation_unit.contracts_derived for compilation_unit in self._compilation_units
]
self._contracts_derived = [item for sublist in all_contracts for item in sublist]
return self._contracts_derived
def get_contract_from_name(self, contract_name: Union[str, Constant]) -> List[Contract]:
"""
Return a contract from a name
Args:
@ -193,92 +108,56 @@ class SlitherCore(Context): # pylint: disable=too-many-instance-attributes,too-
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)
contracts = []
for compilation_unit in self._compilation_units:
contract = compilation_unit.get_contract_from_name(contract_name)
if contract:
contracts.append(contract)
return contracts
# endregion
###################################################################################
###################################################################################
# region Variables
# region Source code
###################################################################################
###################################################################################
@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
###################################################################################
###################################################################################
def source_code(self) -> Dict[str, str]:
""" {filename: source_code (str)}: source code """
return self._raw_source_code
@property
def structures_top_level(self) -> List[StructureTopLevel]:
return self._structures_top_level
def filename(self) -> Optional[str]:
"""str: Filename."""
return self._filename
@property
def enums_top_level(self) -> List[EnumTopLevel]:
return self._enums_top_level
@filename.setter
def filename(self, filename: str):
self._filename = filename
@property
def variables_top_level(self) -> List[TopLevelVariable]:
return self._variables_top_level
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 functions_top_level(self) -> List[FunctionTopLevel]:
return self._functions_top_level
# endregion
###################################################################################
###################################################################################
# region Export
###################################################################################
###################################################################################
def markdown_root(self) -> str:
return self._markdown_root
def print_functions(self, d: str):
"""
Export all the functions to dot files
"""
for c in self.contracts:
for f in c.functions:
f.cfg_to_dot(os.path.join(d, "{}.{}.dot".format(c.name, f.name)))
for compilation_unit in self._compilation_units:
for c in compilation_unit.contracts:
for f in c.functions:
f.cfg_to_dot(os.path.join(d, "{}.{}.dot".format(c.name, f.name)))
# 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
- 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 = [
elem["source_mapping"].get("filename_absolute", "unknown")
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
def disallow_partial(self) -> bool:
"""
@ -460,42 +337,3 @@ class SlitherCore(Context): # pylint: disable=too-many-instance-attributes,too-
return self.show_ignore_findings
# 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
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
if TYPE_CHECKING:
from slither.core.compilation_unit import SlitherCompilationUnit
class SourceMapping(Context):
def __init__(self):
@ -25,27 +28,33 @@ class SourceMapping(Context):
return self._source_mapping
@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
from a start/end offset. All numbers start from 1.
Not done in an efficient way
"""
start_line, starting_column = slither.crytic_compile.get_line_from_offset(filename, start)
end_line, ending_column = slither.crytic_compile.get_line_from_offset(
start_line, starting_column = compilation_unit.core.crytic_compile.get_line_from_offset(
filename, start
)
end_line, ending_column = compilation_unit.core.crytic_compile.get_line_from_offset(
filename, start + length
)
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
see https://solidity.readthedocs.io/en/develop/miscellaneous.html#source-mappings
Returns:
(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)
if len(position) != 1:
@ -66,30 +75,32 @@ class SourceMapping(Context):
is_dependency = False
# If possible, convert the filename to its absolute/relative version
if slither.crytic_compile:
filenames = slither.crytic_compile.filename_lookup(filename_used)
if compilation_unit.core.crytic_compile:
filenames = compilation_unit.core.crytic_compile.filename_lookup(filename_used)
filename_absolute = filenames.absolute
filename_relative = filenames.relative
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 (
filename_absolute in slither.source_code
or filename_absolute in slither.crytic_compile.src_content
filename_absolute in compilation_unit.core.source_code
or filename_absolute in compilation_unit.core.crytic_compile.src_content
):
filename = filename_absolute
elif filename_relative in slither.source_code:
elif filename_relative in compilation_unit.core.source_code:
filename = filename_relative
elif filename_short in slither.source_code:
elif filename_short in compilation_unit.core.source_code:
filename = filename_short
else:
filename = filename_used
else:
filename = filename_used
if slither.crytic_compile:
(lines, starting_column, ending_column) = self._compute_line(slither, filename, s, l)
if compilation_unit.core.crytic_compile:
(lines, starting_column, ending_column) = self._compute_line(
compilation_unit, filename, s, l
)
else:
(lines, starting_column, ending_column) = ([], None, None)
@ -106,11 +117,11 @@ class SourceMapping(Context):
"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):
self._source_mapping = offset
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=""):
lines = self.source_mapping.get("lines", None)

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

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

@ -24,12 +24,12 @@ class ConstantPragma(AbstractDetector):
def _detect(self):
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 = sorted(list(set(versions)))
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"]
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.
results = []
pragma = self.slither.pragma_directives
pragma = self.compilation_unit.pragma_directives
disallowed_pragmas = []
for p in pragma:
@ -137,24 +137,19 @@ Consider using the latest version of Solidity for testing."""
results.append(json)
if self.slither.crytic_compile:
if self.slither.crytic_compile.compiler_version:
if (
self.slither.crytic_compile.compiler_version.version
not in self.ALLOWED_VERSIONS
):
info = [
"solc-",
self.slither.crytic_compile.compiler_version.version,
" is not recommended for deployment\n",
]
if self.compilation_unit.solc_version not in self.ALLOWED_VERSIONS:
info = [
"solc-",
self.compilation_unit.solc_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
# the line in the config that specifies the problematic version of solc
# 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
results.append(json)
results.append(json)
return results

@ -73,13 +73,13 @@ Every Ether sent to `Locked` will be lost."""
def _detect(self):
results = []
for contract in self.slither.contracts_derived:
for contract in self.compilation_unit.contracts_derived:
if contract.is_signature_only():
continue
funcs_payable = [function for function in contract.functions if function.payable]
if funcs_payable:
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"]
for function in funcs_payable:
info += ["\t - ", function, "\n"]

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

@ -68,10 +68,10 @@ Attackers can trigger unexpected behaviour by calling `bug(1)`."""
"""Detect dangerous conversion to enum"""
results = []
# 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
for c in self.slither.contracts:
for c in self.compilation_unit.contracts:
ret = _detect_dangerous_enum_conversions(c)
for node, var in ret:
func_info = [node, " has a dangerous enum conversion\n"]

@ -72,10 +72,12 @@ class PublicMappingNested(AbstractDetector):
"""
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:
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 []
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
# As the result, we might miss some TPs if the reused is due to the constructor called
# 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.
_add_constructors_with_args(
current_contract.explicit_base_constructor_calls,
@ -123,7 +123,7 @@ The constructor of `A` is called multiple times in `D` and `E`:
results = []
# 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 []
# Loop for each contract

@ -128,13 +128,13 @@ contract A {
results = []
# 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
# Check if pragma experimental ABIEncoderV2 is used
if not any(
(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

@ -133,7 +133,7 @@ contract A {
Detect storage signed integer array init/assignment
"""
results = []
if self.slither.solc_version not in vulnerable_solc_versions:
if self.compilation_unit.solc_version not in vulnerable_solc_versions:
return results
for contract in self.contracts:
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 = []
# 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
for contract in self.slither.contracts:
for contract in self.compilation_unit.contracts:
contract_info = ["Contract ", contract, " \n"]
nodes = self._detect_uninitialized_function_ptr_in_constructor(contract)
for node in nodes:

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

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

@ -20,7 +20,7 @@ class Backdoor(AbstractDetector):
def _detect(self):
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
for f in contract.functions:
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_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
if not self._match_state_variable(contract, 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'}
"""
results = []
for contract in self.slither.contracts_derived:
for contract in self.compilation_unit.contracts_derived:
functions = self._detect_unimplemented_function(contract)
if functions:
info = [contract, " does not implement functions:\n"]

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

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

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

@ -135,7 +135,7 @@ Bob calls `updateOwner` without specifying the `newOwner`, soBob loses ownership
# Check derived contracts for missing zero address validation
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)
for (_, var_nodes) in missing_zero_address_validation:
for var, nodes in var_nodes.items():

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

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

@ -1,16 +1,17 @@
from collections import defaultdict
from slither.core.compilation_unit import SlitherCompilationUnit
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
in the inheritance tree.
:param slither:
:return:
"""
missings = slither.contracts_with_missing_inheritance
missings = compilation_unit.contracts_with_missing_inheritance
ret = []
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
results = []
names_reused = self.slither.contract_name_collisions
compilation_unit = self.compilation_unit
names_reused = compilation_unit.contract_name_collisions
# First show the contracts that we know are missing
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)
@ -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
# 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:
info = [b, " inherits from a contract for which the name is reused.\n"]

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

@ -104,7 +104,7 @@ Otherwise, thoroughly review the contract to ensure a user-controlled variable c
"""
results = []
# 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
for contract in self.contracts:
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):
""""""
results = []
for c in self.slither.contracts_derived:
for c in self.compilation_unit.contracts_derived:
values = self.detect_call_in_loop(c)
for node in values:
func = node.function

@ -42,7 +42,7 @@ Bob calls `delegate` and delegates the execution to his malicious contract. As a
def _detect(self):
results = []
for contract in self.slither.contracts_derived:
for contract in self.compilation_unit.contracts_derived:
for f in contract.functions:
# If its an upgradeable proxy, do not report protected function
# 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):
""""""
results = []
for c in self.slither.contracts_derived:
for c in self.compilation_unit.contracts_derived:
values = self.detect_costly_operations_in_loop(c)
for node in values:
func = node.function

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

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

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

@ -1,7 +1,7 @@
"""
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.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.visitors.expression.export_values import ExportValues
@ -70,13 +70,13 @@ class ConstCandidateStateVars(AbstractDetector):
"""Detect state variables that could be const"""
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_non_constant_elementary_variables = {
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_variables_written = [
@ -101,5 +101,5 @@ class ConstCandidateStateVars(AbstractDetector):
return results
@staticmethod
def _format(slither, result):
custom_format(slither, result)
def _format(compilation_unit: SlitherCompilationUnit, 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.visited_all_paths = {}
for contract in self.slither.contracts:
for contract in self.compilation_unit.contracts:
for function in contract.functions:
if function.is_implemented and function.contract_declarer == contract:
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
variables_written_in_proxy = []
for c in self.slither.contracts:
for c in self.compilation_unit.contracts:
if c.is_upgradeable_proxy:
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)
"""
results = []
for c in self.slither.contracts_derived:
for c in self.compilation_unit.contracts_derived:
ret = self._detect_uninitialized(c)
for variable, functions in ret:

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

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

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

@ -1,11 +1,13 @@
import re
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.formatters.utils.patches import create_patch
def custom_format(slither, result):
def custom_format(comilation_unit: SlitherCompilationUnit, result):
elements = result["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"]
)
if target_contract:
@ -14,7 +16,7 @@ def custom_format(slither, result):
)
if function:
_patch(
slither,
comilation_unit,
result,
element["source_mapping"]["filename_absolute"],
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):
in_file_str = slither.source_code[in_file].encode("utf8")
def _patch(
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]
# 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

@ -1,5 +1,7 @@
import re
import logging
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.slithir.operations import (
Send,
Transfer,
@ -24,7 +26,7 @@ logger = logging.getLogger("Slither.Format")
# pylint: disable=anomalous-backslash-in-string
def custom_format(slither, result):
def custom_format(compilation_unit: SlitherCompilationUnit, result):
elements = result["elements"]
for element in elements:
target = element["additional_fields"]["target"]
@ -38,7 +40,7 @@ def custom_format(slither, result):
)
continue
_patch(slither, result, element, target)
_patch(compilation_unit, result, element, target)
# endregion
@ -157,7 +159,7 @@ def _convert_CapWords(original_name, slither):
return name
def _convert_mixedCase(original_name, slither):
def _convert_mixedCase(original_name, compilation_unit: SlitherCompilationUnit):
name = original_name
if isinstance(name, bytes):
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].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)")
if name in SOLIDITY_KEYWORDS:
raise FormatImpossible(f"{original_name} cannot be converted to {name} (Solidity keyword)")
return name
def _convert_UPPER_CASE_WITH_UNDERSCORES(name, slither):
if _name_already_use(slither, name.upper()):
def _convert_UPPER_CASE_WITH_UNDERSCORES(name, compilation_unit: SlitherCompilationUnit):
if _name_already_use(compilation_unit, name.upper()):
raise FormatImpossible(f"{name} cannot be converted to {name.upper()} (already used)")
if name.upper() in SOLIDITY_KEYWORDS:
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 = slither.get_contract_from_name(contract_name)
contract = compilation_unit.get_contract_from_name(contract_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":
target = slither.get_contract_from_name(element["name"])
target = compilation_unit.get_contract_from_name(element["name"])
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":
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":
# Avoid constructor (FP?)
if element["name"] != element["type_specific_fields"]["parent"]["name"]:
function_sig = element["type_specific_fields"]["signature"]
target = _get_from_contract(
slither, element, function_sig, "get_function_from_signature"
compilation_unit, element, function_sig, "get_function_from_signature"
)
elif _target == "modifier":
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":
contract_name = element["type_specific_fields"]["parent"]["type_specific_fields"]["parent"][
@ -242,7 +250,7 @@ def _patch(slither, result, element, _target):
"signature"
]
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)
target = function.get_local_variable_from_name(param_name)
@ -256,24 +264,26 @@ def _patch(slither, result, element, _target):
"signature"
]
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)
target = function.get_local_variable_from_name(var_name)
# State variable
else:
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":
target = _get_from_contract(
slither, element, element["name"], "get_enum_from_canonical_name"
compilation_unit, element, element["name"], "get_enum_from_canonical_name"
)
else:
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
@ -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)
def _explore(slither, result, target, convert):
for contract in slither.contracts_derived:
_explore_contract(slither, contract, result, target, convert)
def _explore(compilation_unit: SlitherCompilationUnit, result, target, convert):
for contract in compilation_unit.contracts_derived:
_explore_contract(compilation_unit, contract, result, target, convert)
# endregion

@ -1,21 +1,23 @@
import re
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.formatters.exceptions import FormatError, FormatImpossible
from slither.formatters.utils.patches import create_patch
def custom_format(slither, result):
def custom_format(compilation_unit: SlitherCompilationUnit, result):
elements = result["elements"]
for element in elements:
# TODO: decide if this should be changed in the constant detector
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"])
if not var.expression:
raise FormatImpossible(f"{var.name} is uninitialized and cannot become constant.")
_patch(
slither,
compilation_unit,
result,
element["source_mapping"]["filename_absolute"],
element["name"],
@ -26,9 +28,15 @@ def custom_format(slither, result):
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]
# Add keyword `constant` before the variable name
(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
def custom_format(slither, result):
def custom_format(compilation_unit: SlitherCompilationUnit, result):
elements = result["elements"]
for element in elements:
if element["type"] == "variable":
_patch(
slither,
compilation_unit,
result,
element["source_mapping"]["filename_absolute"],
element["source_mapping"]["start"],
)
def _patch(slither, result, in_file, modify_loc_start):
in_file_str = slither.source_code[in_file].encode("utf8")
def _patch(compilation_unit: SlitherCompilationUnit, result, in_file, modify_loc_start):
in_file_str = compilation_unit.core.source_code[in_file].encode("utf8")
old_str_of_interest = in_file_str[modify_loc_start:]
old_str = (
old_str_of_interest.decode("utf-8").partition(";")[0]

@ -226,8 +226,17 @@ class PrinterCallGraph(AbstractPrinter):
results = []
with open(all_contracts_filename, "w", encoding="utf8") as f:
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(
["strict digraph {"] + [_process_functions(self.slither.functions)] + ["}"]
["strict digraph {"] + [_process_functions(all_functions_as_dict.values())] + ["}"]
)
f.write(content)
results.append((all_contracts_filename, content))

@ -79,9 +79,8 @@ def _is_constant(f: Function) -> bool: # pylint: disable=too-many-branches
:return:
"""
if f.view or f.pure:
if f.contract.slither.crytic_compile and f.contract.slither.crytic_compile.compiler_version:
if not f.contract.slither.crytic_compile.compiler_version.version.startswith("0.4"):
return True
if not f.contract.compilation_unit.solc_version.startswith("0.4"):
return True
if f.payable:
return False
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.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
if (
f.contract.slither.crytic_compile
and f.contract.slither.crytic_compile.compiler_version
):
if f.contract.slither.crytic_compile.compiler_version.version.startswith("0.4"):
return False
if f.contract.compilation_unit.solc_version.startswith("0.4"):
return False
else:
return False
if isinstance(ir, InternalCall):

@ -21,8 +21,14 @@ def _extract_evm_info(slither):
CFG = load_evm_cfg_builder()
for contract in slither.contracts_derived:
contract_bytecode_runtime = slither.crytic_compile.bytecode_runtime(contract.name)
contract_srcmap_runtime = slither.crytic_compile.srcmap_runtime(contract.name)
contract_bytecode_runtime = (
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)
evm_info["cfg", contract.name] = cfg
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_bytecode_init = slither.crytic_compile.bytecode_init(contract.name)
contract_srcmap_init = slither.crytic_compile.srcmap_init(contract.name)
contract_bytecode_init = (
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)
evm_info["cfg_init", contract.name] = cfg_init
@ -61,7 +71,6 @@ class PrinterEVM(AbstractPrinter):
"""
txt = ""
if not self.slither.crytic_compile:
txt = "The EVM printer requires to compile with crytic-compile"
self.info(red(txt))

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

@ -34,21 +34,22 @@ class PrinterSlithIR(AbstractPrinter):
"""
txt = ""
for contract in self.contracts:
if contract.is_top_level:
continue
txt += "Contract {}\n".format(contract.name)
for function in contract.functions:
txt += f'\tFunction {function.canonical_name} {"" if function.is_shadowed else "(*)"}\n'
for compilation_unit in self.slither.compilation_units:
for contract in compilation_unit.contracts:
if contract.is_top_level:
continue
txt += "Contract {}\n".format(contract.name)
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)
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)
res = self.generate_output(txt)
return res

@ -1,14 +1,15 @@
import logging
import os
from typing import Union, List
from crytic_compile import CryticCompile, InvalidCompilation
# pylint: disable= no-name-in-module
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.printers.abstract_printer import AbstractPrinter
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.core.slither_core import SlitherCore
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
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")
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
def __init__(self, target, **kwargs):
def __init__(self, target: Union[str, CryticCompile], **kwargs):
"""
Args:
target (str | list(json) | CryticCompile)
target (str | CryticCompile)
Keyword Args:
solc (str): solc binary location (default 'solc')
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__()
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._skip_assembly: bool = kwargs.get("skip_assembly", False)
self._show_ignored_findings: bool = kwargs.get("show_ignored_findings", False)
# list of files provided (see --splitted option)
if isinstance(target, list):
self._init_from_list(target)
elif isinstance(target, str) and target.endswith(".json"):
self._init_from_raw_json(target)
else:
self._parser = SlitherSolc("", self)
try:
if isinstance(target, CryticCompile):
crytic_compile = target
else:
crytic_compile = CryticCompile(target, **kwargs)
self._crytic_compile = crytic_compile
except InvalidCompilation as e:
# pylint: disable=raise-missing-from
raise SlitherError(f"Invalid compilation: \n{str(e)}")
for path, ast in crytic_compile.asts.items():
self._parser.parse_top_level_from_loaded_json(ast, path)
self._parsers: List[SlitherCompilationUnitSolc] = []
try:
if isinstance(target, CryticCompile):
crytic_compile = target
else:
crytic_compile = CryticCompile(target, **kwargs)
self._crytic_compile = crytic_compile
except InvalidCompilation as e:
# pylint: disable=raise-missing-from
raise SlitherError(f"Invalid compilation: \n{str(e)}")
for compilation_unit in crytic_compile.compilation_units.values():
compilation_unit_slither = SlitherCompilationUnit(self, compilation_unit)
self._compilation_units.append(compilation_unit_slither)
parser = SlitherCompilationUnitSolc(compilation_unit_slither)
self._parsers.append(parser)
for path, ast in compilation_unit.asts.items():
parser.parse_top_level_from_loaded_json(ast, path)
self.add_source_code(path)
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)
self._triage_mode = triage_mode
self._parser.parse_contracts()
for parser in self._parsers:
parser.parse_contracts()
# skip_analyze is only used for testing
if not kwargs.get("skip_analyze", False):
self._parser.analyze_contracts()
def _init_from_raw_json(self, filename):
if not os.path.isfile(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:
stdout = astFile.read()
if not stdout:
to_log = f"Empty AST file: {filename}"
raise SlitherError(to_log)
contracts_json = stdout.split("\n=")
self._parser = SlitherSolc(filename, self)
for c in contracts_json:
self._parser.parse_top_level_from_json(c)
def _init_from_list(self, contract):
self._parser = SlitherSolc("", self)
for c in contract:
if "absolutePath" in c:
path = c["absolutePath"]
else:
path = c["attributes"]["absolutePath"]
self._parser.parse_top_level_from_loaded_json(c, path)
for parser in self._parsers:
parser.analyze_contracts()
# def _init_from_raw_json(self, filename):
# if not os.path.isfile(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:
# stdout = astFile.read()
# if not stdout:
# to_log = f"Empty AST file: {filename}"
# raise SlitherError(to_log)
# contracts_json = stdout.split("\n=")
#
# self._parser = SlitherCompilationUnitSolc(filename, self)
#
# for c in contracts_json:
# self._parser.parse_top_level_from_json(c)
# def _init_from_list(self, contract):
# self._parser = SlitherCompilationUnitSolc("", self)
# for c in contract:
# if "absolutePath" in c:
# path = c["absolutePath"]
# else:
# path = c["attributes"]["absolutePath"]
# self._parser.parse_top_level_from_loaded_json(c, path)
@property
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)
instance = detector_class(self, logger_detector)
self._detectors.append(instance)
for compilation_unit in self.compilation_units:
instance = detector_class(compilation_unit, self, logger_detector)
self._detectors.append(instance)
def register_printer(self, printer_class):
"""
@ -181,6 +183,7 @@ class Slither(SlitherCore): # pylint: disable=too-many-instance-attributes
self.load_previous_results()
results = [d.detect() for d in self._detectors]
self.write_results_to_hide()
return results

@ -82,6 +82,7 @@ from slither.visitors.slithir.expression_to_slithir import ExpressionToSlithIR
if TYPE_CHECKING:
from slither.core.cfg.node import Node
from slither.core.compilation_unit import SlitherCompilationUnit
logger = logging.getLogger("ConvertToIR")
@ -434,31 +435,19 @@ def propagate_type_and_convert_call(result, node):
return result
def _convert_type_contract(ir, slither):
def _convert_type_contract(ir, compilation_unit: "SlitherCompilationUnit"):
assert isinstance(ir.variable_left.type, TypeInformation)
contract = ir.variable_left.type.type
if ir.variable_right == "creationCode":
if slither.crytic_compile:
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"
bytecode = compilation_unit.crytic_compile_compilation_unit.bytecode_init(contract.name)
assignment = Assignment(ir.lvalue, Constant(str(bytecode)), ElementaryType("bytes"))
assignment.set_expression(ir.expression)
assignment.set_node(ir.node)
assignment.lvalue.set_type(ElementaryType("bytes"))
return assignment
if ir.variable_right == "runtimeCode":
if slither.crytic_compile:
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"
bytecode = compilation_unit.crytic_compile_compilation_unit.bytecode_runtime(contract.name)
assignment = Assignment(ir.lvalue, Constant(str(bytecode)), ElementaryType("bytes"))
assignment.set_expression(ir.expression)
assignment.set_node(ir.node)
@ -530,7 +519,7 @@ def propagate_types(ir, node: "Node"): # pylint: disable=too-many-locals
# UserdefinedType
t_type = t.type
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)
# 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(
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
t = None
ir_func = ir.function
@ -699,7 +688,7 @@ def propagate_types(ir, node: "Node"): # pylint: disable=too-many-locals
elif isinstance(ir, NewArray):
ir.lvalue.set_type(ir.array_type)
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))
elif isinstance(ir, NewElementaryType):
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_value = ir.call_value
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")])
else:
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):
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:
lib_call = LibraryCall(
lib_contract,

@ -106,7 +106,7 @@ class HighLevelCall(Call, OperationWithLValue):
:return: bool
"""
# 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):
return False
if isinstance(self.function, Variable):

@ -50,7 +50,7 @@ class NewContract(Call, OperationWithLValue): # pylint: disable=too-many-instan
@property
def contract_created(self):
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
###################################################################################

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

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

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

@ -44,7 +44,9 @@ class NodeSolc:
AssignmentOperationType.ASSIGN,
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)
expression = self._node.expression

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

@ -48,7 +48,9 @@ class EventSolc:
elem = EventVariable()
# Todo: check if the source offset is always here
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.analyze(contract)

@ -30,8 +30,10 @@ from slither.solc_parsing.exceptions import ParsingError
if TYPE_CHECKING:
from slither.core.expressions.expression import Expression
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.compilation_unit import SlitherCompilationUnit
LOGGER = logging.getLogger("FunctionSolc")
@ -52,9 +54,9 @@ class FunctionSolc:
function: Function,
function_data: Dict,
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._function = function
@ -100,12 +102,12 @@ class FunctionSolc:
return self._contract_parser
@property
def slither_parser(self) -> "SlitherSolc":
def slither_parser(self) -> "SlitherCompilationUnitSolc":
return self._slither_parser
@property
def slither(self) -> "SlitherCore":
return self._function.slither
def compilation_unit(self) -> "SlitherCompilationUnit":
return self._function.compilation_unit
###################################################################################
###################################################################################
@ -647,7 +649,7 @@ class FunctionSolc:
try:
local_var = LocalVariable()
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)
self._add_local_variable(local_var_parser)
@ -827,7 +829,7 @@ class FunctionSolc:
) -> NodeSolc:
local_var = LocalVariableInitFromTuple()
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)
@ -861,7 +863,7 @@ class FunctionSolc:
node = self._parse_block(statement, node)
elif name == "InlineAssembly":
# 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
yul_object = self._new_yul_block(statement["src"])
entrypoint = yul_object.entrypoint
@ -1069,7 +1071,7 @@ class FunctionSolc:
local_var = LocalVariable()
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)
@ -1085,7 +1087,7 @@ class FunctionSolc:
def _parse_params(self, params: Dict):
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:
params = params["parameters"]
@ -1101,7 +1103,7 @@ class FunctionSolc:
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:
returns = returns["parameters"]

@ -11,7 +11,7 @@ from slither.solc_parsing.declarations.function import FunctionSolc
if TYPE_CHECKING:
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):
@ -20,7 +20,7 @@ class ModifierSolc(FunctionSolc):
modifier: Modifier,
function_data: Dict,
contract_parser: "ContractSolc",
slither_parser: "SlitherSolc",
slither_parser: "SlitherCompilationUnitSolc",
):
super().__init__(modifier, function_data, contract_parser, slither_parser)
# _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:
elem = StructureVariable()
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.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
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
@ -22,7 +22,7 @@ class StructureTopLevelSolc: # pylint: disable=too-few-public-methods
self,
st: Structure,
struct: Dict,
slither_parser: "SlitherSolc",
slither_parser: "SlitherCompilationUnitSolc",
):
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:
elem = StructureVariable()
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.analyze(self._slither_parser)

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

@ -4,19 +4,18 @@ import os
import re
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.enum_top_level import EnumTopLevel
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.variables.top_level_variable import TopLevelVariable
from slither.exceptions import SlitherException
from slither.solc_parsing.declarations.contract import ContractSolc
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.exceptions import VariableNotFound
from slither.solc_parsing.variables.top_level_variable import TopLevelVariableSolc
@ -26,11 +25,11 @@ logger = logging.getLogger("SlitherSolcParsing")
logger.setLevel(logging.INFO)
class SlitherSolc:
class SlitherCompilationUnitSolc:
# pylint: disable=no-self-use,too-many-instance-attributes
def __init__(self, filename: str, core: SlitherCore):
def __init__(self, compilation_unit: SlitherCompilationUnit):
super().__init__()
core.filename = filename
self._contracts_by_id: Dict[int, ContractSolc] = {}
self._parsed = False
self._analyzed = False
@ -41,15 +40,16 @@ class SlitherSolc:
self._functions_top_level_parser: List[FunctionSolc] = []
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._top_level_contracts_counter = 0
@property
def core(self):
return self._core
def compilation_unit(self) -> SlitherCompilationUnit:
return self._compilation_unit
@property
def all_functions_and_modifiers_parser(self) -> List[FunctionSolc]:
@ -140,8 +140,8 @@ class SlitherSolc:
values.append(child["attributes"][self.get_key()])
enum = EnumTopLevel(name, canonicalName, values)
enum.set_offset(top_level_data["src"], self._core)
self.core.enums_top_level.append(enum)
enum.set_offset(top_level_data["src"], self._compilation_unit)
self._compilation_unit.enums_top_level.append(enum)
def parse_top_level_from_loaded_json(
self, data_loaded: Dict, filename: str
@ -152,14 +152,12 @@ class SlitherSolc:
if "sourcePaths" in data_loaded:
for sourcePath in data_loaded["sourcePaths"]:
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":
self._core.solc_version = "0.3"
logger.error("solc <0.4 is not supported")
return
if data_loaded[self.get_key()] == "SourceUnit":
self._core.solc_version = "0.4"
self._parse_source_unit(data_loaded, filename)
else:
logger.error("solc version is not supported")
@ -167,10 +165,10 @@ class SlitherSolc:
for top_level_data in data_loaded[self.get_children()]:
if top_level_data[self.get_key()] == "ContractDefinition":
contract = Contract()
contract = Contract(self._compilation_unit)
contract_parser = ContractSolc(self, contract, 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
@ -179,8 +177,8 @@ class SlitherSolc:
pragma = Pragma(top_level_data["literals"])
else:
pragma = Pragma(top_level_data["attributes"]["literals"])
pragma.set_offset(top_level_data["src"], self._core)
self._core.pragma_directives.append(pragma)
pragma.set_offset(top_level_data["src"], self._compilation_unit)
self._compilation_unit.pragma_directives.append(pragma)
elif top_level_data[self.get_key()] == "ImportDirective":
if self.is_compact_ast:
import_directive = Import(top_level_data["absolutePath"])
@ -189,15 +187,15 @@ class SlitherSolc:
import_directive.alias = top_level_data["unitAlias"]
else:
import_directive = Import(top_level_data["attributes"].get("absolutePath", ""))
import_directive.set_offset(top_level_data["src"], self._core)
self._core.import_directives.append(import_directive)
import_directive.set_offset(top_level_data["src"], self._compilation_unit)
self._compilation_unit.import_directives.append(import_directive)
elif top_level_data[self.get_key()] == "StructDefinition":
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)
self._core.structures_top_level.append(st)
self._compilation_unit.structures_top_level.append(st)
self._structures_top_level_parser.append(st_parser)
elif top_level_data[self.get_key()] == "EnumDefinition":
@ -207,15 +205,15 @@ class SlitherSolc:
elif top_level_data[self.get_key()] == "VariableDeclaration":
var = TopLevelVariable()
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)
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)
self._core.functions_top_level.append(func)
self._compilation_unit.functions_top_level.append(func)
self._functions_top_level_parser.append(func_parser)
self.add_function_or_modifier_parser(func_parser)
@ -246,16 +244,16 @@ class SlitherSolc:
# This works only for crytic compile.
# which used --combined-json ast, rather than --ast-json
# As a result -1 is not used as index
if self._core.crytic_compile is not None:
sourceUnit = len(self._core.source_code)
if self._compilation_unit.core.crytic_compile is not None:
sourceUnit = len(self._compilation_unit.core.source_code)
self._core.source_units[sourceUnit] = name
if os.path.isfile(name) and not name in self._core.source_code:
self._core.add_source_code(name)
self._compilation_unit.source_units[sourceUnit] = name
if os.path.isfile(name) and not name in self._compilation_unit.core.source_code:
self._compilation_unit.core.add_source_code(name)
else:
lib_name = os.path.join("node_modules", name)
if os.path.isfile(lib_name) and not name in self._core.source_code:
self._core.add_source_code(lib_name)
if os.path.isfile(lib_name) and not name in self._compilation_unit.core.source_code:
self._compilation_unit.core.add_source_code(lib_name)
# endregion
###################################################################################
@ -275,7 +273,7 @@ class SlitherSolc:
def parse_contracts(self): # pylint: disable=too-many-statements,too-many-branches
if not self._underlying_contract_to_parser:
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:
raise Exception("Contract analysis can be run only once!")
@ -291,17 +289,17 @@ class SlitherSolc:
"""Your codebase has a contract named 'SlitherInternalTopLevelContract'.
Please rename it, this name is reserved for Slither's internals"""
)
if contract.name in self._core.contracts_as_dict:
if contract.id != self._core.contracts_as_dict[contract.name].id:
self._core.contract_name_collisions[contract.name].append(
if contract.name in self._compilation_unit.contracts_as_dict:
if contract.id != self._compilation_unit.contracts_as_dict[contract.name].id:
self._compilation_unit.contract_name_collisions[contract.name].append(
contract.source_mapping_str
)
self._core.contract_name_collisions[contract.name].append(
self._core.contracts_as_dict[contract.name].source_mapping_str
self._compilation_unit.contract_name_collisions[contract.name].append(
self._compilation_unit.contracts_as_dict[contract.name].source_mapping_str
)
else:
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
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:]:
if i in contract_parser.remapping:
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:
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
for i in contract_parser.baseContracts:
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:
fathers.append(self._contracts_by_id[i])
else:
@ -336,7 +336,7 @@ Please rename it, this name is reserved for Slither's internals"""
for i in contract_parser.baseConstructorContractsCalled:
if i in contract_parser.remapping:
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:
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:
self._core.contracts_with_missing_inheritance.add(
self._compilation_unit.contracts_with_missing_inheritance.add(
contract_parser.underlying_contract
)
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")
self._convert_to_slithir()
compute_dependency(self._core)
self._core.compute_storage_layout()
compute_dependency(self._compilation_unit)
self._compilation_unit.compute_storage_layout()
self._analyzed = True
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):
for func_parser in self._functions_top_level_parser:
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):
try:
@ -560,7 +560,7 @@ Please rename it, this name is reserved for Slither's internals"""
def _convert_to_slithir(self):
for contract in self._core.contracts:
for contract in self._compilation_unit.contracts:
contract.add_constructor_variables()
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()
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_ssa(dict())
self._core.propagate_function_calls()
for contract in self._core.contracts:
self._compilation_unit.propagate_function_calls()
for contract in self._compilation_unit.contracts:
contract.fix_phi()
contract.update_read_write_using_ssa()

@ -20,6 +20,7 @@ from slither.solc_parsing.exceptions import ParsingError
if TYPE_CHECKING:
from slither.core.declarations import Structure, Enum
from slither.core.declarations.contract import Contract
from slither.core.compilation_unit import SlitherCompilationUnit
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.declarations.contract import ContractSolc
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
# 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
):
if isinstance(caller_context, SlitherSolc):
sl = caller_context.core
if isinstance(caller_context, SlitherCompilationUnitSolc):
sl = caller_context.compilation_unit
next_context = caller_context
else:
assert isinstance(caller_context, FunctionSolc)
sl = caller_context.underlying_function.slither
sl = caller_context.underlying_function.compilation_unit
next_context = caller_context.slither_parser
structures_direct_access = sl.structures_top_level
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
next_context = caller_context
structures_direct_access = contract.structures + contract.slither.structures_top_level
all_structuress = [c.structures for c in contract.slither.contracts]
structures_direct_access = (
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 += contract.slither.structures_top_level
enums_direct_access = contract.enums + contract.slither.enums_top_level
all_enumss = [c.enums for c in contract.slither.contracts]
all_structures += contract.compilation_unit.structures_top_level
enums_direct_access = contract.enums + contract.compilation_unit.enums_top_level
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 += contract.slither.enums_top_level
contracts = contract.slither.contracts
all_enums += contract.compilation_unit.enums_top_level
contracts = contract.compilation_unit.contracts
functions = contract.functions + contract.modifiers
else:
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] = []
for p in params[index]:
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.analyze(caller_context)
@ -361,7 +365,7 @@ def parse_type(t: Union[Dict, UnknownType], caller_context):
params_vars.append(var)
for p in return_values[index]:
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.analyze(caller_context)

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

@ -79,12 +79,13 @@ def main():
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}"
_log_error(err, args)
return
contract = contracts[0]
# First elem is the function, second is the event
erc = ERCS[args.erc.upper()]
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.contract import Contract
from slither.core.slither_core import SlitherCore
from slither.core.solidity_types import MappingType, ArrayType
from slither.core.solidity_types.user_defined_type import UserDefinedType
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
def __init__(
self,
slither,
slither: SlitherCore,
external_to_public=False,
remove_assert=False,
private_to_internal=False,
@ -51,7 +52,7 @@ class Flattening:
pragma_solidity: Optional[str] = None,
):
self._source_codes: Dict[Contract, str] = {}
self._slither = slither
self._slither: SlitherCore = slither
self._external_to_public = external_to_public
self._remove_assert = remove_assert
self._use_abi_encoder_v2 = False
@ -71,10 +72,11 @@ class Flattening:
Set _use_abi_encorder_v2
:return:
"""
for p in self._slither.pragma_directives:
if "ABIEncoderV2" in str(p.directive):
self._use_abi_encoder_v2 = True
return
for compilation_unit in self._slither.compilation_units:
for p in compilation_unit.pragma_directives:
if "ABIEncoderV2" in str(p.directive):
self._use_abi_encoder_v2 = True
return
def _get_source_code(
self, contract: Contract
@ -187,8 +189,9 @@ class Flattening:
ret = ""
if self._pragma_solidity:
ret += f"pragma solidity {self._pragma_solidity};\n"
elif self._slither.solc_version:
ret += f"pragma solidity {self._slither.solc_version};\n"
else:
# 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:
ret += "pragma experimental ABIEncoderV2;\n"
@ -338,10 +341,11 @@ class Flattening:
elif strategy == Strategy.LocalImport:
exports = self._export_with_import()
else:
contract = self._slither.get_contract_from_name(target)
if contract is None:
contracts = self._slither.get_contract_from_name(target)
if len(contracts) != 1:
logger.error(f"{target} not found")
return
contract = contracts[0]
exports = [self._export_contract_with_inheritance(contract)]
if json:

@ -9,5 +9,9 @@ def kspec_coverage(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_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
in_file = contract.source_mapping["filename_absolute"]
# 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
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.
"""
# 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
if contract is None:
if len(contracts) != 1:
raise ResolveFunctionException(f"Could not resolve target contract: {contract_name}")
contract = contracts[0]
# Obtain the target function
target_function = next(
(function for function in contract.functions if function.name == function_name),

@ -126,8 +126,8 @@ def main():
# Perform slither analysis on the given filename
slither = Slither(args.filename, **vars(args))
contract = slither.get_contract_from_name(args.contract)
if not contract:
contracts = slither.get_contract_from_name(args.contract)
if len(contracts) != 1:
if len(slither.contracts) == 1:
contract = slither.contracts[0]
else:
@ -137,6 +137,8 @@ def main():
to_log = f"{args.contract} not found"
logger.error(to_log)
return
else:
contract = contracts[0]
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()
:return:
"""
if contract.slither.crytic_compile is None:
if contract.compilation_unit.core.crytic_compile is None:
logging.error("Please compile with crytic-compile")
return
if contract.slither.crytic_compile.type not in [
if contract.compilation_unit.core.crytic_compile.type not in [
PlatformType.TRUFFLE,
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
# 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
# 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)
# Get the properties
@ -116,13 +118,13 @@ def generate_erc20(
# Generate Echidna config file
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 = ""
# 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)
logger.info("################################################")
@ -132,7 +134,7 @@ def generate_erc20(
logger.info(green(unit_test_info))
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}"
logger.info(green(txt))
@ -193,7 +195,7 @@ def _check_compatibility(contract):
def _get_properties(contract, properties: List[Property]) -> Tuple[str, List[Property]]:
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 properties])

@ -24,7 +24,7 @@ def generate_truffle_test(
filename_init = f"Initialization{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)

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

@ -138,7 +138,9 @@ class AbstractCheck(metaclass=abc.ABCMeta):
return all_results
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

@ -55,7 +55,7 @@ Consider using a `Initializable` contract to follow [standard practice](https://
"""
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:
info = [
"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
def _check(self):
initializable = self.contract.slither.get_contract_from_name("Initializable")
initializable = self.contract.compilation_unit.get_contract_from_name("Initializable")
# See InitializablePresent
if initializable is None:
return []
@ -114,7 +114,7 @@ Review manually the contract's initialization. Consider inheriting a `Initializa
REQUIRE_CONTRACT = True
def _check(self):
initializable = self.contract.slither.get_contract_from_name("Initializable")
initializable = self.contract.compilation_unit.get_contract_from_name("Initializable")
# See InitializablePresent
if initializable is None:
return []
@ -160,7 +160,7 @@ Use `Initializable.initializer()`.
REQUIRE_CONTRACT = True
def _check(self):
initializable = self.contract.slither.get_contract_from_name("Initializable")
initializable = self.contract.compilation_unit.get_contract_from_name("Initializable")
# See InitializablePresent
if initializable is None:
return []

@ -4,7 +4,7 @@ import json
import logging
import zipfile
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 slither.core.cfg.node import Node
@ -15,6 +15,9 @@ from slither.exceptions import SlitherError
from slither.utils.colors import yellow
from slither.utils.myprettytable import MyPrettyTable
if TYPE_CHECKING:
from slither.core.compilation_unit import SlitherCompilationUnit
logger = logging.getLogger("Slither")
@ -508,7 +511,7 @@ class Output:
self,
name: str,
source_mapping,
slither,
compilation_unit: "SlitherCompilationUnit",
additional_fields: Optional[Dict] = None,
):
# If this a tuple with (filename, start, end), convert it to a source mapping.
@ -523,7 +526,7 @@ class Output:
for (
source_unit_id,
source_unit_filename,
) in slither.source_units.items()
) in compilation_unit.source_units.items()
if source_unit_filename == filename
),
-1,
@ -536,7 +539,7 @@ class Output:
if isinstance(source_mapping, str):
source_mapping_str = source_mapping
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 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",
"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",
"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:\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",
"id": "38b2d47301e59ffa598ec48a8e874e6a7070d6cf4e4ab2909f33a8b72fc28a4b",
"id": "a2fad0e9c8a75e55b8c548dd184dc33d78142ea5bac9c36cdc5eb174e56d689c",
"check": "locked-ether",
"impact": "Medium",
"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",
"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",
"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:\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",
"id": "38b2d47301e59ffa598ec48a8e874e6a7070d6cf4e4ab2909f33a8b72fc28a4b",
"id": "a2fad0e9c8a75e55b8c548dd184dc33d78142ea5bac9c36cdc5eb174e56d689c",
"check": "locked-ether",
"impact": "Medium",
"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",
"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",
"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:\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",
"id": "1eb31cab3df894265d213a6a2e85c7fe840a4021a598ab09f1052a86c897d886",
"id": "1818e759217cfcf6787001194bba24ad45470b123962a72d39062edb19447022",
"check": "locked-ether",
"impact": "Medium",
"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",
"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",
"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:\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",
"id": "1eb31cab3df894265d213a6a2e85c7fe840a4021a598ab09f1052a86c897d886",
"id": "1818e759217cfcf6787001194bba24ad45470b123962a72d39062edb19447022",
"check": "locked-ether",
"impact": "Medium",
"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",
"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",
"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:\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",
"id": "54735fb882b1f9775c73514464a814ee233d22cfb9eb38621a7fd5a80b93deca",
"id": "4c6393f838949def61fef871f6771fee22a8783f10fcc084c78287fd2f1d60ed",
"check": "pragma",
"impact": "Informational",
"confidence": "High"

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

Loading…
Cancel
Save