From 232dfb1aaf4de04cb250a9393576abf207fabb8d Mon Sep 17 00:00:00 2001 From: Josselin Date: Fri, 9 Apr 2021 16:55:16 +0200 Subject: [PATCH 1/8] Add the support for multiple compilation units. This commit includes a lot of breaking changes: - SlitherCore is split into SlitherCore and SlitherCompilationUnit, as a result several functions from Slither are not direclty accessible anymore - There is a list of compilation unit in SlitherCore - The detectors are run per compilation unit - however slither filters out results if their ID are the same to avoid dupplicate - The printers are run per SlitherCore. - The non-crytic-compile inputs are removed (json/list of json). This will help us to clean the API in the long run. These were only used in internal tests - slither.get_contract_from_name returns now a list (which can be empty) --- examples/scripts/convert_to_ir.py | 5 +- examples/scripts/data_dependency.py | 31 +- examples/scripts/functions_called.py | 5 +- examples/scripts/functions_writing.py | 5 +- examples/scripts/possible_paths.py | 6 +- examples/scripts/taint_mapping.py | 4 +- examples/scripts/variable_in_condition.py | 5 +- .../data_dependency/data_dependency.py | 29 +- slither/core/cfg/node.py | 6 +- slither/core/children/child_node.py | 6 +- slither/core/children/child_slither.py | 17 - slither/core/compilation_unit.py | 259 +++++++++++++++ slither/core/declarations/contract.py | 42 +-- slither/core/declarations/function.py | 23 +- slither/core/slither_core.py | 296 ++++-------------- slither/core/source_mapping/source_mapping.py | 45 ++- slither/detectors/abstract_detector.py | 39 +-- .../attributes/const_functions_asm.py | 6 +- .../attributes/const_functions_state.py | 2 +- .../detectors/attributes/constant_pragma.py | 4 +- .../detectors/attributes/incorrect_solc.py | 27 +- slither/detectors/attributes/locked_ether.py | 4 +- .../attributes/unimplemented_interface.py | 4 +- .../compiler_bugs/enum_conversion.py | 4 +- .../compiler_bugs/public_mapping_nested.py | 6 +- .../compiler_bugs/reused_base_constructor.py | 4 +- .../storage_ABIEncoderV2_array.py | 4 +- .../storage_signed_integer_array.py | 2 +- ...initialized_function_ptr_in_constructor.py | 4 +- .../erc/incorrect_erc20_interface.py | 2 +- .../erc/incorrect_erc721_interface.py | 2 +- slither/detectors/examples/backdoor.py | 2 +- slither/detectors/functions/unimplemented.py | 4 +- slither/detectors/operations/bad_prng.py | 2 +- .../missing_events_access_control.py | 2 +- .../operations/missing_events_arithmetic.py | 2 +- .../missing_zero_address_validation.py | 2 +- .../operations/unused_return_values.py | 2 +- slither/detectors/reentrancy/token.py | 2 +- slither/detectors/slither/name_reused.py | 15 +- slither/detectors/source/rtlo.py | 2 +- .../statements/array_length_assignment.py | 2 +- slither/detectors/statements/calls_in_loop.py | 2 +- .../statements/controlled_delegatecall.py | 2 +- .../statements/costly_operations_in_loop.py | 2 +- .../statements/incorrect_strict_equality.py | 2 +- .../detectors/statements/too_many_digits.py | 2 +- .../statements/unprotected_upgradeable.py | 2 +- .../possible_const_state_variables.py | 10 +- .../uninitialized_local_variables.py | 2 +- .../uninitialized_state_variables.py | 4 +- .../uninitialized_storage_variables.py | 2 +- .../variables/unused_state_variables.py | 8 +- .../formatters/attributes/const_functions.py | 14 +- .../formatters/functions/external_function.py | 14 +- .../naming_convention/naming_convention.py | 54 ++-- .../possible_const_state_variables.py | 18 +- .../variables/unused_state_variables.py | 9 +- slither/printers/call/call_graph.py | 11 +- slither/printers/guidance/echidna.py | 17 +- slither/printers/summary/evm.py | 11 +- slither/printers/summary/human_summary.py | 15 +- slither/printers/summary/slithir.py | 29 +- slither/slither.py | 115 +++---- slither/slithir/convert.py | 21 +- slither/slithir/operations/high_level_call.py | 2 +- slither/slithir/operations/new_contract.py | 2 +- slither/solc_parsing/cfg/node.py | 4 +- slither/solc_parsing/declarations/contract.py | 42 +-- slither/solc_parsing/declarations/event.py | 4 +- slither/solc_parsing/declarations/function.py | 26 +- slither/solc_parsing/declarations/modifier.py | 4 +- .../declarations/structure_contract.py | 2 +- .../declarations/structure_top_level.py | 6 +- .../expressions/expression_parsing.py | 63 ++-- ...lc.py => slither_compilation_unit_solc.py} | 106 +++---- .../solidity_types/type_parsing.py | 32 +- slither/solc_parsing/yul/parse_yul.py | 26 +- slither/tools/erc_conformance/__main__.py | 5 +- slither/tools/flattening/flattening.py | 5 +- .../tools/mutator/utils/generic_patching.py | 2 +- .../tools/possible_paths/possible_paths.py | 6 +- slither/tools/properties/__main__.py | 6 +- slither/tools/properties/properties/erc20.py | 18 +- .../ercs/erc20/unit_tests/truffle.py | 2 +- slither/tools/upgradeability/__main__.py | 15 +- .../upgradeability/checks/abstract_checks.py | 4 +- .../upgradeability/checks/initialization.py | 8 +- slither/utils/output.py | 11 +- ...ked_ether-0.5.1.sol.0.5.1.LockedEther.json | 6 +- .../locked_ether.sol.0.4.25.LockedEther.json | 6 +- ...agma.0.4.24.sol.0.4.25.ConstantPragma.json | 6 +- .../arbitrary_send-0.5.1.arbitrary-send.txt | 10 - .../arbitrary_send.arbitrary-send.txt | 10 - tests/expected_json/backdoor.backdoor.txt | 6 - tests/expected_json/backdoor.suicidal.txt | 6 - ...const_state_variables.constable-states.txt | 10 - .../constant-0.5.1.constant-function-asm.txt | 2 - ...constant-0.5.1.constant-function-state.txt | 2 - .../constant.constant-function-asm.txt | 5 - .../constant.constant-function-state.txt | 8 - ...d_delegatecall.controlled-delegatecall.txt | 8 - .../deprecated_calls.deprecated-standards.txt | 20 -- .../erc20_indexed.erc20-indexed.txt | 8 - .../external_function.external-function.txt | 14 - .../external_function_2.external-function.txt | 2 - .../incorrect_equality.incorrect-equality.txt | 28 -- ...orrect_erc20_interface.erc20-interface.txt | 10 - ...rect_erc721_interface.erc721-interface.txt | 14 - ...nline_assembly_contract-0.5.1.assembly.txt | 6 - .../inline_assembly_contract.assembly.txt | 6 - ...inline_assembly_library-0.5.1.assembly.txt | 8 - .../inline_assembly_library.assembly.txt | 8 - .../locked_ether-0.5.1.locked-ether.txt | 8 - .../locked_ether.locked-ether.txt | 8 - .../low_level_calls.low-level-calls.txt | 6 - .../multiple_calls_in_loop.calls-loop.txt | 5 - .../naming_convention.naming-convention.txt | 16 - .../old_solc.sol.json.solc-version.txt | 5 - tests/expected_json/pragma.0.4.24.pragma.txt | 8 - ...ntrancy-0.5.1-events.reentrancy-events.txt | 9 - .../reentrancy-0.5.1.reentrancy-eth.txt | 14 - .../reentrancy.reentrancy-eth.txt | 14 - .../reentrancy.reentrancy-unlimited-gas.txt | 9 - .../right_to_left_override.rtlo.txt | 6 - .../shadowing_abstract.shadowing-abstract.txt | 6 - ...wing_builtin_symbols.shadowing-builtin.txt | 17 - ...adowing_local_variable.shadowing-local.txt | 16 - ...adowing_state_variable.shadowing-state.txt | 6 - .../solc_version_incorrect.solc-version.txt | 7 - ...ion_incorrect_05.ast.json.solc-version.txt | 6 - tests/expected_json/timestamp.timestamp.txt | 13 - .../too_many_digits.too-many-digits.txt | 14 - .../tx_origin-0.5.1.tx-origin.txt | 6 - tests/expected_json/tx_origin.tx-origin.txt | 6 - .../unchecked_send-0.5.1.unchecked-send.txt | 5 - ...ninitialized-0.5.1.uninitialized-state.txt | 12 - .../uninitialized.uninitialized-state.txt | 12 - ...zed_local_variable.uninitialized-local.txt | 5 - ..._storage_pointer.uninitialized-storage.txt | 5 - .../unused_return.unused-return.txt | 6 - .../unused_state.unused-state.txt | 8 - tests/expected_json/void-cst.void-cst.txt | 6 - 143 files changed, 956 insertions(+), 1219 deletions(-) delete mode 100644 slither/core/children/child_slither.py create mode 100644 slither/core/compilation_unit.py rename slither/solc_parsing/{slitherSolc.py => slither_compilation_unit_solc.py} (87%) delete mode 100644 tests/expected_json/arbitrary_send-0.5.1.arbitrary-send.txt delete mode 100644 tests/expected_json/arbitrary_send.arbitrary-send.txt delete mode 100644 tests/expected_json/backdoor.backdoor.txt delete mode 100644 tests/expected_json/backdoor.suicidal.txt delete mode 100644 tests/expected_json/const_state_variables.constable-states.txt delete mode 100644 tests/expected_json/constant-0.5.1.constant-function-asm.txt delete mode 100644 tests/expected_json/constant-0.5.1.constant-function-state.txt delete mode 100644 tests/expected_json/constant.constant-function-asm.txt delete mode 100644 tests/expected_json/constant.constant-function-state.txt delete mode 100644 tests/expected_json/controlled_delegatecall.controlled-delegatecall.txt delete mode 100644 tests/expected_json/deprecated_calls.deprecated-standards.txt delete mode 100644 tests/expected_json/erc20_indexed.erc20-indexed.txt delete mode 100644 tests/expected_json/external_function.external-function.txt delete mode 100644 tests/expected_json/external_function_2.external-function.txt delete mode 100644 tests/expected_json/incorrect_equality.incorrect-equality.txt delete mode 100644 tests/expected_json/incorrect_erc20_interface.erc20-interface.txt delete mode 100644 tests/expected_json/incorrect_erc721_interface.erc721-interface.txt delete mode 100644 tests/expected_json/inline_assembly_contract-0.5.1.assembly.txt delete mode 100644 tests/expected_json/inline_assembly_contract.assembly.txt delete mode 100644 tests/expected_json/inline_assembly_library-0.5.1.assembly.txt delete mode 100644 tests/expected_json/inline_assembly_library.assembly.txt delete mode 100644 tests/expected_json/locked_ether-0.5.1.locked-ether.txt delete mode 100644 tests/expected_json/locked_ether.locked-ether.txt delete mode 100644 tests/expected_json/low_level_calls.low-level-calls.txt delete mode 100644 tests/expected_json/multiple_calls_in_loop.calls-loop.txt delete mode 100644 tests/expected_json/naming_convention.naming-convention.txt delete mode 100644 tests/expected_json/old_solc.sol.json.solc-version.txt delete mode 100644 tests/expected_json/pragma.0.4.24.pragma.txt delete mode 100644 tests/expected_json/reentrancy-0.5.1-events.reentrancy-events.txt delete mode 100644 tests/expected_json/reentrancy-0.5.1.reentrancy-eth.txt delete mode 100644 tests/expected_json/reentrancy.reentrancy-eth.txt delete mode 100644 tests/expected_json/reentrancy.reentrancy-unlimited-gas.txt delete mode 100644 tests/expected_json/right_to_left_override.rtlo.txt delete mode 100644 tests/expected_json/shadowing_abstract.shadowing-abstract.txt delete mode 100644 tests/expected_json/shadowing_builtin_symbols.shadowing-builtin.txt delete mode 100644 tests/expected_json/shadowing_local_variable.shadowing-local.txt delete mode 100644 tests/expected_json/shadowing_state_variable.shadowing-state.txt delete mode 100644 tests/expected_json/solc_version_incorrect.solc-version.txt delete mode 100644 tests/expected_json/solc_version_incorrect_05.ast.json.solc-version.txt delete mode 100644 tests/expected_json/timestamp.timestamp.txt delete mode 100644 tests/expected_json/too_many_digits.too-many-digits.txt delete mode 100644 tests/expected_json/tx_origin-0.5.1.tx-origin.txt delete mode 100644 tests/expected_json/tx_origin.tx-origin.txt delete mode 100644 tests/expected_json/unchecked_send-0.5.1.unchecked-send.txt delete mode 100644 tests/expected_json/uninitialized-0.5.1.uninitialized-state.txt delete mode 100644 tests/expected_json/uninitialized.uninitialized-state.txt delete mode 100644 tests/expected_json/uninitialized_local_variable.uninitialized-local.txt delete mode 100644 tests/expected_json/uninitialized_storage_pointer.uninitialized-storage.txt delete mode 100644 tests/expected_json/unused_return.unused-return.txt delete mode 100644 tests/expected_json/unused_state.unused-state.txt delete mode 100644 tests/expected_json/void-cst.void-cst.txt diff --git a/examples/scripts/convert_to_ir.py b/examples/scripts/convert_to_ir.py index ebbefd911..fba619b9f 100644 --- a/examples/scripts/convert_to_ir.py +++ b/examples/scripts/convert_to_ir.py @@ -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 diff --git a/examples/scripts/data_dependency.py b/examples/scripts/data_dependency.py index 6a7db7fe5..c8fde13b4 100644 --- a/examples/scripts/data_dependency.py +++ b/examples/scripts/data_dependency.py @@ -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") diff --git a/examples/scripts/functions_called.py b/examples/scripts/functions_called.py index 8521a202b..192b9e943 100644 --- a/examples/scripts/functions_called.py +++ b/examples/scripts/functions_called.py @@ -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()") diff --git a/examples/scripts/functions_writing.py b/examples/scripts/functions_writing.py index 1fd83e0e4..61564972b 100644 --- a/examples/scripts/functions_writing.py +++ b/examples/scripts/functions_writing.py @@ -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") diff --git a/examples/scripts/possible_paths.py b/examples/scripts/possible_paths.py index b84d3651f..06b68c2bc 100644 --- a/examples/scripts/possible_paths.py +++ b/examples/scripts/possible_paths.py @@ -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 diff --git a/examples/scripts/taint_mapping.py b/examples/scripts/taint_mapping.py index c1754e635..a0c09ab1e 100644 --- a/examples/scripts/taint_mapping.py +++ b/examples/scripts/taint_mapping.py @@ -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) diff --git a/examples/scripts/variable_in_condition.py b/examples/scripts/variable_in_condition.py index 91efded15..e11e39608 100644 --- a/examples/scripts/variable_in_condition.py +++ b/examples/scripts/variable_in_condition.py @@ -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") diff --git a/slither/analyses/data_dependency/data_dependency.py b/slither/analyses/data_dependency/data_dependency.py index 27109db73..b9fedd341 100644 --- a/slither/analyses/data_dependency/data_dependency.py +++ b/slither/analyses/data_dependency/data_dependency.py @@ -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) diff --git a/slither/core/cfg/node.py b/slither/core/cfg/node.py index a27259c86..ae07fa526 100644 --- a/slither/core/cfg/node.py +++ b/slither/core/cfg/node.py @@ -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: diff --git a/slither/core/children/child_node.py b/slither/core/children/child_node.py index ee0b05aa7..1c745f414 100644 --- a/slither/core/children/child_node.py +++ b/slither/core/children/child_node.py @@ -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 diff --git a/slither/core/children/child_slither.py b/slither/core/children/child_slither.py deleted file mode 100644 index 98c70c302..000000000 --- a/slither/core/children/child_slither.py +++ /dev/null @@ -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 diff --git a/slither/core/compilation_unit.py b/slither/core/compilation_unit.py new file mode 100644 index 000000000..c56ab7d30 --- /dev/null +++ b/slither/core/compilation_unit.py @@ -0,0 +1,259 @@ +import math +from collections import defaultdict +from typing import Optional, Dict, List, Set, Union, TYPE_CHECKING, Tuple + +from crytic_compile import CompilationUnit +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] = {} + + @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 + + # 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 diff --git a/slither/core/declarations/contract.py b/slither/core/declarations/contract.py index f16b0e70c..28e6faf5a 100644 --- a/slither/core/declarations/contract.py +++ b/slither/core/declarations/contract.py @@ -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 @@ -29,16 +28,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 @@ -79,6 +80,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 @@ -565,7 +568,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 @@ -975,9 +978,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 ################################################################################### @@ -992,12 +995,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 @@ -1028,7 +1030,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 @@ -1095,14 +1097,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) @@ -1121,7 +1123,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 ) @@ -1130,7 +1132,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) @@ -1157,7 +1159,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 @@ -1168,7 +1170,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 diff --git a/slither/core/declarations/function.py b/slither/core/declarations/function.py index dbc8c3921..c840f9aee 100644 --- a/slither/core/declarations/function.py +++ b/slither/core/declarations/function.py @@ -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) diff --git a/slither/core/slither_core.py b/slither/core/slither_core.py index 2226b9879..5bbe467cb 100644 --- a/slither/core/slither_core.py +++ b/slither/core/slither_core.py @@ -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,83 +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 - ################################################################################### - ################################################################################### - # region Source code - ################################################################################### - ################################################################################### + self._compilation_units: List[SlitherCompilationUnit] = [] - @property - def source_code(self) -> Dict[str, str]: - """ {filename: source_code (str)}: source code """ - return self._raw_source_code + self._contracts: List[Contract] = [] + self._contracts_derived: List[Contract] = [] @property - def source_units(self) -> Dict[int, str]: - return self._source_units + def compilation_units(self) -> List[SlitherCompilationUnit]: + return list(self._compilation_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 ################################################################################### @@ -166,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: @@ -189,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) + 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 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 + # 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 ################################################################################### @@ -325,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"] @@ -429,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: """ @@ -456,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 diff --git a/slither/core/source_mapping/source_mapping.py b/slither/core/source_mapping/source_mapping.py index 943b82c83..9d18dd50c 100644 --- a/slither/core/source_mapping/source_mapping.py +++ b/slither/core/source_mapping/source_mapping.py @@ -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) diff --git a/slither/detectors/abstract_detector.py b/slither/detectors/abstract_detector.py index 2fb0d076a..15f6f84dd 100644 --- a/slither/detectors/abstract_detector.py +++ b/slither/detectors/abstract_detector.py @@ -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 diff --git a/slither/detectors/attributes/const_functions_asm.py b/slither/detectors/attributes/const_functions_asm.py index 28c706c69..bbdc53d01 100644 --- a/slither/detectors/attributes/const_functions_asm.py +++ b/slither/detectors/attributes/const_functions_asm.py @@ -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) diff --git a/slither/detectors/attributes/const_functions_state.py b/slither/detectors/attributes/const_functions_state.py index 556bc0c00..9548eaf71 100644 --- a/slither/detectors/attributes/const_functions_state.py +++ b/slither/detectors/attributes/const_functions_state.py @@ -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: diff --git a/slither/detectors/attributes/constant_pragma.py b/slither/detectors/attributes/constant_pragma.py index d542d70a0..98c12d338 100644 --- a/slither/detectors/attributes/constant_pragma.py +++ b/slither/detectors/attributes/constant_pragma.py @@ -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: diff --git a/slither/detectors/attributes/incorrect_solc.py b/slither/detectors/attributes/incorrect_solc.py index 35ce900db..25715d5fe 100644 --- a/slither/detectors/attributes/incorrect_solc.py +++ b/slither/detectors/attributes/incorrect_solc.py @@ -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 diff --git a/slither/detectors/attributes/locked_ether.py b/slither/detectors/attributes/locked_ether.py index efcbcef8b..7c918dd70 100644 --- a/slither/detectors/attributes/locked_ether.py +++ b/slither/detectors/attributes/locked_ether.py @@ -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"] diff --git a/slither/detectors/attributes/unimplemented_interface.py b/slither/detectors/attributes/unimplemented_interface.py index 8b3e11c8a..08ad36b92 100644 --- a/slither/detectors/attributes/unimplemented_interface.py +++ b/slither/detectors/attributes/unimplemented_interface.py @@ -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 diff --git a/slither/detectors/compiler_bugs/enum_conversion.py b/slither/detectors/compiler_bugs/enum_conversion.py index 20e74b0a4..3dc54dfd2 100644 --- a/slither/detectors/compiler_bugs/enum_conversion.py +++ b/slither/detectors/compiler_bugs/enum_conversion.py @@ -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"] diff --git a/slither/detectors/compiler_bugs/public_mapping_nested.py b/slither/detectors/compiler_bugs/public_mapping_nested.py index e3bbcc944..d499e3a10 100644 --- a/slither/detectors/compiler_bugs/public_mapping_nested.py +++ b/slither/detectors/compiler_bugs/public_mapping_nested.py @@ -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: diff --git a/slither/detectors/compiler_bugs/reused_base_constructor.py b/slither/detectors/compiler_bugs/reused_base_constructor.py index c1b431493..00a285f1b 100644 --- a/slither/detectors/compiler_bugs/reused_base_constructor.py +++ b/slither/detectors/compiler_bugs/reused_base_constructor.py @@ -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 diff --git a/slither/detectors/compiler_bugs/storage_ABIEncoderV2_array.py b/slither/detectors/compiler_bugs/storage_ABIEncoderV2_array.py index cc1aa9d4f..9c62de637 100644 --- a/slither/detectors/compiler_bugs/storage_ABIEncoderV2_array.py +++ b/slither/detectors/compiler_bugs/storage_ABIEncoderV2_array.py @@ -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 diff --git a/slither/detectors/compiler_bugs/storage_signed_integer_array.py b/slither/detectors/compiler_bugs/storage_signed_integer_array.py index 4683bc9fe..453e5e712 100644 --- a/slither/detectors/compiler_bugs/storage_signed_integer_array.py +++ b/slither/detectors/compiler_bugs/storage_signed_integer_array.py @@ -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) diff --git a/slither/detectors/compiler_bugs/uninitialized_function_ptr_in_constructor.py b/slither/detectors/compiler_bugs/uninitialized_function_ptr_in_constructor.py index b0a94df0c..c22142909 100644 --- a/slither/detectors/compiler_bugs/uninitialized_function_ptr_in_constructor.py +++ b/slither/detectors/compiler_bugs/uninitialized_function_ptr_in_constructor.py @@ -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: diff --git a/slither/detectors/erc/incorrect_erc20_interface.py b/slither/detectors/erc/incorrect_erc20_interface.py index 5c7904b5f..c6b16f656 100644 --- a/slither/detectors/erc/incorrect_erc20_interface.py +++ b/slither/detectors/erc/incorrect_erc20_interface.py @@ -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: diff --git a/slither/detectors/erc/incorrect_erc721_interface.py b/slither/detectors/erc/incorrect_erc721_interface.py index f25205242..adb74638f 100644 --- a/slither/detectors/erc/incorrect_erc721_interface.py +++ b/slither/detectors/erc/incorrect_erc721_interface.py @@ -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: diff --git a/slither/detectors/examples/backdoor.py b/slither/detectors/examples/backdoor.py index 36d8326e4..1e73fa814 100644 --- a/slither/detectors/examples/backdoor.py +++ b/slither/detectors/examples/backdoor.py @@ -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: diff --git a/slither/detectors/functions/unimplemented.py b/slither/detectors/functions/unimplemented.py index 20f538fc0..a91cfdc42 100644 --- a/slither/detectors/functions/unimplemented.py +++ b/slither/detectors/functions/unimplemented.py @@ -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"] diff --git a/slither/detectors/operations/bad_prng.py b/slither/detectors/operations/bad_prng.py index 39adc7840..c74581588 100644 --- a/slither/detectors/operations/bad_prng.py +++ b/slither/detectors/operations/bad_prng.py @@ -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: diff --git a/slither/detectors/operations/missing_events_access_control.py b/slither/detectors/operations/missing_events_access_control.py index eb371d474..a1acb7afc 100644 --- a/slither/detectors/operations/missing_events_access_control.py +++ b/slither/detectors/operations/missing_events_access_control.py @@ -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"] diff --git a/slither/detectors/operations/missing_events_arithmetic.py b/slither/detectors/operations/missing_events_arithmetic.py index d77289715..cc6eee4be 100644 --- a/slither/detectors/operations/missing_events_arithmetic.py +++ b/slither/detectors/operations/missing_events_arithmetic.py @@ -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"] diff --git a/slither/detectors/operations/missing_zero_address_validation.py b/slither/detectors/operations/missing_zero_address_validation.py index db2c2f61d..2476345bb 100644 --- a/slither/detectors/operations/missing_zero_address_validation.py +++ b/slither/detectors/operations/missing_zero_address_validation.py @@ -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(): diff --git a/slither/detectors/operations/unused_return_values.py b/slither/detectors/operations/unused_return_values.py index 4d98f71da..7d004b1ee 100644 --- a/slither/detectors/operations/unused_return_values.py +++ b/slither/detectors/operations/unused_return_values.py @@ -67,7 +67,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 diff --git a/slither/detectors/reentrancy/token.py b/slither/detectors/reentrancy/token.py index e6beceb91..95b04d5f4 100644 --- a/slither/detectors/reentrancy/token.py +++ b/slither/detectors/reentrancy/token.py @@ -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"] diff --git a/slither/detectors/slither/name_reused.py b/slither/detectors/slither/name_reused.py index 9937d351f..75e36899b 100644 --- a/slither/detectors/slither/name_reused.py +++ b/slither/detectors/slither/name_reused.py @@ -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"] diff --git a/slither/detectors/source/rtlo.py b/slither/detectors/source/rtlo.py index 592d5395d..a7dc434e9 100644 --- a/slither/detectors/source/rtlo.py +++ b/slither/detectors/source/rtlo.py @@ -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) diff --git a/slither/detectors/statements/array_length_assignment.py b/slither/detectors/statements/array_length_assignment.py index 46b946d9e..18507d3f0 100644 --- a/slither/detectors/statements/array_length_assignment.py +++ b/slither/detectors/statements/array_length_assignment.py @@ -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) diff --git a/slither/detectors/statements/calls_in_loop.py b/slither/detectors/statements/calls_in_loop.py index f612fd728..5a159792e 100644 --- a/slither/detectors/statements/calls_in_loop.py +++ b/slither/detectors/statements/calls_in_loop.py @@ -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 diff --git a/slither/detectors/statements/controlled_delegatecall.py b/slither/detectors/statements/controlled_delegatecall.py index a5975abb6..057c7ae6d 100644 --- a/slither/detectors/statements/controlled_delegatecall.py +++ b/slither/detectors/statements/controlled_delegatecall.py @@ -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 diff --git a/slither/detectors/statements/costly_operations_in_loop.py b/slither/detectors/statements/costly_operations_in_loop.py index c0b0a880d..83af4e1aa 100644 --- a/slither/detectors/statements/costly_operations_in_loop.py +++ b/slither/detectors/statements/costly_operations_in_loop.py @@ -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 diff --git a/slither/detectors/statements/incorrect_strict_equality.py b/slither/detectors/statements/incorrect_strict_equality.py index d7a6bc91d..f28e22aeb 100644 --- a/slither/detectors/statements/incorrect_strict_equality.py +++ b/slither/detectors/statements/incorrect_strict_equality.py @@ -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 diff --git a/slither/detectors/statements/too_many_digits.py b/slither/detectors/statements/too_many_digits.py index ec0d557dc..4737ab2f5 100644 --- a/slither/detectors/statements/too_many_digits.py +++ b/slither/detectors/statements/too_many_digits.py @@ -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 diff --git a/slither/detectors/statements/unprotected_upgradeable.py b/slither/detectors/statements/unprotected_upgradeable.py index e3eb87369..6ebca08b4 100644 --- a/slither/detectors/statements/unprotected_upgradeable.py +++ b/slither/detectors/statements/unprotected_upgradeable.py @@ -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: diff --git a/slither/detectors/variables/possible_const_state_variables.py b/slither/detectors/variables/possible_const_state_variables.py index 4df63db09..70a9ab65a 100644 --- a/slither/detectors/variables/possible_const_state_variables.py +++ b/slither/detectors/variables/possible_const_state_variables.py @@ -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) diff --git a/slither/detectors/variables/uninitialized_local_variables.py b/slither/detectors/variables/uninitialized_local_variables.py index 5547d2225..9923816b4 100644 --- a/slither/detectors/variables/uninitialized_local_variables.py +++ b/slither/detectors/variables/uninitialized_local_variables.py @@ -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: diff --git a/slither/detectors/variables/uninitialized_state_variables.py b/slither/detectors/variables/uninitialized_state_variables.py index 09e449e2d..eeab8e754 100644 --- a/slither/detectors/variables/uninitialized_state_variables.py +++ b/slither/detectors/variables/uninitialized_state_variables.py @@ -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: diff --git a/slither/detectors/variables/uninitialized_storage_variables.py b/slither/detectors/variables/uninitialized_storage_variables.py index ab96d1260..5d47af66c 100644 --- a/slither/detectors/variables/uninitialized_storage_variables.py +++ b/slither/detectors/variables/uninitialized_storage_variables.py @@ -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 = [ diff --git a/slither/detectors/variables/unused_state_variables.py b/slither/detectors/variables/unused_state_variables.py index 53c4fefe8..838ca081b 100644 --- a/slither/detectors/variables/unused_state_variables.py +++ b/slither/detectors/variables/unused_state_variables.py @@ -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) diff --git a/slither/formatters/attributes/const_functions.py b/slither/formatters/attributes/const_functions.py index 95fc6d8c8..5aaeb0b17 100644 --- a/slither/formatters/attributes/const_functions.py +++ b/slither/formatters/attributes/const_functions.py @@ -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")) diff --git a/slither/formatters/functions/external_function.py b/slither/formatters/functions/external_function.py index dd2eaf362..1959b7a40 100644 --- a/slither/formatters/functions/external_function.py +++ b/slither/formatters/functions/external_function.py @@ -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 diff --git a/slither/formatters/naming_convention/naming_convention.py b/slither/formatters/naming_convention/naming_convention.py index ccf1ba037..49f675cce 100644 --- a/slither/formatters/naming_convention/naming_convention.py +++ b/slither/formatters/naming_convention/naming_convention.py @@ -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 diff --git a/slither/formatters/variables/possible_const_state_variables.py b/slither/formatters/variables/possible_const_state_variables.py index 2736a4b59..f06e23158 100644 --- a/slither/formatters/variables/possible_const_state_variables.py +++ b/slither/formatters/variables/possible_const_state_variables.py @@ -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( diff --git a/slither/formatters/variables/unused_state_variables.py b/slither/formatters/variables/unused_state_variables.py index 2bcae09d3..8e0852a17 100644 --- a/slither/formatters/variables/unused_state_variables.py +++ b/slither/formatters/variables/unused_state_variables.py @@ -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] diff --git a/slither/printers/call/call_graph.py b/slither/printers/call/call_graph.py index c74ece8fa..5adc24a89 100644 --- a/slither/printers/call/call_graph.py +++ b/slither/printers/call/call_graph.py @@ -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)) diff --git a/slither/printers/guidance/echidna.py b/slither/printers/guidance/echidna.py index 584487b5e..f60305604 100644 --- a/slither/printers/guidance/echidna.py +++ b/slither/printers/guidance/echidna.py @@ -79,8 +79,13 @@ 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"): + if ( + f.contract.compilation_unit.crytic_compile + and f.contract.compilation_unit.crytic_compile.compiler_version + ): + if not f.contract.compilation_unit.crytic_compile.compiler_version.version.startswith( + "0.4" + ): return True if f.payable: return False @@ -104,10 +109,12 @@ def _is_constant(f: Function) -> bool: # pylint: disable=too-many-branches 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 + f.contract.compilation_unit.crytic_compile + and f.contract.compilation_unit.crytic_compile.compiler_version ): - if f.contract.slither.crytic_compile.compiler_version.version.startswith("0.4"): + if f.contract.compilation_unit.crytic_compile.compiler_version.version.startswith( + "0.4" + ): return False else: return False diff --git a/slither/printers/summary/evm.py b/slither/printers/summary/evm.py index e14365062..c3c7c9a86 100644 --- a/slither/printers/summary/evm.py +++ b/slither/printers/summary/evm.py @@ -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( @@ -61,7 +67,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)) diff --git a/slither/printers/summary/human_summary.py b/slither/printers/summary/human_summary.py index b47c51ddc..c3c94c9a1 100644 --- a/slither/printers/summary/human_summary.py +++ b/slither/printers/summary/human_summary.py @@ -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: diff --git a/slither/printers/summary/slithir.py b/slither/printers/summary/slithir.py index 6c7045d66..81ccacea8 100644 --- a/slither/printers/summary/slithir.py +++ b/slither/printers/summary/slithir.py @@ -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 diff --git a/slither/slither.py b/slither/slither.py index 4d33a54c1..3bc8c1225 100644 --- a/slither/slither.py +++ b/slither/slither.py @@ -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 diff --git a/slither/slithir/convert.py b/slither/slithir/convert.py index f9fdc79cf..072e791d4 100644 --- a/slither/slithir/convert.py +++ b/slither/slithir/convert.py @@ -76,6 +76,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") @@ -381,13 +382,13 @@ 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) + if compilation_unit.crytic_compile: + bytecode = compilation_unit.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" @@ -399,8 +400,8 @@ def _convert_type_contract(ir, slither): 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) + if compilation_unit.crytic_compile: + bytecode = compilation_unit.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" @@ -477,7 +478,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 @@ -581,7 +582,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 @@ -646,7 +647,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) @@ -953,7 +954,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")) @@ -1155,7 +1156,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, diff --git a/slither/slithir/operations/high_level_call.py b/slither/slithir/operations/high_level_call.py index e10943de7..c5a7f4e25 100644 --- a/slither/slithir/operations/high_level_call.py +++ b/slither/slithir/operations/high_level_call.py @@ -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): diff --git a/slither/slithir/operations/new_contract.py b/slither/slithir/operations/new_contract.py index e5fc112bb..b5894c6ff 100644 --- a/slither/slithir/operations/new_contract.py +++ b/slither/slithir/operations/new_contract.py @@ -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 ################################################################################### diff --git a/slither/solc_parsing/cfg/node.py b/slither/solc_parsing/cfg/node.py index 2d2bec0f3..d7009fdda 100644 --- a/slither/solc_parsing/cfg/node.py +++ b/slither/solc_parsing/cfg/node.py @@ -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 diff --git a/slither/solc_parsing/declarations/contract.py b/slither/solc_parsing/declarations/contract.py index 54067b8fe..dde0053c4 100644 --- a/slither/solc_parsing/declarations/contract.py +++ b/slither/solc_parsing/declarations/contract.py @@ -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) diff --git a/slither/solc_parsing/declarations/event.py b/slither/solc_parsing/declarations/event.py index 29fee7b4a..1f8904fc1 100644 --- a/slither/solc_parsing/declarations/event.py +++ b/slither/solc_parsing/declarations/event.py @@ -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) diff --git a/slither/solc_parsing/declarations/function.py b/slither/solc_parsing/declarations/function.py index 3107e4974..2c369c420 100644 --- a/slither/solc_parsing/declarations/function.py +++ b/slither/solc_parsing/declarations/function.py @@ -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"] diff --git a/slither/solc_parsing/declarations/modifier.py b/slither/solc_parsing/declarations/modifier.py index bccbf78b5..ddd854d70 100644 --- a/slither/solc_parsing/declarations/modifier.py +++ b/slither/solc_parsing/declarations/modifier.py @@ -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 diff --git a/slither/solc_parsing/declarations/structure_contract.py b/slither/solc_parsing/declarations/structure_contract.py index 1fea022c6..9c3784ea9 100644 --- a/slither/solc_parsing/declarations/structure_contract.py +++ b/slither/solc_parsing/declarations/structure_contract.py @@ -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) diff --git a/slither/solc_parsing/declarations/structure_top_level.py b/slither/solc_parsing/declarations/structure_top_level.py index fb26426d4..1babca03a 100644 --- a/slither/solc_parsing/declarations/structure_top_level.py +++ b/slither/solc_parsing/declarations/structure_top_level.py @@ -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) diff --git a/slither/solc_parsing/expressions/expression_parsing.py b/slither/solc_parsing/expressions/expression_parsing.py index 480bdc937..d1306fce2 100644 --- a/slither/solc_parsing/expressions/expression_parsing.py +++ b/slither/solc_parsing/expressions/expression_parsing.py @@ -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") diff --git a/slither/solc_parsing/slitherSolc.py b/slither/solc_parsing/slither_compilation_unit_solc.py similarity index 87% rename from slither/solc_parsing/slitherSolc.py rename to slither/solc_parsing/slither_compilation_unit_solc.py index e4638e974..3dd604fe7 100644 --- a/slither/solc_parsing/slitherSolc.py +++ b/slither/solc_parsing/slither_compilation_unit_solc.py @@ -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() diff --git a/slither/solc_parsing/solidity_types/type_parsing.py b/slither/solc_parsing/solidity_types/type_parsing.py index ebf93a423..d1c4e83cd 100644 --- a/slither/solc_parsing/solidity_types/type_parsing.py +++ b/slither/solc_parsing/solidity_types/type_parsing.py @@ -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) diff --git a/slither/solc_parsing/yul/parse_yul.py b/slither/solc_parsing/yul/parse_yul.py index 4a47ef800..3640bff48 100644 --- a/slither/solc_parsing/yul/parse_yul.py +++ b/slither/solc_parsing/yul/parse_yul.py @@ -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 diff --git a/slither/tools/erc_conformance/__main__.py b/slither/tools/erc_conformance/__main__.py index 9eb977ff6..51f546a46 100644 --- a/slither/tools/erc_conformance/__main__.py +++ b/slither/tools/erc_conformance/__main__.py @@ -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) diff --git a/slither/tools/flattening/flattening.py b/slither/tools/flattening/flattening.py index 2e3d6c650..13132938a 100644 --- a/slither/tools/flattening/flattening.py +++ b/slither/tools/flattening/flattening.py @@ -338,10 +338,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: diff --git a/slither/tools/mutator/utils/generic_patching.py b/slither/tools/mutator/utils/generic_patching.py index 4e335a755..d03bfaa4f 100644 --- a/slither/tools/mutator/utils/generic_patching.py +++ b/slither/tools/mutator/utils/generic_patching.py @@ -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"] diff --git a/slither/tools/possible_paths/possible_paths.py b/slither/tools/possible_paths/possible_paths.py index 75ab29de4..9e52ed6b7 100644 --- a/slither/tools/possible_paths/possible_paths.py +++ b/slither/tools/possible_paths/possible_paths.py @@ -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), diff --git a/slither/tools/properties/__main__.py b/slither/tools/properties/__main__.py index 4a2dc8c99..91c990669 100644 --- a/slither/tools/properties/__main__.py +++ b/slither/tools/properties/__main__.py @@ -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) diff --git a/slither/tools/properties/properties/erc20.py b/slither/tools/properties/properties/erc20.py index 600c0b79c..dd13e3a0f 100644 --- a/slither/tools/properties/properties/erc20.py +++ b/slither/tools/properties/properties/erc20.py @@ -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]) diff --git a/slither/tools/properties/properties/ercs/erc20/unit_tests/truffle.py b/slither/tools/properties/properties/ercs/erc20/unit_tests/truffle.py index 58ac93d56..f54dcec0e 100644 --- a/slither/tools/properties/properties/ercs/erc20/unit_tests/truffle.py +++ b/slither/tools/properties/properties/ercs/erc20/unit_tests/truffle.py @@ -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) diff --git a/slither/tools/upgradeability/__main__.py b/slither/tools/upgradeability/__main__.py index 5bcc23ca1..9e8821a03 100644 --- a/slither/tools/upgradeability/__main__.py +++ b/slither/tools/upgradeability/__main__.py @@ -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: diff --git a/slither/tools/upgradeability/checks/abstract_checks.py b/slither/tools/upgradeability/checks/abstract_checks.py index 9935c39b1..a6aca61bf 100644 --- a/slither/tools/upgradeability/checks/abstract_checks.py +++ b/slither/tools/upgradeability/checks/abstract_checks.py @@ -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.markdown_root + ) output.data["check"] = self.ARGUMENT diff --git a/slither/tools/upgradeability/checks/initialization.py b/slither/tools/upgradeability/checks/initialization.py index 4a5f65187..7f13be7ee 100644 --- a/slither/tools/upgradeability/checks/initialization.py +++ b/slither/tools/upgradeability/checks/initialization.py @@ -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 [] diff --git a/slither/utils/output.py b/slither/utils/output.py index f1ded8a38..c74a439ff 100644 --- a/slither/utils/output.py +++ b/slither/utils/output.py @@ -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") @@ -502,7 +505,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. @@ -517,7 +520,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, @@ -530,7 +533,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): diff --git a/tests/detectors/locked-ether/locked_ether-0.5.1.sol.0.5.1.LockedEther.json b/tests/detectors/locked-ether/locked_ether-0.5.1.sol.0.5.1.LockedEther.json index 50ace190c..00e8497d6 100644 --- a/tests/detectors/locked-ether/locked_ether-0.5.1.sol.0.5.1.LockedEther.json +++ b/tests/detectors/locked-ether/locked_ether-0.5.1.sol.0.5.1.LockedEther.json @@ -68,9 +68,9 @@ } } ], - "description": "Contract locking ether found in :\n\tContract OnlyLocked (tests/detectors/locked-ether/locked_ether-0.5.1.sol#26) has payable functions:\n\t - Locked.receive() (tests/detectors/locked-ether/locked_ether-0.5.1.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/locked_ether-0.5.1.sol#L26) has payable functions:\n\t - [Locked.receive()](tests/detectors/locked-ether/locked_ether-0.5.1.sol#L4-L6)\n\tBut does not have a function to withdraw the ether\n", - "id": "38b2d47301e59ffa598ec48a8e874e6a7070d6cf4e4ab2909f33a8b72fc28a4b", + "description": "Contract locking ether found:\n\tContract OnlyLocked (tests/detectors/locked-ether/locked_ether-0.5.1.sol#26) has payable functions:\n\t - Locked.receive() (tests/detectors/locked-ether/locked_ether-0.5.1.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/locked_ether-0.5.1.sol#L26) has payable functions:\n\t - [Locked.receive()](tests/detectors/locked-ether/locked_ether-0.5.1.sol#L4-L6)\n\tBut does not have a function to withdraw the ether\n", + "id": "a2fad0e9c8a75e55b8c548dd184dc33d78142ea5bac9c36cdc5eb174e56d689c", "check": "locked-ether", "impact": "Medium", "confidence": "High" diff --git a/tests/detectors/locked-ether/locked_ether.sol.0.4.25.LockedEther.json b/tests/detectors/locked-ether/locked_ether.sol.0.4.25.LockedEther.json index 9a081738d..84702eccc 100644 --- a/tests/detectors/locked-ether/locked_ether.sol.0.4.25.LockedEther.json +++ b/tests/detectors/locked-ether/locked_ether.sol.0.4.25.LockedEther.json @@ -68,9 +68,9 @@ } } ], - "description": "Contract locking ether found in :\n\tContract OnlyLocked (tests/detectors/locked-ether/locked_ether.sol#26) has payable functions:\n\t - Locked.receive() (tests/detectors/locked-ether/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/locked_ether.sol#L26) has payable functions:\n\t - [Locked.receive()](tests/detectors/locked-ether/locked_ether.sol#L4-L6)\n\tBut does not have a function to withdraw the ether\n", - "id": "38b2d47301e59ffa598ec48a8e874e6a7070d6cf4e4ab2909f33a8b72fc28a4b", + "description": "Contract locking ether found:\n\tContract OnlyLocked (tests/detectors/locked-ether/locked_ether.sol#26) has payable functions:\n\t - Locked.receive() (tests/detectors/locked-ether/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/locked_ether.sol#L26) has payable functions:\n\t - [Locked.receive()](tests/detectors/locked-ether/locked_ether.sol#L4-L6)\n\tBut does not have a function to withdraw the ether\n", + "id": "a2fad0e9c8a75e55b8c548dd184dc33d78142ea5bac9c36cdc5eb174e56d689c", "check": "locked-ether", "impact": "Medium", "confidence": "High" diff --git a/tests/detectors/pragma/pragma.0.4.24.sol.0.4.25.ConstantPragma.json b/tests/detectors/pragma/pragma.0.4.24.sol.0.4.25.ConstantPragma.json index f4126bd95..3294de4aa 100644 --- a/tests/detectors/pragma/pragma.0.4.24.sol.0.4.25.ConstantPragma.json +++ b/tests/detectors/pragma/pragma.0.4.24.sol.0.4.25.ConstantPragma.json @@ -55,9 +55,9 @@ } } ], - "description": "Different versions of Solidity is used in :\n\t- Version used: ['^0.4.23', '^0.4.24']\n\t- ^0.4.23 (tests/detectors/pragma/pragma.0.4.23.sol#1)\n\t- ^0.4.24 (tests/detectors/pragma/pragma.0.4.24.sol#1)\n", - "markdown": "Different versions of Solidity is used in :\n\t- Version used: ['^0.4.23', '^0.4.24']\n\t- [^0.4.23](tests/detectors/pragma/pragma.0.4.23.sol#L1)\n\t- [^0.4.24](tests/detectors/pragma/pragma.0.4.24.sol#L1)\n", - "id": "1379d7b43e8c8bc939547079131e06fc888bb82740880718f8ff3409a6d5173e", + "description": "Different versions of Solidity is used:\n\t- Version used: ['^0.4.23', '^0.4.24']\n\t- ^0.4.23 (tests/detectors/pragma/pragma.0.4.23.sol#1)\n\t- ^0.4.24 (tests/detectors/pragma/pragma.0.4.24.sol#1)\n", + "markdown": "Different versions of Solidity is used:\n\t- Version used: ['^0.4.23', '^0.4.24']\n\t- [^0.4.23](tests/detectors/pragma/pragma.0.4.23.sol#L1)\n\t- [^0.4.24](tests/detectors/pragma/pragma.0.4.24.sol#L1)\n", + "id": "516a4869445e911756ede6d0f03a7b3fbb27a37497cf9ec86e1fe3e6fba6a095", "check": "pragma", "impact": "Informational", "confidence": "High" diff --git a/tests/expected_json/arbitrary_send-0.5.1.arbitrary-send.txt b/tests/expected_json/arbitrary_send-0.5.1.arbitrary-send.txt deleted file mode 100644 index ac242770b..000000000 --- a/tests/expected_json/arbitrary_send-0.5.1.arbitrary-send.txt +++ /dev/null @@ -1,10 +0,0 @@ - -Test.direct() (tests/arbitrary_send-0.5.1.sol#11-13) sends eth to arbitrary user - Dangerous calls: - - msg.sender.send(address(this).balance) (tests/arbitrary_send-0.5.1.sol#12) -Test.indirect() (tests/arbitrary_send-0.5.1.sol#19-21) sends eth to arbitrary user - Dangerous calls: - - destination.send(address(this).balance) (tests/arbitrary_send-0.5.1.sol#20) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#functions-that-send-ether-to-arbitrary-destinations -tests/arbitrary_send-0.5.1.sol analyzed (1 contracts with 1 detectors), 2 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/arbitrary_send.arbitrary-send.txt b/tests/expected_json/arbitrary_send.arbitrary-send.txt deleted file mode 100644 index b1c94ec70..000000000 --- a/tests/expected_json/arbitrary_send.arbitrary-send.txt +++ /dev/null @@ -1,10 +0,0 @@ - -Test.direct() (tests/arbitrary_send.sol#11-13) sends eth to arbitrary user - Dangerous calls: - - msg.sender.send(address(this).balance) (tests/arbitrary_send.sol#12) -Test.indirect() (tests/arbitrary_send.sol#19-21) sends eth to arbitrary user - Dangerous calls: - - destination.send(address(this).balance) (tests/arbitrary_send.sol#20) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#functions-that-send-ether-to-arbitrary-destinations -tests/arbitrary_send.sol analyzed (1 contracts with 1 detectors), 2 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/backdoor.backdoor.txt b/tests/expected_json/backdoor.backdoor.txt deleted file mode 100644 index e5251c854..000000000 --- a/tests/expected_json/backdoor.backdoor.txt +++ /dev/null @@ -1,6 +0,0 @@ - -Backdoor function found in C.i_am_a_backdoor() (tests/backdoor.sol#4-6) -Reference: https://github.com/trailofbits/slither/wiki/Adding-a-new-detector -tests/backdoor.sol analyzed (1 contracts with 1 detectors), 1 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration -INFO:Slither:/home/travis/build/crytic/slither/scripts/../tests/expected_json/backdoor.backdoor.json exists already, the overwrite is prevented diff --git a/tests/expected_json/backdoor.suicidal.txt b/tests/expected_json/backdoor.suicidal.txt deleted file mode 100644 index 5194bb9ae..000000000 --- a/tests/expected_json/backdoor.suicidal.txt +++ /dev/null @@ -1,6 +0,0 @@ - -C.i_am_a_backdoor() (tests/backdoor.sol#4-6) allows anyone to destruct the contract -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#suicidal -tests/backdoor.sol analyzed (1 contracts with 1 detectors), 1 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration -INFO:Slither:/home/travis/build/crytic/slither/scripts/../tests/expected_json/backdoor.suicidal.json exists already, the overwrite is prevented diff --git a/tests/expected_json/const_state_variables.constable-states.txt b/tests/expected_json/const_state_variables.constable-states.txt deleted file mode 100644 index 7435ba0db..000000000 --- a/tests/expected_json/const_state_variables.constable-states.txt +++ /dev/null @@ -1,10 +0,0 @@ - -A.myFriendsAddress (tests/const_state_variables.sol#7) should be constant -A.test (tests/const_state_variables.sol#10) should be constant -A.text2 (tests/const_state_variables.sol#14) should be constant -B.mySistersAddress (tests/const_state_variables.sol#26) should be constant -MyConc.should_be_constant (tests/const_state_variables.sol#42) should be constant -MyConc.should_be_constant_2 (tests/const_state_variables.sol#43) should be constant -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#state-variables-that-could-be-declared-constant -tests/const_state_variables.sol analyzed (3 contracts with 1 detectors), 6 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/constant-0.5.1.constant-function-asm.txt b/tests/expected_json/constant-0.5.1.constant-function-asm.txt deleted file mode 100644 index 46f078ea9..000000000 --- a/tests/expected_json/constant-0.5.1.constant-function-asm.txt +++ /dev/null @@ -1,2 +0,0 @@ -tests/constant-0.5.1.sol analyzed (1 contracts with 1 detectors), 0 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/constant-0.5.1.constant-function-state.txt b/tests/expected_json/constant-0.5.1.constant-function-state.txt deleted file mode 100644 index 46f078ea9..000000000 --- a/tests/expected_json/constant-0.5.1.constant-function-state.txt +++ /dev/null @@ -1,2 +0,0 @@ -tests/constant-0.5.1.sol analyzed (1 contracts with 1 detectors), 0 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/constant.constant-function-asm.txt b/tests/expected_json/constant.constant-function-asm.txt deleted file mode 100644 index 774564059..000000000 --- a/tests/expected_json/constant.constant-function-asm.txt +++ /dev/null @@ -1,5 +0,0 @@ - -Constant.test_assembly_bug() (tests/constant.sol#22-24) is declared view but contains assembly code -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#constant-functions-using-assembly-code -tests/constant.sol analyzed (1 contracts with 1 detectors), 1 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/constant.constant-function-state.txt b/tests/expected_json/constant.constant-function-state.txt deleted file mode 100644 index 7c48fc925..000000000 --- a/tests/expected_json/constant.constant-function-state.txt +++ /dev/null @@ -1,8 +0,0 @@ - -Constant.test_view_bug() (tests/constant.sol#5-7) is declared view but changes state variables: - - Constant.a (tests/constant.sol#3) -Constant.test_constant_bug() (tests/constant.sol#9-11) is declared view but changes state variables: - - Constant.a (tests/constant.sol#3) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#constant-functions-changing-the-state -tests/constant.sol analyzed (1 contracts with 1 detectors), 2 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/controlled_delegatecall.controlled-delegatecall.txt b/tests/expected_json/controlled_delegatecall.controlled-delegatecall.txt deleted file mode 100644 index 454c2fae2..000000000 --- a/tests/expected_json/controlled_delegatecall.controlled-delegatecall.txt +++ /dev/null @@ -1,8 +0,0 @@ - -C.bad_delegate_call(bytes) (tests/controlled_delegatecall.sol#8-11) uses delegatecall to a input-controlled function id - - addr_bad.delegatecall(data) (tests/controlled_delegatecall.sol#10) -C.bad_delegate_call2(bytes) (tests/controlled_delegatecall.sol#18-20) uses delegatecall to a input-controlled function id - - addr_bad.delegatecall(abi.encode(func_id,data)) (tests/controlled_delegatecall.sol#19) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#controlled-delegatecall -tests/controlled_delegatecall.sol analyzed (1 contracts with 1 detectors), 2 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/deprecated_calls.deprecated-standards.txt b/tests/expected_json/deprecated_calls.deprecated-standards.txt deleted file mode 100644 index 463e2b62c..000000000 --- a/tests/expected_json/deprecated_calls.deprecated-standards.txt +++ /dev/null @@ -1,20 +0,0 @@ - -Deprecated standard detected ContractWithDeprecatedReferences.globalBlockHash (tests/deprecated_calls.sol#2): - - Usage of "block.blockhash()" should be replaced with "blockhash()" -Deprecated standard detected msg.gas == msg.value (tests/deprecated_calls.sol#7): - - Usage of "msg.gas" should be replaced with "gasleft()" -Deprecated standard detected THROW (tests/deprecated_calls.sol#9): - - Usage of "throw" should be replaced with "revert()" -Deprecated standard detected sha3Result = sha3()(test deprecated sha3 usage) (tests/deprecated_calls.sol#16): - - Usage of "sha3()" should be replaced with "keccak256()" -Deprecated standard detected blockHashResult = block.blockhash(0) (tests/deprecated_calls.sol#19): - - Usage of "block.blockhash()" should be replaced with "blockhash()" -Deprecated standard detected address(this).callcode() (tests/deprecated_calls.sol#22): - - Usage of "callcode" should be replaced with "delegatecall" -Deprecated standard detected suicide(address)(address(0)) (tests/deprecated_calls.sol#25): - - Usage of "suicide()" should be replaced with "selfdestruct()" -Deprecated standard detected globalBlockHash = block.blockhash(0) (tests/deprecated_calls.sol#2): - - Usage of "block.blockhash()" should be replaced with "blockhash()" -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#deprecated-standards -tests/deprecated_calls.sol analyzed (1 contracts with 1 detectors), 8 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/erc20_indexed.erc20-indexed.txt b/tests/expected_json/erc20_indexed.erc20-indexed.txt deleted file mode 100644 index 7f9af2641..000000000 --- a/tests/expected_json/erc20_indexed.erc20-indexed.txt +++ /dev/null @@ -1,8 +0,0 @@ - -ERC20 event IERC20BadTransfer(address,address,uint256) (tests/erc20_indexed.sol#19)does not index parameter from -ERC20 event IERC20BadTransfer(address,address,uint256) (tests/erc20_indexed.sol#19)does not index parameter to -ERC20 event IERC20BadApproval(address,address,uint256) (tests/erc20_indexed.sol#20)does not index parameter owner -ERC20 event IERC20BadApproval(address,address,uint256) (tests/erc20_indexed.sol#20)does not index parameter spender -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#unindexed-erc20-event-parameters -tests/erc20_indexed.sol analyzed (3 contracts with 1 detectors), 4 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/external_function.external-function.txt b/tests/expected_json/external_function.external-function.txt deleted file mode 100644 index 3bb411274..000000000 --- a/tests/expected_json/external_function.external-function.txt +++ /dev/null @@ -1,14 +0,0 @@ - -funcNotCalled3() should be declared external: - - ContractWithFunctionNotCalled.funcNotCalled3() (tests/external_function.sol#13-15) -funcNotCalled2() should be declared external: - - ContractWithFunctionNotCalled.funcNotCalled2() (tests/external_function.sol#17-19) -funcNotCalled() should be declared external: - - ContractWithFunctionNotCalled.funcNotCalled() (tests/external_function.sol#21-23) -funcNotCalled() should be declared external: - - ContractWithFunctionNotCalled2.funcNotCalled() (tests/external_function.sol#32-39) -parameter_read_ok_for_external(uint256) should be declared external: - - FunctionParameterWrite.parameter_read_ok_for_external(uint256) (tests/external_function.sol#74-76) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#public-function-that-could-be-declared-external -tests/external_function.sol analyzed (6 contracts with 1 detectors), 5 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/external_function_2.external-function.txt b/tests/expected_json/external_function_2.external-function.txt deleted file mode 100644 index 00120a8e8..000000000 --- a/tests/expected_json/external_function_2.external-function.txt +++ /dev/null @@ -1,2 +0,0 @@ -tests/external_function_2.sol analyzed (4 contracts with 1 detectors), 0 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/incorrect_equality.incorrect-equality.txt b/tests/expected_json/incorrect_equality.incorrect-equality.txt deleted file mode 100644 index 58b289938..000000000 --- a/tests/expected_json/incorrect_equality.incorrect-equality.txt +++ /dev/null @@ -1,28 +0,0 @@ - -ERC20TestBalance.bad0(ERC20Function) (tests/incorrect_equality.sol#21-23) uses a dangerous strict equality: - - require(bool)(erc.balanceOf(address(this)) == 10) (tests/incorrect_equality.sol#22) -ERC20TestBalance.bad1(ERC20Variable) (tests/incorrect_equality.sol#25-27) uses a dangerous strict equality: - - require(bool)(erc.balanceOf(msg.sender) == 10) (tests/incorrect_equality.sol#26) -TestContractBalance.bad0() (tests/incorrect_equality.sol#32-35) uses a dangerous strict equality: - - require(bool)(address(address(this)).balance == 10000000000000000000) (tests/incorrect_equality.sol#33) -TestContractBalance.bad1() (tests/incorrect_equality.sol#37-40) uses a dangerous strict equality: - - require(bool)(10000000000000000000 == address(address(this)).balance) (tests/incorrect_equality.sol#38) -TestContractBalance.bad2() (tests/incorrect_equality.sol#42-45) uses a dangerous strict equality: - - require(bool)(address(this).balance == 10000000000000000000) (tests/incorrect_equality.sol#43) -TestContractBalance.bad3() (tests/incorrect_equality.sol#47-50) uses a dangerous strict equality: - - require(bool)(10000000000000000000 == address(this).balance) (tests/incorrect_equality.sol#48) -TestContractBalance.bad4() (tests/incorrect_equality.sol#52-57) uses a dangerous strict equality: - - balance == 10000000000000000000 (tests/incorrect_equality.sol#54) -TestContractBalance.bad5() (tests/incorrect_equality.sol#59-64) uses a dangerous strict equality: - - 10000000000000000000 == balance (tests/incorrect_equality.sol#61) -TestContractBalance.bad6() (tests/incorrect_equality.sol#66-71) uses a dangerous strict equality: - - balance == 10000000000000000000 (tests/incorrect_equality.sol#68) -TestSolidityKeyword.bad0() (tests/incorrect_equality.sol#123-125) uses a dangerous strict equality: - - require(bool)(now == 0) (tests/incorrect_equality.sol#124) -TestSolidityKeyword.bad1() (tests/incorrect_equality.sol#127-129) uses a dangerous strict equality: - - require(bool)(block.number == 0) (tests/incorrect_equality.sol#128) -TestSolidityKeyword.bad2() (tests/incorrect_equality.sol#131-133) uses a dangerous strict equality: - - require(bool)(block.number == 0) (tests/incorrect_equality.sol#132) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-strict-equalities -tests/incorrect_equality.sol analyzed (5 contracts with 1 detectors), 12 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/incorrect_erc20_interface.erc20-interface.txt b/tests/expected_json/incorrect_erc20_interface.erc20-interface.txt deleted file mode 100644 index e961c22e4..000000000 --- a/tests/expected_json/incorrect_erc20_interface.erc20-interface.txt +++ /dev/null @@ -1,10 +0,0 @@ - -Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface:Token.transfer(address,uint256) (tests/incorrect_erc20_interface.sol#4) -Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface:Token.approve(address,uint256) (tests/incorrect_erc20_interface.sol#5) -Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface:Token.transferFrom(address,address,uint256) (tests/incorrect_erc20_interface.sol#6) -Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface:Token.totalSupply() (tests/incorrect_erc20_interface.sol#7) -Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface:Token.balanceOf(address) (tests/incorrect_erc20_interface.sol#8) -Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface:Token.allowance(address,address) (tests/incorrect_erc20_interface.sol#9) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-erc20-interface -tests/incorrect_erc20_interface.sol analyzed (1 contracts with 1 detectors), 6 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/incorrect_erc721_interface.erc721-interface.txt b/tests/expected_json/incorrect_erc721_interface.erc721-interface.txt deleted file mode 100644 index 1c27d799c..000000000 --- a/tests/expected_json/incorrect_erc721_interface.erc721-interface.txt +++ /dev/null @@ -1,14 +0,0 @@ - -Token (tests/incorrect_erc721_interface.sol#6-16) has incorrect ERC721 function interface:IERC165.supportsInterface(bytes4) (tests/incorrect_erc721_interface.sol#4) -Token (tests/incorrect_erc721_interface.sol#6-16) has incorrect ERC721 function interface:Token.balanceOf(address) (tests/incorrect_erc721_interface.sol#7) -Token (tests/incorrect_erc721_interface.sol#6-16) has incorrect ERC721 function interface:Token.ownerOf(uint256) (tests/incorrect_erc721_interface.sol#8) -Token (tests/incorrect_erc721_interface.sol#6-16) has incorrect ERC721 function interface:Token.safeTransferFrom(address,address,uint256,bytes) (tests/incorrect_erc721_interface.sol#9) -Token (tests/incorrect_erc721_interface.sol#6-16) has incorrect ERC721 function interface:Token.safeTransferFrom(address,address,uint256) (tests/incorrect_erc721_interface.sol#10) -Token (tests/incorrect_erc721_interface.sol#6-16) has incorrect ERC721 function interface:Token.transferFrom(address,address,uint256) (tests/incorrect_erc721_interface.sol#11) -Token (tests/incorrect_erc721_interface.sol#6-16) has incorrect ERC721 function interface:Token.approve(address,uint256) (tests/incorrect_erc721_interface.sol#12) -Token (tests/incorrect_erc721_interface.sol#6-16) has incorrect ERC721 function interface:Token.setApprovalForAll(address,bool) (tests/incorrect_erc721_interface.sol#13) -Token (tests/incorrect_erc721_interface.sol#6-16) has incorrect ERC721 function interface:Token.getApproved(uint256) (tests/incorrect_erc721_interface.sol#14) -Token (tests/incorrect_erc721_interface.sol#6-16) has incorrect ERC721 function interface:Token.isApprovedForAll(address,address) (tests/incorrect_erc721_interface.sol#15) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-erc721-interface -tests/incorrect_erc721_interface.sol analyzed (2 contracts with 1 detectors), 10 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/inline_assembly_contract-0.5.1.assembly.txt b/tests/expected_json/inline_assembly_contract-0.5.1.assembly.txt deleted file mode 100644 index d850bd7a8..000000000 --- a/tests/expected_json/inline_assembly_contract-0.5.1.assembly.txt +++ /dev/null @@ -1,6 +0,0 @@ - -GetCode.at(address) (tests/inline_assembly_contract-0.5.1.sol#6-20) uses assembly - - INLINE ASM (tests/inline_assembly_contract-0.5.1.sol#7-20) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#assembly-usage -tests/inline_assembly_contract-0.5.1.sol analyzed (1 contracts with 1 detectors), 1 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/inline_assembly_contract.assembly.txt b/tests/expected_json/inline_assembly_contract.assembly.txt deleted file mode 100644 index 68e6653c7..000000000 --- a/tests/expected_json/inline_assembly_contract.assembly.txt +++ /dev/null @@ -1,6 +0,0 @@ - -GetCode.at(address) (tests/inline_assembly_contract.sol#6-20) uses assembly - - INLINE ASM (tests/inline_assembly_contract.sol#7-20) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#assembly-usage -tests/inline_assembly_contract.sol analyzed (1 contracts with 1 detectors), 1 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/inline_assembly_library-0.5.1.assembly.txt b/tests/expected_json/inline_assembly_library-0.5.1.assembly.txt deleted file mode 100644 index e71de7e9b..000000000 --- a/tests/expected_json/inline_assembly_library-0.5.1.assembly.txt +++ /dev/null @@ -1,8 +0,0 @@ - -VectorSum.sumAsm(uint256[]) (tests/inline_assembly_library-0.5.1.sol#16-22) uses assembly - - INLINE ASM (tests/inline_assembly_library-0.5.1.sol#18-21) -VectorSum.sumPureAsm(uint256[]) (tests/inline_assembly_library-0.5.1.sol#25-47) uses assembly - - INLINE ASM (tests/inline_assembly_library-0.5.1.sol#26-47) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#assembly-usage -tests/inline_assembly_library-0.5.1.sol analyzed (1 contracts with 1 detectors), 2 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/inline_assembly_library.assembly.txt b/tests/expected_json/inline_assembly_library.assembly.txt deleted file mode 100644 index 1c569791a..000000000 --- a/tests/expected_json/inline_assembly_library.assembly.txt +++ /dev/null @@ -1,8 +0,0 @@ - -VectorSum.sumAsm(uint256[]) (tests/inline_assembly_library.sol#16-22) uses assembly - - INLINE ASM (tests/inline_assembly_library.sol#18-21) -VectorSum.sumPureAsm(uint256[]) (tests/inline_assembly_library.sol#25-47) uses assembly - - INLINE ASM (tests/inline_assembly_library.sol#26-47) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#assembly-usage -tests/inline_assembly_library.sol analyzed (1 contracts with 1 detectors), 2 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/locked_ether-0.5.1.locked-ether.txt b/tests/expected_json/locked_ether-0.5.1.locked-ether.txt deleted file mode 100644 index 67fa5daa3..000000000 --- a/tests/expected_json/locked_ether-0.5.1.locked-ether.txt +++ /dev/null @@ -1,8 +0,0 @@ - -Contract locking ether found in : - Contract OnlyLocked (tests/locked_ether-0.5.1.sol#26) has payable functions: - - Locked.receive() (tests/locked_ether-0.5.1.sol#4-6) - But does not have a function to withdraw the ether -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#contracts-that-lock-ether -tests/locked_ether-0.5.1.sol analyzed (4 contracts with 1 detectors), 1 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/locked_ether.locked-ether.txt b/tests/expected_json/locked_ether.locked-ether.txt deleted file mode 100644 index 0384aaeb1..000000000 --- a/tests/expected_json/locked_ether.locked-ether.txt +++ /dev/null @@ -1,8 +0,0 @@ - -Contract locking ether found in : - Contract OnlyLocked (tests/locked_ether.sol#26) has payable functions: - - Locked.receive() (tests/locked_ether.sol#4-6) - But does not have a function to withdraw the ether -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#contracts-that-lock-ether -tests/locked_ether.sol analyzed (4 contracts with 1 detectors), 1 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/low_level_calls.low-level-calls.txt b/tests/expected_json/low_level_calls.low-level-calls.txt deleted file mode 100644 index 4fba69a74..000000000 --- a/tests/expected_json/low_level_calls.low-level-calls.txt +++ /dev/null @@ -1,6 +0,0 @@ - -Low level call in Sender.send(address) (tests/low_level_calls.sol#5-7): - - _receiver.call.value(msg.value).gas(7777)() (tests/low_level_calls.sol#6) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#low-level-calls -tests/low_level_calls.sol analyzed (2 contracts with 1 detectors), 1 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/multiple_calls_in_loop.calls-loop.txt b/tests/expected_json/multiple_calls_in_loop.calls-loop.txt deleted file mode 100644 index bd95d1b97..000000000 --- a/tests/expected_json/multiple_calls_in_loop.calls-loop.txt +++ /dev/null @@ -1,5 +0,0 @@ - -CallInLoop.bad() (tests/multiple_calls_in_loop.sol#9-13) has external calls inside a loop: destinations[i].transfer(i) (tests/multiple_calls_in_loop.sol#11) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation/#calls-inside-a-loop -tests/multiple_calls_in_loop.sol analyzed (1 contracts with 1 detectors), 1 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/naming_convention.naming-convention.txt b/tests/expected_json/naming_convention.naming-convention.txt deleted file mode 100644 index 6c1a8a2d2..000000000 --- a/tests/expected_json/naming_convention.naming-convention.txt +++ /dev/null @@ -1,16 +0,0 @@ - -Contract naming (tests/naming_convention.sol#3-48) is not in CapWords -Struct naming.test (tests/naming_convention.sol#14-16) is not in CapWords -Event namingevent_(uint256) (tests/naming_convention.sol#23) is not in CapWords -Function naming.GetOne() (tests/naming_convention.sol#30-33) is not in mixedCase -Parameter naming.setInt(uint256,uint256).Number2 (tests/naming_convention.sol#35) is not in mixedCase -Constant naming.MY_other_CONSTANT (tests/naming_convention.sol#9) is not in UPPER_CASE_WITH_UNDERSCORES -Variable naming.Var_One (tests/naming_convention.sol#11) is not in mixedCase -Enum naming.numbers (tests/naming_convention.sol#6) is not in CapWords -Modifier naming.CantDo() (tests/naming_convention.sol#41-43) is not in mixedCase -Parameter T.test(uint256,uint256)._used (tests/naming_convention.sol#59) is not in mixedCase -Variable T._myPublicVar (tests/naming_convention.sol#56) is not in mixedCase -Variable T.l (tests/naming_convention.sol#67) used l, O, I, which should not be used -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#conformity-to-solidity-naming-conventions -tests/naming_convention.sol analyzed (4 contracts with 1 detectors), 12 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/old_solc.sol.json.solc-version.txt b/tests/expected_json/old_solc.sol.json.solc-version.txt deleted file mode 100644 index 83a789c46..000000000 --- a/tests/expected_json/old_solc.sol.json.solc-version.txt +++ /dev/null @@ -1,5 +0,0 @@ - -Pragma version0.4.21 (None) allows old versions -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-versions-of-solidity -tests/old_solc.sol.json analyzed (1 contracts with 1 detectors), 1 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/pragma.0.4.24.pragma.txt b/tests/expected_json/pragma.0.4.24.pragma.txt deleted file mode 100644 index fc9f763c5..000000000 --- a/tests/expected_json/pragma.0.4.24.pragma.txt +++ /dev/null @@ -1,8 +0,0 @@ - -Different versions of Solidity is used in : - - Version used: ['^0.4.23', '^0.4.24'] - - ^0.4.23 (tests/pragma.0.4.23.sol#1) - - ^0.4.24 (tests/pragma.0.4.24.sol#1) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#different-pragma-directives-are-used -tests/pragma.0.4.24.sol analyzed (1 contracts with 1 detectors), 1 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/reentrancy-0.5.1-events.reentrancy-events.txt b/tests/expected_json/reentrancy-0.5.1-events.reentrancy-events.txt deleted file mode 100644 index c42c64ee0..000000000 --- a/tests/expected_json/reentrancy-0.5.1-events.reentrancy-events.txt +++ /dev/null @@ -1,9 +0,0 @@ - -Reentrancy in Test.bug(C) (tests/reentrancy-0.5.1-events.sol#14-17): - External calls: - - c.f() (tests/reentrancy-0.5.1-events.sol#15) - Event emitted after the call(s): - - E() (tests/reentrancy-0.5.1-events.sol#16) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-3 -tests/reentrancy-0.5.1-events.sol analyzed (2 contracts with 1 detectors), 1 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/reentrancy-0.5.1.reentrancy-eth.txt b/tests/expected_json/reentrancy-0.5.1.reentrancy-eth.txt deleted file mode 100644 index e6b0bf8cc..000000000 --- a/tests/expected_json/reentrancy-0.5.1.reentrancy-eth.txt +++ /dev/null @@ -1,14 +0,0 @@ - -Reentrancy in Reentrancy.withdrawBalance() (tests/reentrancy-0.5.1.sol#14-22): - External calls: - - (ret,mem) = msg.sender.call.value(userBalance[msg.sender])() (tests/reentrancy-0.5.1.sol#17) - State variables written after the call(s): - - userBalance[msg.sender] = 0 (tests/reentrancy-0.5.1.sol#21) -Reentrancy in Reentrancy.withdrawBalance_fixed_3() (tests/reentrancy-0.5.1.sol#44-53): - External calls: - - (ret,mem) = msg.sender.call.value(amount)() (tests/reentrancy-0.5.1.sol#49) - State variables written after the call(s): - - userBalance[msg.sender] = amount (tests/reentrancy-0.5.1.sol#51) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities -tests/reentrancy-0.5.1.sol analyzed (1 contracts with 1 detectors), 2 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/reentrancy.reentrancy-eth.txt b/tests/expected_json/reentrancy.reentrancy-eth.txt deleted file mode 100644 index 47902aeb6..000000000 --- a/tests/expected_json/reentrancy.reentrancy-eth.txt +++ /dev/null @@ -1,14 +0,0 @@ - -Reentrancy in Reentrancy.withdrawBalance() (tests/reentrancy.sol#14-21): - External calls: - - ! (msg.sender.call.value(userBalance[msg.sender])()) (tests/reentrancy.sol#17) - State variables written after the call(s): - - userBalance[msg.sender] = 0 (tests/reentrancy.sol#20) -Reentrancy in Reentrancy.withdrawBalance_nested() (tests/reentrancy.sol#64-70): - External calls: - - msg.sender.call.value(amount / 2)() (tests/reentrancy.sol#67) - State variables written after the call(s): - - userBalance[msg.sender] = 0 (tests/reentrancy.sol#68) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities -tests/reentrancy.sol analyzed (3 contracts with 1 detectors), 2 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/reentrancy.reentrancy-unlimited-gas.txt b/tests/expected_json/reentrancy.reentrancy-unlimited-gas.txt deleted file mode 100644 index 8bd078733..000000000 --- a/tests/expected_json/reentrancy.reentrancy-unlimited-gas.txt +++ /dev/null @@ -1,9 +0,0 @@ - -Reentrancy in Reentrancy.withdrawBalance_fixed_2() (tests/reentrancy.sol#33-40): - External calls: - - msg.sender.transfer(userBalance[msg.sender]) (tests/reentrancy.sol#38) - State variables written after the call(s): - - userBalance[msg.sender] = 0 (tests/reentrancy.sol#39) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-4 -tests/reentrancy.sol analyzed (3 contracts with 1 detectors), 1 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/right_to_left_override.rtlo.txt b/tests/expected_json/right_to_left_override.rtlo.txt deleted file mode 100644 index f62ebe14b..000000000 --- a/tests/expected_json/right_to_left_override.rtlo.txt +++ /dev/null @@ -1,6 +0,0 @@ - -tests/right_to_left_override.sol contains a unicode right-to-left-override character at byte offset 96: - - b' test1(/*A\xe2\x80\xae/*B*/2 , 1/*\xe2\x80\xad' -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#right-to-left-override-character -tests/right_to_left_override.sol analyzed (1 contracts with 1 detectors), 1 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/shadowing_abstract.shadowing-abstract.txt b/tests/expected_json/shadowing_abstract.shadowing-abstract.txt deleted file mode 100644 index 0de1d0dae..000000000 --- a/tests/expected_json/shadowing_abstract.shadowing-abstract.txt +++ /dev/null @@ -1,6 +0,0 @@ - -DerivedContract.owner (tests/shadowing_abstract.sol#7) shadows: - - BaseContract.owner (tests/shadowing_abstract.sol#2) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#state-variable-shadowing-from-abstract-contracts -tests/shadowing_abstract.sol analyzed (2 contracts with 1 detectors), 1 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/shadowing_builtin_symbols.shadowing-builtin.txt b/tests/expected_json/shadowing_builtin_symbols.shadowing-builtin.txt deleted file mode 100644 index e2effe4f1..000000000 --- a/tests/expected_json/shadowing_builtin_symbols.shadowing-builtin.txt +++ /dev/null @@ -1,17 +0,0 @@ - -BaseContract.blockhash (tests/shadowing_builtin_symbols.sol#4) (state variable) shadows built-in symbol" -BaseContract.now (tests/shadowing_builtin_symbols.sol#5) (state variable) shadows built-in symbol" -BaseContractrevert(bool) (tests/shadowing_builtin_symbols.sol#7) (event) shadows built-in symbol" -ExtendedContract.assert(bool) (tests/shadowing_builtin_symbols.sol#13-15) (function) shadows built-in symbol" -ExtendedContract.assert(bool).msg (tests/shadowing_builtin_symbols.sol#14) (local variable) shadows built-in symbol" -ExtendedContract.ecrecover (tests/shadowing_builtin_symbols.sol#11) (state variable) shadows built-in symbol" -FurtherExtendedContract.require() (tests/shadowing_builtin_symbols.sol#23-28) (modifier) shadows built-in symbol" -FurtherExtendedContract.require().keccak256 (tests/shadowing_builtin_symbols.sol#25) (local variable) shadows built-in symbol" -FurtherExtendedContract.require().sha3 (tests/shadowing_builtin_symbols.sol#26) (local variable) shadows built-in symbol" -FurtherExtendedContract.blockhash (tests/shadowing_builtin_symbols.sol#19) (state variable) shadows built-in symbol" -FurtherExtendedContract.this (tests/shadowing_builtin_symbols.sol#20) (state variable) shadows built-in symbol" -FurtherExtendedContract.abi (tests/shadowing_builtin_symbols.sol#21) (state variable) shadows built-in symbol" -Reserved.mutable (tests/shadowing_builtin_symbols.sol#32) (state variable) shadows built-in symbol" -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#builtin-symbol-shadowing -tests/shadowing_builtin_symbols.sol analyzed (4 contracts with 1 detectors), 13 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/shadowing_local_variable.shadowing-local.txt b/tests/expected_json/shadowing_local_variable.shadowing-local.txt deleted file mode 100644 index aa063e8b2..000000000 --- a/tests/expected_json/shadowing_local_variable.shadowing-local.txt +++ /dev/null @@ -1,16 +0,0 @@ - -FurtherExtendedContract.shadowingParent(uint256).x (tests/shadowing_local_variable.sol#25) shadows: - - FurtherExtendedContract.x (tests/shadowing_local_variable.sol#17) (state variable) - - ExtendedContract.x (tests/shadowing_local_variable.sol#9) (state variable) - - BaseContract.x (tests/shadowing_local_variable.sol#4) (state variable) -FurtherExtendedContract.shadowingParent(uint256).y (tests/shadowing_local_variable.sol#25) shadows: - - BaseContract.y (tests/shadowing_local_variable.sol#5) (state variable) -FurtherExtendedContract.shadowingParent(uint256).z (tests/shadowing_local_variable.sol#25) shadows: - - ExtendedContract.z() (tests/shadowing_local_variable.sol#11) (function) -FurtherExtendedContract.shadowingParent(uint256).w (tests/shadowing_local_variable.sol#25) shadows: - - FurtherExtendedContract.w() (tests/shadowing_local_variable.sol#20-23) (modifier) -FurtherExtendedContract.shadowingParent(uint256).v (tests/shadowing_local_variable.sol#25) shadows: - - ExtendedContractv() (tests/shadowing_local_variable.sol#13) (event) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#local-variable-shadowing -tests/shadowing_local_variable.sol analyzed (3 contracts with 1 detectors), 5 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/shadowing_state_variable.shadowing-state.txt b/tests/expected_json/shadowing_state_variable.shadowing-state.txt deleted file mode 100644 index 30645e488..000000000 --- a/tests/expected_json/shadowing_state_variable.shadowing-state.txt +++ /dev/null @@ -1,6 +0,0 @@ - -DerivedContract.owner (tests/shadowing_state_variable.sol#12) shadows: - - BaseContract.owner (tests/shadowing_state_variable.sol#2) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#state-variable-shadowing -tests/shadowing_state_variable.sol analyzed (2 contracts with 1 detectors), 1 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/solc_version_incorrect.solc-version.txt b/tests/expected_json/solc_version_incorrect.solc-version.txt deleted file mode 100644 index 3185583bf..000000000 --- a/tests/expected_json/solc_version_incorrect.solc-version.txt +++ /dev/null @@ -1,7 +0,0 @@ - -Pragma version^0.4.23 (tests/solc_version_incorrect.sol#2) allows old versions -Pragma version>=0.4.0<0.6.0 (tests/solc_version_incorrect.sol#3) allows old versions -solc-0.4.25 is not recommended for deployment -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-versions-of-solidity -tests/solc_version_incorrect.sol analyzed (1 contracts with 1 detectors), 3 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/solc_version_incorrect_05.ast.json.solc-version.txt b/tests/expected_json/solc_version_incorrect_05.ast.json.solc-version.txt deleted file mode 100644 index 82933723e..000000000 --- a/tests/expected_json/solc_version_incorrect_05.ast.json.solc-version.txt +++ /dev/null @@ -1,6 +0,0 @@ - -Pragma version^0.5.5 (None) is known to contain severe issues (https://solidity.readthedocs.io/en/latest/bugs.html) -Pragma version0.5.7 (None) allows old versions -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-versions-of-solidity -tests/solc_version_incorrect_05.ast.json analyzed (1 contracts with 1 detectors), 2 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/timestamp.timestamp.txt b/tests/expected_json/timestamp.timestamp.txt deleted file mode 100644 index 934b1a6cb..000000000 --- a/tests/expected_json/timestamp.timestamp.txt +++ /dev/null @@ -1,13 +0,0 @@ - -Timestamp.bad0() (tests/timestamp.sol#4-6) uses timestamp for comparisons - Dangerous comparisons: - - require(bool)(block.timestamp == 0) (tests/timestamp.sol#5) -Timestamp.bad1() (tests/timestamp.sol#8-11) uses timestamp for comparisons - Dangerous comparisons: - - require(bool)(time == 0) (tests/timestamp.sol#10) -Timestamp.bad2() (tests/timestamp.sol#13-15) uses timestamp for comparisons - Dangerous comparisons: - - block.timestamp > 0 (tests/timestamp.sol#14) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#block-timestamp -tests/timestamp.sol analyzed (1 contracts with 1 detectors), 3 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/too_many_digits.too-many-digits.txt b/tests/expected_json/too_many_digits.too-many-digits.txt deleted file mode 100644 index a32a80852..000000000 --- a/tests/expected_json/too_many_digits.too-many-digits.txt +++ /dev/null @@ -1,14 +0,0 @@ - -C.f() (tests/too_many_digits.sol#9-15) uses literals with too many digits: - - x1 = 0x000001 (tests/too_many_digits.sol#10) -C.f() (tests/too_many_digits.sol#9-15) uses literals with too many digits: - - x2 = 0x0000000000001 (tests/too_many_digits.sol#11) -C.f() (tests/too_many_digits.sol#9-15) uses literals with too many digits: - - x3 = 1000000000000000000 (tests/too_many_digits.sol#12) -C.f() (tests/too_many_digits.sol#9-15) uses literals with too many digits: - - x4 = 100000 (tests/too_many_digits.sol#13) -C.h() (tests/too_many_digits.sol#20-24) uses literals with too many digits: - - x2 = 100000 (tests/too_many_digits.sol#22) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#too-many-digits -tests/too_many_digits.sol analyzed (1 contracts with 1 detectors), 5 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/tx_origin-0.5.1.tx-origin.txt b/tests/expected_json/tx_origin-0.5.1.tx-origin.txt deleted file mode 100644 index 7a021f509..000000000 --- a/tests/expected_json/tx_origin-0.5.1.tx-origin.txt +++ /dev/null @@ -1,6 +0,0 @@ - -TxOrigin.bug0() (tests/tx_origin-0.5.1.sol#9-11) uses tx.origin for authorization: require(bool)(tx.origin == owner) (tests/tx_origin-0.5.1.sol#10) -TxOrigin.bug2() (tests/tx_origin-0.5.1.sol#13-17) uses tx.origin for authorization: tx.origin != owner (tests/tx_origin-0.5.1.sol#14) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-usage-of-txorigin -tests/tx_origin-0.5.1.sol analyzed (1 contracts with 1 detectors), 2 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/tx_origin.tx-origin.txt b/tests/expected_json/tx_origin.tx-origin.txt deleted file mode 100644 index a4bf131fe..000000000 --- a/tests/expected_json/tx_origin.tx-origin.txt +++ /dev/null @@ -1,6 +0,0 @@ - -TxOrigin.bug0() (tests/tx_origin.sol#9-11) uses tx.origin for authorization: require(bool)(tx.origin == owner) (tests/tx_origin.sol#10) -TxOrigin.bug2() (tests/tx_origin.sol#13-17) uses tx.origin for authorization: tx.origin != owner (tests/tx_origin.sol#14) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-usage-of-txorigin -tests/tx_origin.sol analyzed (1 contracts with 1 detectors), 2 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/unchecked_send-0.5.1.unchecked-send.txt b/tests/expected_json/unchecked_send-0.5.1.unchecked-send.txt deleted file mode 100644 index ec600ca4f..000000000 --- a/tests/expected_json/unchecked_send-0.5.1.unchecked-send.txt +++ /dev/null @@ -1,5 +0,0 @@ - -MyConc.bad(address) (tests/unchecked_send-0.5.1.sol#2-4) ignores return value by dst.send(msg.value) (tests/unchecked_send-0.5.1.sol#3) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#unchecked-send -tests/unchecked_send-0.5.1.sol analyzed (1 contracts with 1 detectors), 1 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/uninitialized-0.5.1.uninitialized-state.txt b/tests/expected_json/uninitialized-0.5.1.uninitialized-state.txt deleted file mode 100644 index 7404ab040..000000000 --- a/tests/expected_json/uninitialized-0.5.1.uninitialized-state.txt +++ /dev/null @@ -1,12 +0,0 @@ - -Uninitialized.destination (tests/uninitialized-0.5.1.sol#5) is never initialized. It is used in: - - Uninitialized.transfer() (tests/uninitialized-0.5.1.sol#7-9) -Test.balances (tests/uninitialized-0.5.1.sol#15) is never initialized. It is used in: - - Test.use() (tests/uninitialized-0.5.1.sol#23-26) -Test2.st (tests/uninitialized-0.5.1.sol#45) is never initialized. It is used in: - - Test2.use() (tests/uninitialized-0.5.1.sol#53-56) -Test2.v (tests/uninitialized-0.5.1.sol#47) is never initialized. It is used in: - - Test2.init() (tests/uninitialized-0.5.1.sol#49-51) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#uninitialized-state-variables -tests/uninitialized-0.5.1.sol analyzed (4 contracts with 1 detectors), 4 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/uninitialized.uninitialized-state.txt b/tests/expected_json/uninitialized.uninitialized-state.txt deleted file mode 100644 index 280c270c4..000000000 --- a/tests/expected_json/uninitialized.uninitialized-state.txt +++ /dev/null @@ -1,12 +0,0 @@ - -Uninitialized.destination (tests/uninitialized.sol#5) is never initialized. It is used in: - - Uninitialized.transfer() (tests/uninitialized.sol#7-9) -Test.balances (tests/uninitialized.sol#15) is never initialized. It is used in: - - Test.use() (tests/uninitialized.sol#23-26) -Test2.st (tests/uninitialized.sol#45) is never initialized. It is used in: - - Test2.use() (tests/uninitialized.sol#53-56) -Test2.v (tests/uninitialized.sol#47) is never initialized. It is used in: - - Test2.init() (tests/uninitialized.sol#49-51) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#uninitialized-state-variables -tests/uninitialized.sol analyzed (4 contracts with 1 detectors), 4 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/uninitialized_local_variable.uninitialized-local.txt b/tests/expected_json/uninitialized_local_variable.uninitialized-local.txt deleted file mode 100644 index 0d743f7f4..000000000 --- a/tests/expected_json/uninitialized_local_variable.uninitialized-local.txt +++ /dev/null @@ -1,5 +0,0 @@ - -Uninitialized.func().uint_not_init (tests/uninitialized_local_variable.sol#4) is a local variable never initialized -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#uninitialized-local-variables -tests/uninitialized_local_variable.sol analyzed (1 contracts with 1 detectors), 1 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/uninitialized_storage_pointer.uninitialized-storage.txt b/tests/expected_json/uninitialized_storage_pointer.uninitialized-storage.txt deleted file mode 100644 index 00b7beea5..000000000 --- a/tests/expected_json/uninitialized_storage_pointer.uninitialized-storage.txt +++ /dev/null @@ -1,5 +0,0 @@ - -Uninitialized.func().st_bug (tests/uninitialized_storage_pointer.sol#10) is a storage variable never initialized -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#uninitialized-storage-variables -tests/uninitialized_storage_pointer.sol analyzed (1 contracts with 1 detectors), 1 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/unused_return.unused-return.txt b/tests/expected_json/unused_return.unused-return.txt deleted file mode 100644 index 49e78e58b..000000000 --- a/tests/expected_json/unused_return.unused-return.txt +++ /dev/null @@ -1,6 +0,0 @@ - -User.test(Target) (tests/unused_return.sol#17-29) ignores return value by t.f() (tests/unused_return.sol#18) -User.test(Target) (tests/unused_return.sol#17-29) ignores return value by a.add(0) (tests/unused_return.sol#22) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#unused-return -tests/unused_return.sol analyzed (3 contracts with 1 detectors), 2 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/unused_state.unused-state.txt b/tests/expected_json/unused_state.unused-state.txt deleted file mode 100644 index 1772ef561..000000000 --- a/tests/expected_json/unused_state.unused-state.txt +++ /dev/null @@ -1,8 +0,0 @@ - -A.unused (tests/unused_state.sol#4) is never used in B (tests/unused_state.sol#11-16) -A.unused2 (tests/unused_state.sol#5) is never used in B (tests/unused_state.sol#11-16) -A.unused3 (tests/unused_state.sol#6) is never used in B (tests/unused_state.sol#11-16) -A.unused4 (tests/unused_state.sol#7) is never used in B (tests/unused_state.sol#11-16) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#unused-state-variables -tests/unused_state.sol analyzed (2 contracts with 1 detectors), 4 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration diff --git a/tests/expected_json/void-cst.void-cst.txt b/tests/expected_json/void-cst.void-cst.txt deleted file mode 100644 index 68c773d95..000000000 --- a/tests/expected_json/void-cst.void-cst.txt +++ /dev/null @@ -1,6 +0,0 @@ - -Void constructor called in D.constructor() (tests/void-cst.sol#10-12): - - C() (tests/void-cst.sol#10) -Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#void-constructor -tests/void-cst.sol analyzed (2 contracts with 1 detectors), 1 result(s) found -Use https://crytic.io/ to get access to additional detectors and Github integration From 73764edc4f4ea835a434f3d2dd86b9fecea60f75 Mon Sep 17 00:00:00 2001 From: Josselin Date: Fri, 9 Apr 2021 17:18:55 +0200 Subject: [PATCH 2/8] Use crytic-compile@dev-compilation-units --- setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 0f541adc5..01d28b812 100644 --- a/setup.py +++ b/setup.py @@ -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@dev-compilation-units#egg=crytic-compile"], license="AGPL-3.0", long_description=open("README.md").read(), entry_points={ From 71e33c608e628b0884f81704d82121bc4519636d Mon Sep 17 00:00:00 2001 From: Josselin Date: Fri, 9 Apr 2021 18:16:37 +0200 Subject: [PATCH 3/8] Minor fixes --- setup.py | 6 ++++-- slither/tools/flattening/flattening.py | 9 +++++---- slither/tools/kspec_coverage/kspec_coverage.py | 6 +++++- tests/test_function.py | 6 ++++-- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/setup.py b/setup.py index 01d28b812..a2ab2b2e8 100644 --- a/setup.py +++ b/setup.py @@ -11,10 +11,12 @@ setup( install_requires=[ "prettytable>=0.7.2", "pysha3>=1.0.2", - #"crytic-compile>=0.1.13", + # "crytic-compile>=0.1.13", "crytic-compile", ], - dependency_links=["git+https://github.com/crytic/crytic-compile.git@dev-compilation-units#egg=crytic-compile"], + dependency_links=[ + "git+https://github.com/crytic/crytic-compile.git@dev-compilation-units#egg=crytic-compile" + ], license="AGPL-3.0", long_description=open("README.md").read(), entry_points={ diff --git a/slither/tools/flattening/flattening.py b/slither/tools/flattening/flattening.py index 13132938a..e900e4581 100644 --- a/slither/tools/flattening/flattening.py +++ b/slither/tools/flattening/flattening.py @@ -71,10 +71,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 diff --git a/slither/tools/kspec_coverage/kspec_coverage.py b/slither/tools/kspec_coverage/kspec_coverage.py index 86b59be53..569a35cf1 100755 --- a/slither/tools/kspec_coverage/kspec_coverage.py +++ b/slither/tools/kspec_coverage/kspec_coverage.py @@ -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) diff --git a/tests/test_function.py b/tests/test_function.py index 273a9eab0..2a943e0ae 100644 --- a/tests/test_function.py +++ b/tests/test_function.py @@ -13,7 +13,8 @@ from slither.core.solidity_types.elementary_type import ElementaryType def test_functions(): # pylint: disable=too-many-statements slither = Slither("tests/test_function.sol") - functions = slither.contracts_as_dict["TestFunction"].available_functions_as_dict() + compilation_unit = slither.compilation_units[0] + functions = compilation_unit.contracts_as_dict["TestFunction"].available_functions_as_dict() f = functions["external_payable(uint256)"] assert f.name == "external_payable" @@ -243,7 +244,8 @@ def test_functions(): def test_function_can_send_eth(): slither = Slither("tests/test_function.sol") - functions = slither.contracts_as_dict["TestFunctionCanSendEth"].available_functions_as_dict() + compilation_unit = slither.compilation_units[0] + functions = compilation_unit.contracts_as_dict["TestFunctionCanSendEth"].available_functions_as_dict() assert functions["send_direct()"].can_send_eth() is True assert functions["transfer_direct()"].can_send_eth() is True From 84f76055a189fe72915390fcf6362c9d1ece1127 Mon Sep 17 00:00:00 2001 From: Josselin Date: Fri, 9 Apr 2021 19:59:29 +0200 Subject: [PATCH 4/8] Additional fixes --- slither/core/compilation_unit.py | 6 +++++- slither/printers/guidance/echidna.py | 20 ++++--------------- slither/printers/summary/evm.py | 8 ++++++-- slither/slithir/convert.py | 16 ++------------- slither/tools/flattening/flattening.py | 10 ++++++---- .../upgradeability/checks/abstract_checks.py | 2 +- 6 files changed, 24 insertions(+), 38 deletions(-) diff --git a/slither/core/compilation_unit.py b/slither/core/compilation_unit.py index c56ab7d30..9bf3d50dc 100644 --- a/slither/core/compilation_unit.py +++ b/slither/core/compilation_unit.py @@ -2,7 +2,7 @@ import math from collections import defaultdict from typing import Optional, Dict, List, Set, Union, TYPE_CHECKING, Tuple -from crytic_compile import CompilationUnit +from crytic_compile import CompilationUnit, CryticCompile from crytic_compile.compiler.compiler import CompilerVersion from slither.core.context.context import Context @@ -81,6 +81,10 @@ class SlitherCompilationUnit(Context): 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 ################################################################################### ################################################################################### diff --git a/slither/printers/guidance/echidna.py b/slither/printers/guidance/echidna.py index f60305604..82d7789df 100644 --- a/slither/printers/guidance/echidna.py +++ b/slither/printers/guidance/echidna.py @@ -79,14 +79,8 @@ def _is_constant(f: Function) -> bool: # pylint: disable=too-many-branches :return: """ if f.view or f.pure: - if ( - f.contract.compilation_unit.crytic_compile - and f.contract.compilation_unit.crytic_compile.compiler_version - ): - if not f.contract.compilation_unit.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: @@ -108,14 +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.compilation_unit.crytic_compile - and f.contract.compilation_unit.crytic_compile.compiler_version - ): - if f.contract.compilation_unit.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): diff --git a/slither/printers/summary/evm.py b/slither/printers/summary/evm.py index c3c7c9a86..6b600257a 100644 --- a/slither/printers/summary/evm.py +++ b/slither/printers/summary/evm.py @@ -38,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 diff --git a/slither/slithir/convert.py b/slither/slithir/convert.py index 072e791d4..7fa748446 100644 --- a/slither/slithir/convert.py +++ b/slither/slithir/convert.py @@ -387,26 +387,14 @@ def _convert_type_contract(ir, compilation_unit: "SlitherCompilationUnit"): contract = ir.variable_left.type.type if ir.variable_right == "creationCode": - if compilation_unit.crytic_compile: - bytecode = compilation_unit.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 compilation_unit.crytic_compile: - bytecode = compilation_unit.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) diff --git a/slither/tools/flattening/flattening.py b/slither/tools/flattening/flattening.py index e900e4581..bcdc492fe 100644 --- a/slither/tools/flattening/flattening.py +++ b/slither/tools/flattening/flattening.py @@ -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 @@ -188,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 {self._slither.crytic_compile.compilation_units[0].compiler_version.version};\n" if self._use_abi_encoder_v2: ret += "pragma experimental ABIEncoderV2;\n" diff --git a/slither/tools/upgradeability/checks/abstract_checks.py b/slither/tools/upgradeability/checks/abstract_checks.py index a6aca61bf..0ef3eaa9d 100644 --- a/slither/tools/upgradeability/checks/abstract_checks.py +++ b/slither/tools/upgradeability/checks/abstract_checks.py @@ -139,7 +139,7 @@ class AbstractCheck(metaclass=abc.ABCMeta): def generate_result(self, info, additional_fields=None): output = Output( - info, additional_fields, markdown_root=self.contract.compilation_unit.markdown_root + info, additional_fields, markdown_root=self.contract.compilation_unit.core.markdown_root ) output.data["check"] = self.ARGUMENT From 6233f313dc895b1260ff00c41f787a49c2959f01 Mon Sep 17 00:00:00 2001 From: Josselin Date: Mon, 19 Apr 2021 14:53:26 +0200 Subject: [PATCH 5/8] Minor fixes --- slither/tools/flattening/flattening.py | 2 +- tests/test_function.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/slither/tools/flattening/flattening.py b/slither/tools/flattening/flattening.py index bcdc492fe..f2c721fcd 100644 --- a/slither/tools/flattening/flattening.py +++ b/slither/tools/flattening/flattening.py @@ -191,7 +191,7 @@ class Flattening: ret += f"pragma solidity {self._pragma_solidity};\n" else: # TODO support multiple compiler version - ret += f"pragma solidity {self._slither.crytic_compile.compilation_units[0].compiler_version.version};\n" + 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" diff --git a/tests/test_function.py b/tests/test_function.py index 2a943e0ae..c13276f37 100644 --- a/tests/test_function.py +++ b/tests/test_function.py @@ -245,7 +245,9 @@ def test_functions(): def test_function_can_send_eth(): slither = Slither("tests/test_function.sol") compilation_unit = slither.compilation_units[0] - functions = compilation_unit.contracts_as_dict["TestFunctionCanSendEth"].available_functions_as_dict() + functions = compilation_unit.contracts_as_dict[ + "TestFunctionCanSendEth" + ].available_functions_as_dict() assert functions["send_direct()"].can_send_eth() is True assert functions["transfer_direct()"].can_send_eth() is True From b0c97a1571f1ca9a590fe4e43db38a3064f7cdc3 Mon Sep 17 00:00:00 2001 From: Feist Josselin Date: Tue, 27 Apr 2021 17:29:24 +0200 Subject: [PATCH 6/8] Use crytic-compile@master --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a2ab2b2e8..ec2f37c16 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ setup( "crytic-compile", ], dependency_links=[ - "git+https://github.com/crytic/crytic-compile.git@dev-compilation-units#egg=crytic-compile" + "git+https://github.com/crytic/crytic-compile.git@master#egg=crytic-compile" ], license="AGPL-3.0", long_description=open("README.md").read(), From ceb7478eed7f950e76b0b768690cc9cc5df74710 Mon Sep 17 00:00:00 2001 From: Feist Josselin Date: Tue, 27 Apr 2021 17:34:31 +0200 Subject: [PATCH 7/8] Disable mypy gh action --- .github/workflows/linter.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 452637a31..d6f783c4f 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -56,4 +56,5 @@ jobs: VALIDATE_DOCKERFILE_HADOLINT: false VALIDATE_EDITORCONFIG: false VALIDATE_JSCPD: false + VALIDATE_PYTHON_MYPY: false SHELLCHECK_OPTS: "-e SC1090" From 3f3855a28ebe1680c6db82580dbb6ec7b6ae672a Mon Sep 17 00:00:00 2001 From: Josselin Date: Wed, 28 Apr 2021 11:39:51 +0200 Subject: [PATCH 8/8] run black on setup.py --- setup.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/setup.py b/setup.py index ec2f37c16..eca51187f 100644 --- a/setup.py +++ b/setup.py @@ -14,9 +14,7 @@ setup( # "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={