Пример #1
0
def _convert_to_id(d):
    """
    Id keeps the source mapping of the node, otherwise we risk to consider two different node as the same
    :param d:
    :return:
    """
    if isinstance(d, str):
        return d

    if not isinstance(d, SourceMapping):
        raise SlitherError(
            f"{d} does not inherit from SourceMapping, conversion impossible")

    if isinstance(d, Node):
        if d.expression:
            return f"{d.expression} ({d.source_mapping_str})"
        return f"{str(d)} ({d.source_mapping_str})"

    if isinstance(d, Pragma):
        return f"{d} ({d.source_mapping_str})"

    if hasattr(d, "canonical_name"):
        return f"{d.canonical_name}"

    if hasattr(d, "name"):
        return f"{d.name}"

    raise SlitherError(
        f"{type(d)} cannot be converted (no name, or canonical_name")
Пример #2
0
def _convert_to_id(d):
    '''
    Id keeps the source mapping of the node, otherwise we risk to consider two different node as the same
    :param d:
    :return:
    '''
    if isinstance(d, str):
        return d

    if not isinstance(d, SourceMapping):
        raise SlitherError(
            f'{d} does not inherit from SourceMapping, conversion impossible')

    if isinstance(d, Node):
        if d.expression:
            return f'{d.expression} ({d.source_mapping_str})'
        else:
            return f'{str(d)} ({d.source_mapping_str})'

    if hasattr(d, 'canonical_name'):
        return f'{d.canonical_name}'

    if hasattr(d, 'name'):
        return f'{d.name}'

    raise SlitherError(
        f'{type(d)} cannot be converted (no name, or canonical_name')
Пример #3
0
 def add(self,
         add: SupportedOutput,
         additional_fields: Optional[Dict] = None):
     if not self._data["first_markdown_element"]:
         self._data[
             "first_markdown_element"] = add.source_mapping_to_markdown(
                 self._markdown_root)
     if isinstance(add, Variable):
         self.add_variable(add, additional_fields=additional_fields)
     elif isinstance(add, Contract):
         self.add_contract(add, additional_fields=additional_fields)
     elif isinstance(add, Function):
         self.add_function(add, additional_fields=additional_fields)
     elif isinstance(add, Enum):
         self.add_enum(add, additional_fields=additional_fields)
     elif isinstance(add, Event):
         self.add_event(add, additional_fields=additional_fields)
     elif isinstance(add, Structure):
         self.add_struct(add, additional_fields=additional_fields)
     elif isinstance(add, Pragma):
         self.add_pragma(add, additional_fields=additional_fields)
     elif isinstance(add, Node):
         self.add_node(add, additional_fields=additional_fields)
     else:
         raise SlitherError(f"Impossible to add {type(add)} to the json")
Пример #4
0
def _get_evm_instructions_function(function_info):
    function = function_info["function"]

    # CFG depends on function being constructor or not
    if function.is_constructor:
        cfg = function_info["contract_info"]["cfg_init"]
        # _dispatcher is the only function recognised by evm-cfg-builder in bytecode_init.
        # _dispatcher serves the role of the constructor in init code,
        #    given that there are no other functions.
        # Todo: Could rename it appropriately in evm-cfg-builder
        #    by detecting that init bytecode is being parsed.
        name = "_dispatcher"
        func_hash = ""
    else:
        cfg = function_info["contract_info"]["cfg"]
        name = function.name
        # Get first four bytes of function singature's keccak-256 hash used as function selector
        func_hash = str(hex(get_function_id(function.full_name)))

    function_evm = _get_function_evm(cfg, name, func_hash)
    if function_evm is None:
        to_log = "Function " + function.name + " not found in the EVM code"
        logger.error(to_log)
        raise SlitherError("Function " + function.name +
                           " not found in the EVM code")

    function_ins = []
    for basic_block in sorted(function_evm.basic_blocks,
                              key=lambda x: x.start.pc):
        for ins in basic_block.instructions:
            function_ins.append(ins)

    return function_ins
Пример #5
0
    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)
Пример #6
0
    def _visit_expression(self, expression):
        self._pre_visit(expression)

        if isinstance(expression, AssignmentOperation):
            self._visit_assignement_operation(expression)

        elif isinstance(expression, BinaryOperation):
            self._visit_binary_operation(expression)

        elif isinstance(expression, CallExpression):
            self._visit_call_expression(expression)

        elif isinstance(expression, ConditionalExpression):
            self._visit_conditional_expression(expression)

        elif isinstance(expression, ElementaryTypeNameExpression):
            self._visit_elementary_type_name_expression(expression)

        elif isinstance(expression, Identifier):
            self._visit_identifier(expression)

        elif isinstance(expression, IndexAccess):
            self._visit_index_access(expression)

        elif isinstance(expression, Literal):
            self._visit_literal(expression)

        elif isinstance(expression, MemberAccess):
            self._visit_member_access(expression)

        elif isinstance(expression, NewArray):
            self._visit_new_array(expression)

        elif isinstance(expression, NewContract):
            self._visit_new_contract(expression)

        elif isinstance(expression, NewElementaryType):
            self._visit_new_elementary_type(expression)

        elif isinstance(expression, TupleExpression):
            self._visit_tuple_expression(expression)

        elif isinstance(expression, TypeConversion):
            self._visit_type_conversion(expression)

        elif isinstance(expression, UnaryOperation):
            self._visit_unary_operation(expression)

        elif expression is None:
            pass

        else:
            raise SlitherError('Expression not handled: {}'.format(expression))

        self._post_visit(expression)
Пример #7
0
def _find_variable_init(
    caller_context: CallerContextExpression,
) -> Tuple[List[Contract], List["Function"], FileScope,]:
    from slither.solc_parsing.declarations.contract import ContractSolc
    from slither.solc_parsing.declarations.function import FunctionSolc
    from slither.solc_parsing.declarations.structure_top_level import StructureTopLevelSolc
    from slither.solc_parsing.variables.top_level_variable import TopLevelVariableSolc

    direct_contracts: List[Contract]
    direct_functions_parser: List[Function]
    scope: FileScope

    if isinstance(caller_context, FileScope):
        direct_contracts = []
        direct_functions_parser = []
        scope = caller_context
    elif isinstance(caller_context, ContractSolc):
        direct_contracts = [caller_context.underlying_contract]
        direct_functions_parser = [
            f.underlying_function
            for f in caller_context.functions_parser + caller_context.modifiers_parser
        ]
        scope = caller_context.underlying_contract.file_scope
    elif isinstance(caller_context, FunctionSolc):
        if caller_context.contract_parser:
            direct_contracts = [caller_context.contract_parser.underlying_contract]
            direct_functions_parser = [
                f.underlying_function
                for f in caller_context.contract_parser.functions_parser
                + caller_context.contract_parser.modifiers_parser
            ]
        else:
            # Top level functions
            direct_contracts = []
            direct_functions_parser = []
        underlying_function = caller_context.underlying_function
        if isinstance(underlying_function, FunctionTopLevel):
            scope = underlying_function.file_scope
        else:
            assert isinstance(underlying_function, FunctionContract)
            scope = underlying_function.contract.file_scope
    elif isinstance(caller_context, StructureTopLevelSolc):
        direct_contracts = []
        direct_functions_parser = []
        scope = caller_context.underlying_structure.file_scope
    elif isinstance(caller_context, TopLevelVariableSolc):
        direct_contracts = []
        direct_functions_parser = []
        scope = caller_context.underlying_variable.file_scope
    else:
        raise SlitherError(
            f"{type(caller_context)} ({caller_context} is not valid for find_variable"
        )

    return direct_contracts, direct_functions_parser, scope
Пример #8
0
def _convert_to_markdown(d, markdown_root):
    if isinstance(d, str):
        return d

    if not isinstance(d, SourceMapping):
        raise SlitherError(f'{d} does not inherit from SourceMapping, conversion impossible')

    if isinstance(d, Node):
        if d.expression:
            return f'[{d.expression}]({d.source_mapping_to_markdown(markdown_root)})'
        else:
            return f'[{str(d)}]({d.source_mapping_to_markdown(markdown_root)})'

    if hasattr(d, 'canonical_name'):
        return f'[{d.canonical_name}]({d.source_mapping_to_markdown(markdown_root)})'

    if hasattr(d, 'name'):
        return f'[{d.name}]({d.source_mapping_to_markdown(markdown_root)})'

    raise SlitherError(f'{type(d)} cannot be converted (no name, or canonical_name')
Пример #9
0
def _convert_to_description(d):
    if isinstance(d, str):
        return d

    if not isinstance(d, SourceMapping):
        raise SlitherError(
            f"{d} does not inherit from SourceMapping, conversion impossible")

    if isinstance(d, Node):
        if d.expression:
            return f"{d.expression} ({d.source_mapping_str})"
        return f"{str(d)} ({d.source_mapping_str})"

    if hasattr(d, "canonical_name"):
        return f"{d.canonical_name} ({d.source_mapping_str})"

    if hasattr(d, "name"):
        return f"{d.name} ({d.source_mapping_str})"

    raise SlitherError(
        f"{type(d)} cannot be converted (no name, or canonical_name")
Пример #10
0
def load_evm_cfg_builder():
    try:
        # Avoiding the addition of evm_cfg_builder as permanent dependency
        from evm_cfg_builder.cfg import CFG
        return CFG
    except ImportError:
        logger.error(
            "To use evm features, you need to install evm-cfg-builder")
        logger.error(
            "Documentation: https://github.com/crytic/evm_cfg_builder")
        logger.error("Installation: pip install evm-cfg-builder")
        raise SlitherError("evm-cfg-builder not installed.")
Пример #11
0
def _get_function_or_variable(contract, signature):
    f = contract.get_function_from_signature(signature)

    if f:
        return f

    for variable in contract.state_variables:
        # Todo: can lead to incorrect variable in case of shadowing
        if variable.visibility in ["public"]:
            if variable.name + "()" == signature:
                return variable

    raise SlitherError(f"Function id checks: {signature} not found in {contract.name}")
Пример #12
0
    def _pre_visit(self, expression):  # pylint: disable=too-many-branches
        if isinstance(expression, AssignmentOperation):
            self._pre_assignement_operation(expression)

        elif isinstance(expression, BinaryOperation):
            self._pre_binary_operation(expression)

        elif isinstance(expression, CallExpression):
            self._pre_call_expression(expression)

        elif isinstance(expression, ConditionalExpression):
            self._pre_conditional_expression(expression)

        elif isinstance(expression, ElementaryTypeNameExpression):
            self._pre_elementary_type_name_expression(expression)

        elif isinstance(expression, Identifier):
            self._pre_identifier(expression)

        elif isinstance(expression, IndexAccess):
            self._pre_index_access(expression)

        elif isinstance(expression, Literal):
            self._pre_literal(expression)

        elif isinstance(expression, MemberAccess):
            self._pre_member_access(expression)

        elif isinstance(expression, NewArray):
            self._pre_new_array(expression)

        elif isinstance(expression, NewContract):
            self._pre_new_contract(expression)

        elif isinstance(expression, NewElementaryType):
            self._pre_new_elementary_type(expression)

        elif isinstance(expression, TupleExpression):
            self._pre_tuple_expression(expression)

        elif isinstance(expression, TypeConversion):
            self._pre_type_conversion(expression)

        elif isinstance(expression, UnaryOperation):
            self._pre_unary_operation(expression)

        elif expression is None:
            pass

        else:
            raise SlitherError("Expression not handled: {}".format(expression))
Пример #13
0
 def add(self, add, additional_fields=None):
     if isinstance(add, Variable):
         self.add_variable(add, additional_fields=additional_fields)
     elif isinstance(add, Contract):
         self.add_contract(add, additional_fields=additional_fields)
     elif isinstance(add, Function):
         self.add_function(add, additional_fields=additional_fields)
     elif isinstance(add, Enum):
         self.add_enum(add, additional_fields=additional_fields)
     elif isinstance(add, Event):
         self.add_event(add, additional_fields=additional_fields)
     elif isinstance(add, Structure):
         self.add_struct(add, additional_fields=additional_fields)
     elif isinstance(add, Pragma):
         self.add_pragma(add, additional_fields=additional_fields)
     elif isinstance(add, Node):
         self.add_node(add, additional_fields=additional_fields)
     else:
         raise SlitherError(f'Impossible to add {type(add)} to the json')
Пример #14
0
def convert_string_to_int(val: str) -> int:
    if val.startswith("0x") or val.startswith("0X"):
        return int(val, 16)

    if "e" in val or "E" in val:
        base, expo = val.split("e") if "e" in val else val.split("E")
        base, expo = Decimal(base), int(expo)
        # The resulting number must be < 2**256-1, otherwise solc
        # Would not be able to compile it
        # 10**77 is the largest exponent that fits
        # See https://github.com/ethereum/solidity/blob/9e61f92bd4d19b430cb8cb26f1c7cf79f1dff380/libsolidity/ast/Types.cpp#L1281-L1290
        if expo > 77:
            if base != Decimal(0):
                raise SlitherError(
                    f"{base}e{expo} is too large to fit in any Solidity integer size"
                )
            return 0
        return int(Decimal(base) * Decimal(10 ** expo))

    return int(Decimal(val))
Пример #15
0
def _find_variable_init(
    caller_context: CallerContext,
) -> 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, SlitherCompilationUnitSolc):
        direct_contracts = []
        direct_functions_parser = []
        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_parser.compilation_unit
        sl_parser = caller_context.slither_parser
    elif isinstance(caller_context, FunctionSolc):
        if caller_context.contract_parser:
            direct_contracts = [
                caller_context.contract_parser.underlying_contract
            ]
            direct_functions_parser = (
                caller_context.contract_parser.functions_parser +
                caller_context.contract_parser.modifiers_parser)
        else:
            # Top level functions
            direct_contracts = []
            direct_functions_parser = []
        sl = caller_context.underlying_function.compilation_unit
        sl_parser = caller_context.slither_parser
    else:
        raise SlitherError(
            f"{type(caller_context)} ({caller_context} is not valid for find_variable"
        )

    return direct_contracts, direct_functions_parser, sl, sl_parser
Пример #16
0
def get_evm_instructions(obj):
    assert isinstance(obj, (Function, Contract, Node))

    if KEY_EVM_INS not in obj.context:

        CFG = load_evm_cfg_builder()

        slither = obj.slither

        if not slither.crytic_compile:
            raise SlitherError(
                "EVM features require to compile with crytic-compile")

        contract_info = {}
        function_info = {}
        node_info = {}

        if isinstance(obj, Node):
            contract_info["contract"] = obj.function.contract
        elif isinstance(obj, Function):
            contract_info["contract"] = obj.contract
        else:
            contract_info["contract"] = obj

        # Get contract runtime bytecode, srcmap and cfg
        contract_info[
            "bytecode_runtime"] = slither.crytic_compile.bytecode_runtime(
                contract_info["contract"].name)
        contract_info[
            "srcmap_runtime"] = slither.crytic_compile.srcmap_runtime(
                contract_info["contract"].name)
        contract_info["cfg"] = CFG(contract_info["bytecode_runtime"])

        # Get contract init bytecode, srcmap and cfg
        contract_info["bytecode_init"] = slither.crytic_compile.bytecode_init(
            contract_info["contract"].name)
        contract_info["srcmap_init"] = slither.crytic_compile.srcmap_init(
            contract_info["contract"].name)
        contract_info["cfg_init"] = CFG(contract_info["bytecode_init"])

        # Get evm instructions
        if isinstance(obj, Contract):
            # Get evm instructions for contract
            obj.context[KEY_EVM_INS] = _get_evm_instructions_contract(
                contract_info)

        elif isinstance(obj, Function):
            # Get evm instructions for function
            function_info["function"] = obj
            function_info["contract_info"] = contract_info
            obj.context[KEY_EVM_INS] = _get_evm_instructions_function(
                function_info)

        else:
            # Get evm instructions for node
            node_info["node"] = obj

            # CFG and srcmap depend on function being constructor or not
            if node_info["node"].function.is_constructor:
                cfg = contract_info["cfg_init"]
                srcmap = contract_info["srcmap_init"]
            else:
                cfg = contract_info["cfg"]
                srcmap = contract_info["srcmap_runtime"]

            node_info["cfg"] = cfg
            node_info["srcmap"] = srcmap
            node_info["contract"] = contract_info["contract"]
            node_info["slither"] = slither

            obj.context[KEY_EVM_INS] = _get_evm_instructions_node(node_info)

    return obj.context.get(KEY_EVM_INS, [])
Пример #17
0
    def __init__(self, target: Union[str, CryticCompile], **kwargs):
        """
        Args:
            target (str | CryticCompile)
        Keyword Args:
            solc (str): solc binary location (default 'solc')
            disable_solc_warnings (bool): True to disable solc warnings (default false)
            solc_arguments (str): solc arguments (default '')
            ast_format (str): ast format (default '--ast-compact-json')
            filter_paths (list(str)): list of path to filter (default [])
            triage_mode (bool): if true, switch to triage mode (default false)
            exclude_dependencies (bool): if true, exclude results that are only related to dependencies
            generate_patches (bool): if true, patches are generated (json output only)

            truffle_ignore (bool): ignore truffle.js presence (default false)
            truffle_build_directory (str): build truffle directory (default 'build/contracts')
            truffle_ignore_compile (bool): do not run truffle compile (default False)
            truffle_version (str): use a specific truffle version (default None)

            embark_ignore (bool): ignore embark.js presence (default false)
            embark_ignore_compile (bool): do not run embark build (default False)
            embark_overwrite_config (bool): overwrite original config file (default false)

        """
        super().__init__()

        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)

        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)

            _update_file_scopes(compilation_unit_slither.scopes.values())

        if kwargs.get("generate_patches", False):
            self.generate_patches = True

        self._markdown_root = kwargs.get("markdown_root", "")

        self._detectors = []
        self._printers = []

        filter_paths = kwargs.get("filter_paths", [])
        for p in filter_paths:
            self.add_path_to_filter(p)

        self._exclude_dependencies = kwargs.get("exclude_dependencies", False)

        triage_mode = kwargs.get("triage_mode", False)
        self._triage_mode = triage_mode

        for parser in self._parsers:
            parser.parse_contracts()

        # skip_analyze is only used for testing
        if not kwargs.get("skip_analyze", False):
            for parser in self._parsers:
                parser.analyze_contracts()
Пример #18
0
    def __init__(self, target, **kwargs):
        """
        Args:
            target (str | list(json) | CryticCompile)
        Keyword Args:
            solc (str): solc binary location (default 'solc')
            disable_solc_warnings (bool): True to disable solc warnings (default false)
            solc_arguments (str): solc arguments (default '')
            ast_format (str): ast format (default '--ast-compact-json')
            filter_paths (list(str)): list of path to filter (default [])
            triage_mode (bool): if true, switch to triage mode (default false)
            exclude_dependencies (bool): if true, exclude results that are only related to dependencies
            generate_patches (bool): if true, patches are generated (json output only)

            truffle_ignore (bool): ignore truffle.js presence (default false)
            truffle_build_directory (str): build truffle directory (default 'build/contracts')
            truffle_ignore_compile (bool): do not run truffle compile (default False)
            truffle_version (str): use a specific truffle version (default None)

            embark_ignore (bool): ignore embark.js presence (default false)
            embark_ignore_compile (bool): do not run embark build (default False)
            embark_overwrite_config (bool): overwrite original config file (default false)

        """
        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.add_source_code(path)

        if kwargs.get("generate_patches", False):
            self.generate_patches = True

        self._markdown_root = kwargs.get("markdown_root", "")

        self._detectors = []
        self._printers = []

        filter_paths = kwargs.get("filter_paths", [])
        for p in filter_paths:
            self.add_path_to_filter(p)

        self._exclude_dependencies = kwargs.get("exclude_dependencies", False)

        triage_mode = kwargs.get("triage_mode", False)
        self._triage_mode = triage_mode

        self._parser.parse_contracts()

        # skip_analyze is only used for testing
        if not kwargs.get("skip_analyze", False):
            self._parser.analyze_contracts()
def parse_type(
    t: Union[Dict, UnknownType],
    caller_context: Union[CallerContextExpression,
                          "SlitherCompilationUnitSolc"],
) -> Type:
    """
    caller_context can be a SlitherCompilationUnitSolc because we recursively call the function
    and go up in the context's scope. If we are really lost we just go over the SlitherCompilationUnitSolc

    :param t:
    :type t:
    :param caller_context:
    :type caller_context:
    :return:
    :rtype:
    """
    # local import to avoid circular dependency
    # pylint: disable=too-many-locals,too-many-branches,too-many-statements
    # pylint: disable=import-outside-toplevel
    from slither.solc_parsing.expressions.expression_parsing import parse_expression
    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.declarations.custom_error import CustomErrorSolc
    from slither.solc_parsing.declarations.structure_top_level import StructureTopLevelSolc
    from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc
    from slither.solc_parsing.variables.top_level_variable import TopLevelVariableSolc

    sl: "SlitherCompilationUnit"
    renaming: Dict[str, str]
    user_defined_types: Dict[str, TypeAlias]
    # 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, SlitherCompilationUnitSolc) or (
            isinstance(caller_context, FunctionSolc)
            and caller_context.contract_parser is None):
        structures_direct_access: List["Structure"]
        if isinstance(caller_context, SlitherCompilationUnitSolc):
            sl = caller_context.compilation_unit
            next_context = caller_context
            renaming = {}
            user_defined_types = {}
        else:
            assert isinstance(caller_context, FunctionSolc)
            sl = caller_context.underlying_function.compilation_unit
            next_context = caller_context.slither_parser
            renaming = caller_context.underlying_function.file_scope.renaming
            user_defined_types = caller_context.underlying_function.file_scope.user_defined_types
        structures_direct_access = sl.structures_top_level
        all_structuress = [c.structures for c in sl.contracts]
        all_structures = [
            item for sublist in all_structuress for item in sublist
        ]
        all_structures += structures_direct_access
        enums_direct_access = sl.enums_top_level
        all_enumss = [c.enums for c in sl.contracts]
        all_enums = [item for sublist in all_enumss for item in sublist]
        all_enums += enums_direct_access
        contracts = sl.contracts
        functions = []
    elif isinstance(
            caller_context,
        (StructureTopLevelSolc, CustomErrorSolc, TopLevelVariableSolc)):
        if isinstance(caller_context, StructureTopLevelSolc):
            scope = caller_context.underlying_structure.file_scope
        elif isinstance(caller_context, TopLevelVariableSolc):
            scope = caller_context.underlying_variable.file_scope
        else:
            assert isinstance(caller_context, CustomErrorSolc)
            custom_error = caller_context.underlying_custom_error
            if isinstance(custom_error, CustomErrorTopLevel):
                scope = custom_error.file_scope
            else:
                assert isinstance(custom_error, CustomErrorContract)
                scope = custom_error.contract.file_scope

        next_context = caller_context.slither_parser
        structures_direct_access = list(scope.structures.values())
        all_structuress = [c.structures for c in scope.contracts.values()]
        all_structures = [
            item for sublist in all_structuress for item in sublist
        ]
        all_structures += structures_direct_access
        enums_direct_access = []
        all_enums = scope.enums.values()
        contracts = scope.contracts.values()
        functions = list(scope.functions)

        renaming = scope.renaming
        user_defined_types = scope.user_defined_types
    elif isinstance(caller_context, (ContractSolc, FunctionSolc)):
        if isinstance(caller_context, FunctionSolc):
            underlying_func = caller_context.underlying_function
            # If contract_parser is set to None, then underlying_function is a functionContract
            # See note above
            assert isinstance(underlying_func, FunctionContract)
            contract = underlying_func.contract
            next_context = caller_context.contract_parser
            scope = caller_context.underlying_function.file_scope
        else:
            contract = caller_context.underlying_contract
            next_context = caller_context
            scope = caller_context.underlying_contract.file_scope

        structures_direct_access = contract.structures
        structures_direct_access += contract.file_scope.structures.values()
        all_structuress = [
            c.structures for c in contract.file_scope.contracts.values()
        ]
        all_structures = [
            item for sublist in all_structuress for item in sublist
        ]
        all_structures += contract.file_scope.structures.values()
        enums_direct_access: List["Enum"] = contract.enums
        enums_direct_access += contract.file_scope.enums.values()
        all_enumss = [c.enums for c in contract.file_scope.contracts.values()]
        all_enums = [item for sublist in all_enumss for item in sublist]
        all_enums += contract.file_scope.enums.values()
        contracts = contract.file_scope.contracts.values()
        functions = contract.functions + contract.modifiers

        renaming = scope.renaming
        user_defined_types = scope.user_defined_types
    else:
        raise ParsingError(f"Incorrect caller context: {type(caller_context)}")

    is_compact_ast = caller_context.is_compact_ast
    if is_compact_ast:
        key = "nodeType"
    else:
        key = "name"

    if isinstance(t, UnknownType):
        name = t.name
        if name in renaming:
            name = renaming[name]
        if name in user_defined_types:
            return user_defined_types[name]
        return _find_from_type_name(
            name,
            functions,
            contracts,
            structures_direct_access,
            all_structures,
            enums_direct_access,
            all_enums,
        )

    if t[key] == "ElementaryTypeName":
        if is_compact_ast:
            return ElementaryType(t["name"])
        return ElementaryType(t["attributes"][key])

    if t[key] == "UserDefinedTypeName":
        if is_compact_ast:
            name = t["typeDescriptions"]["typeString"]
            if name in renaming:
                name = renaming[name]
            if name in user_defined_types:
                return user_defined_types[name]
            return _find_from_type_name(
                name,
                functions,
                contracts,
                structures_direct_access,
                all_structures,
                enums_direct_access,
                all_enums,
            )

        # Determine if we have a type node (otherwise we use the name node, as some older solc did not have 'type').
        type_name_key = "type" if "type" in t["attributes"] else key

        name = t["attributes"][type_name_key]
        if name in renaming:
            name = renaming[name]
        if name in user_defined_types:
            return user_defined_types[name]
        return _find_from_type_name(
            name,
            functions,
            contracts,
            structures_direct_access,
            all_structures,
            enums_direct_access,
            all_enums,
        )

    # Introduced with Solidity 0.8
    if t[key] == "IdentifierPath":
        if is_compact_ast:
            name = t["name"]
            if name in renaming:
                name = renaming[name]
            if name in user_defined_types:
                return user_defined_types[name]
            return _find_from_type_name(
                name,
                functions,
                contracts,
                structures_direct_access,
                all_structures,
                enums_direct_access,
                all_enums,
            )

        raise SlitherError("Solidity 0.8 not supported with the legacy AST")

    if t[key] == "ArrayTypeName":
        length = None
        if is_compact_ast:
            if t.get("length", None):
                length = parse_expression(t["length"], caller_context)
            array_type = parse_type(t["baseType"], next_context)
        else:
            if len(t["children"]) == 2:
                length = parse_expression(t["children"][1], caller_context)
            else:
                assert len(t["children"]) == 1
            array_type = parse_type(t["children"][0], next_context)
        return ArrayType(array_type, length)

    if t[key] == "Mapping":

        if is_compact_ast:
            mappingFrom = parse_type(t["keyType"], next_context)
            mappingTo = parse_type(t["valueType"], next_context)
        else:
            assert len(t["children"]) == 2

            mappingFrom = parse_type(t["children"][0], next_context)
            mappingTo = parse_type(t["children"][1], next_context)

        return MappingType(mappingFrom, mappingTo)

    if t[key] == "FunctionTypeName":

        if is_compact_ast:
            params = t["parameterTypes"]
            return_values = t["returnParameterTypes"]
            index = "parameters"
        else:
            assert len(t["children"]) == 2
            params = t["children"][0]
            return_values = t["children"][1]
            index = "children"

        assert params[key] == "ParameterList"
        assert return_values[key] == "ParameterList"

        params_vars: List[FunctionTypeVariable] = []
        return_values_vars: List[FunctionTypeVariable] = []
        for p in params[index]:
            var = FunctionTypeVariable()
            var.set_offset(p["src"], caller_context.compilation_unit)

            var_parser = FunctionTypeVariableSolc(var, p)
            var_parser.analyze(caller_context)

            params_vars.append(var)
        for p in return_values[index]:
            var = FunctionTypeVariable()
            var.set_offset(p["src"], caller_context.compilation_unit)

            var_parser = FunctionTypeVariableSolc(var, p)
            var_parser.analyze(caller_context)

            return_values_vars.append(var)

        return FunctionType(params_vars, return_values_vars)

    raise ParsingError("Type name not found " + str(t))
Пример #20
0
def parse_type(t: Union[Dict, UnknownType], caller_context):
    # local import to avoid circular dependency
    # pylint: disable=too-many-locals,too-many-branches,too-many-statements
    # pylint: disable=import-outside-toplevel
    from slither.solc_parsing.expressions.expression_parsing import parse_expression
    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.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, SlitherCompilationUnitSolc) or (
            isinstance(caller_context, FunctionSolc)
            and caller_context.contract_parser is None):
        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.compilation_unit
            next_context = caller_context.slither_parser
        structures_direct_access = sl.structures_top_level
        all_structuress = [c.structures for c in sl.contracts]
        all_structures = [
            item for sublist in all_structuress for item in sublist
        ]
        all_structures += structures_direct_access
        enums_direct_access = sl.enums_top_level
        all_enumss = [c.enums for c in sl.contracts]
        all_enums = [item for sublist in all_enumss for item in sublist]
        all_enums += enums_direct_access
        contracts = sl.contracts
        functions = []
    elif isinstance(caller_context, (ContractSolc, FunctionSolc)):
        if isinstance(caller_context, FunctionSolc):
            underlying_func = caller_context.underlying_function
            # If contract_parser is set to None, then underlying_function is a functionContract
            # See note above
            assert isinstance(underlying_func, FunctionContract)
            contract = underlying_func.contract
            next_context = caller_context.contract_parser
        else:
            contract = caller_context.underlying_contract
            next_context = caller_context

        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.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.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)}")

    is_compact_ast = caller_context.is_compact_ast
    if is_compact_ast:
        key = "nodeType"
    else:
        key = "name"

    if isinstance(t, UnknownType):
        return _find_from_type_name(
            t.name,
            functions,
            contracts,
            structures_direct_access,
            all_structures,
            enums_direct_access,
            all_enums,
        )

    if t[key] == "ElementaryTypeName":
        if is_compact_ast:
            return ElementaryType(t["name"])
        return ElementaryType(t["attributes"][key])

    if t[key] == "UserDefinedTypeName":
        if is_compact_ast:
            return _find_from_type_name(
                t["typeDescriptions"]["typeString"],
                functions,
                contracts,
                structures_direct_access,
                all_structures,
                enums_direct_access,
                all_enums,
            )

        # Determine if we have a type node (otherwise we use the name node, as some older solc did not have 'type').
        type_name_key = "type" if "type" in t["attributes"] else key
        return _find_from_type_name(
            t["attributes"][type_name_key],
            functions,
            contracts,
            structures_direct_access,
            all_structures,
            enums_direct_access,
            all_enums,
        )

    # Introduced with Solidity 0.8
    if t[key] == "IdentifierPath":
        if is_compact_ast:
            return _find_from_type_name(
                t["name"],
                functions,
                contracts,
                structures_direct_access,
                all_structures,
                enums_direct_access,
                all_enums,
            )

        raise SlitherError("Solidity 0.8 not supported with the legacy AST")

    if t[key] == "ArrayTypeName":
        length = None
        if is_compact_ast:
            if t.get("length", None):
                length = parse_expression(t["length"], caller_context)
            array_type = parse_type(t["baseType"], next_context)
        else:
            if len(t["children"]) == 2:
                length = parse_expression(t["children"][1], caller_context)
            else:
                assert len(t["children"]) == 1
            array_type = parse_type(t["children"][0], next_context)
        return ArrayType(array_type, length)

    if t[key] == "Mapping":

        if is_compact_ast:
            mappingFrom = parse_type(t["keyType"], next_context)
            mappingTo = parse_type(t["valueType"], next_context)
        else:
            assert len(t["children"]) == 2

            mappingFrom = parse_type(t["children"][0], next_context)
            mappingTo = parse_type(t["children"][1], next_context)

        return MappingType(mappingFrom, mappingTo)

    if t[key] == "FunctionTypeName":

        if is_compact_ast:
            params = t["parameterTypes"]
            return_values = t["returnParameterTypes"]
            index = "parameters"
        else:
            assert len(t["children"]) == 2
            params = t["children"][0]
            return_values = t["children"][1]
            index = "children"

        assert params[key] == "ParameterList"
        assert return_values[key] == "ParameterList"

        params_vars: List[FunctionTypeVariable] = []
        return_values_vars: List[FunctionTypeVariable] = []
        for p in params[index]:
            var = FunctionTypeVariable()
            var.set_offset(p["src"], caller_context.compilation_unit)

            var_parser = FunctionTypeVariableSolc(var, p)
            var_parser.analyze(caller_context)

            params_vars.append(var)
        for p in return_values[index]:
            var = FunctionTypeVariable()
            var.set_offset(p["src"], caller_context.compilation_unit)

            var_parser = FunctionTypeVariableSolc(var, p)
            var_parser.analyze(caller_context)

            return_values_vars.append(var)

        return FunctionType(params_vars, return_values_vars)

    raise ParsingError("Type name not found " + str(t))