Beispiel #1
0
def parse_yul_function_call(root: YulScope, node: YulNode,
                            ast: Dict) -> Optional[Expression]:
    args = [parse_yul(root, node, arg) for arg in ast["arguments"]]
    ident = parse_yul(root, node, ast["functionName"])

    if not isinstance(ident, Identifier):
        raise SlitherException(
            "expected identifier from parsing function name")

    if isinstance(ident.value, YulBuiltin):
        name = ident.value.name
        if name in binary_ops:
            if name in ["shl", "shr", "sar"]:
                # lmao ok
                return BinaryOperation(args[1], args[0], binary_ops[name])

            return BinaryOperation(args[0], args[1], binary_ops[name])

        if name in unary_ops:
            return UnaryOperation(args[0], unary_ops[name])

        ident = Identifier(
            SolidityFunction(format_function_descriptor(ident.value.name)))

    if isinstance(ident.value, Function):
        return CallExpression(ident, args,
                              vars_to_typestr(ident.value.returns))
    if isinstance(ident.value, SolidityFunction):
        return CallExpression(ident, args,
                              vars_to_typestr(ident.value.return_type))

    raise SlitherException(
        f"unexpected function call target type {str(type(ident.value))}")
Beispiel #2
0
 def _analyze_content_top_level_function(self):
     try:
         for func_parser in self._functions_top_level_parser:
             func_parser.analyze_content()
     except (VariableNotFound, KeyError) as e:
         raise SlitherException(
             f"Missing {e} during top level function analyze") from e
Beispiel #3
0
 def state_variable(self):
     if self._name.endswith("_slot"):
         return self._name[:-5]
     if self._name.endswith("_offset"):
         return self._name[:-7]
     to_log = f"Incorrect YUL parsing. {self} is not a solidity variable that can be seen as a state variable"
     raise SlitherException(to_log)
Beispiel #4
0
def convert_subdenomination(
    value: str, sub: str
) -> int:  # pylint: disable=too-many-return-statements

    # to allow 0.1 ether conversion
    if value[0:2] == "0x":
        decimal_value = Decimal(int(value, 16))
    else:
        decimal_value = Decimal(value)
    if sub == "wei":
        return int(decimal_value)
    if sub == "szabo":
        return int(decimal_value * int(1e12))
    if sub == "finney":
        return int(decimal_value * int(1e15))
    if sub == "ether":
        return int(decimal_value * int(1e18))
    if sub == "seconds":
        return int(decimal_value)
    if sub == "minutes":
        return int(decimal_value * 60)
    if sub == "hours":
        return int(decimal_value * 60 * 60)
    if sub == "days":
        return int(decimal_value * 60 * 60 * 24)
    if sub == "weeks":
        return int(decimal_value * 60 * 60 * 24 * 7)
    if sub == "years":
        return int(decimal_value * 60 * 60 * 24 * 7 * 365)

    raise SlitherException(f"Subdemonination conversion impossible {decimal_value} {sub}")
Beispiel #5
0
def parse_yul_identifier(root: YulScope, _node: YulNode, ast: Dict) -> Optional[Expression]:
    name = ast["name"]

    if name in builtins:
        return Identifier(YulBuiltin(name))

    # check function-scoped variables
    parent_func = root.parent_func
    if parent_func:
        variable = parent_func.get_local_variable_from_name(name)
        if variable:
            return Identifier(variable)

        if isinstance(parent_func, FunctionContract):
            variable = parent_func.contract.get_state_variable_from_name(name)
            if variable:
                return Identifier(variable)

    # check yul-scoped variable
    variable = root.get_yul_local_variable_from_name(name)
    if variable:
        return Identifier(variable.underlying)

    # check yul-scoped function

    func = root.get_yul_local_function_from_name(name)
    if func:
        return Identifier(func.underlying)

    magic_suffix = _parse_yul_magic_suffixes(name, root)
    if magic_suffix:
        return magic_suffix

    raise SlitherException(f"unresolved reference to identifier {name}")
Beispiel #6
0
 def _analyze_top_level_variables(self):
     try:
         for var in self._variables_top_level_parser:
             var.analyze(var)
     except (VariableNotFound, KeyError) as e:
         raise SlitherException(
             f"Missing {e} during variable analyze") from e
Beispiel #7
0
    def storage_size(self) -> Tuple[int, bool]:
        from slither.core.declarations.structure import Structure
        from slither.core.declarations.enum import Enum
        from slither.core.declarations.contract import Contract

        if isinstance(self._type, Contract):
            return 20, False
        if isinstance(self._type, Enum):
            return int(math.ceil(math.log2(len(self._type.values)) / 8)), False
        if isinstance(self._type, Structure):
            # todo there's some duplicate logic here and slither_core, can we refactor this?
            slot = 0
            offset = 0
            for elem in self._type.elems_ordered:
                size, new_slot = elem.type.storage_size
                if new_slot:
                    if offset > 0:
                        slot += 1
                        offset = 0
                elif size + offset > 32:
                    slot += 1
                    offset = 0

                if new_slot:
                    slot += math.ceil(size / 32)
                else:
                    offset += size
            if offset > 0:
                slot += 1
            return slot * 32, True
        to_log = f"{self} does not have storage size"
        raise SlitherException(to_log)
Beispiel #8
0
def convert_subdenomination(value, sub):

    # to allow 0.1 ether conversion
    if value[0:2] == "0x":
        value = float(int(value, 16))
    else:
        value = float(value)
    if sub == 'wei':
        return int(value)
    if sub == 'szabo':
        return int(value * int(1e12))
    if sub == 'finney':
        return int(value * int(1e15))
    if sub == 'ether':
        return int(value * int(1e18))
    if sub == 'seconds':
        return int(value)
    if sub == 'minutes':
        return int(value * 60)
    if sub == 'hours':
        return int(value * 60 * 60)
    if sub == 'days':
        return int(value * 60 * 60 * 24)
    if sub == 'weeks':
        return int(value * 60 * 60 * 24 * 7)
    if sub == 'years':
        return int(value * 60 * 60 * 24 * 7 * 365)

    raise SlitherException(f'Subdemonination conversion impossible {value} {sub}')
Beispiel #9
0
    def analyze_contracts(self):  # pylint: disable=too-many-statements,too-many-branches
        if not self._parsed:
            raise SlitherException("Parse the contract before running analyses")
        self._convert_to_slithir()

        compute_dependency(self._core)
        self._core.compute_storage_layout()
        self._analyzed = True
Beispiel #10
0
 def _analyze_top_level_structures(self):
     try:
         for struct in self._structures_top_level_parser:
             struct.analyze()
     except (VariableNotFound, KeyError) as e:
         raise SlitherException(
             f"Missing struct {e} during top level structure analyze"
         ) from e
Beispiel #11
0
    def _get_source_code(self, contract):
        src_mapping = contract.source_mapping
        content = self._slither.source_code[src_mapping['filename_absolute']]
        start = src_mapping['start']
        end = src_mapping['start'] + src_mapping['length']

        # interface must use external
        if self._external_to_public and contract.contract_kind != "interface":
            # to_patch is a list of (index, bool). The bool indicates
            # if the index is for external -> public (true)
            # or a calldata -> memory (false)
            to_patch = []
            for f in contract.functions_declared:
                # fallback must be external
                if f.is_fallback or f.is_constructor_variables:
                    continue
                if f.visibility == 'external':
                    attributes_start = (
                        f.parameters_src.source_mapping['start'] +
                        f.parameters_src.source_mapping['length'])
                    attributes_end = f.returns_src.source_mapping['start']
                    attributes = content[attributes_start:attributes_end]
                    regex = re.search(
                        r'((\sexternal)\s+)|(\sexternal)$|(\)external)$',
                        attributes)
                    if regex:
                        to_patch.append(
                            (attributes_start + regex.span()[0] + 1, True))
                    else:
                        raise SlitherException(
                            f'External keyword not found {f.name} {attributes}'
                        )

                    for var in f.parameters:
                        if var.location == "calldata":
                            calldata_start = var.source_mapping['start']
                            calldata_end = calldata_start + var.source_mapping[
                                'length']
                            calldata_idx = content[
                                calldata_start:calldata_end].find(' calldata ')
                            to_patch.append(
                                (calldata_start + calldata_idx + 1, False))

            to_patch.sort(key=lambda x: x[0], reverse=True)

            content = content[start:end]
            for (index, is_external) in to_patch:
                index = index - start
                if is_external:
                    content = content[:index] + 'public' + content[
                        index + len('external'):]
                else:
                    content = content[:index] + 'memory' + content[
                        index + len('calldata'):]
        else:
            content = content[start:end]

        self._source_codes[contract] = content
Beispiel #12
0
    def new_node(self, node_type, src) -> YulNode:
        if self._function:
            node = self._function.new_node(node_type, src, self.node_scope)
        else:
            raise SlitherException("standalone yul objects are not supported yet")

        yul_node = YulNode(node, self)
        self._nodes.append(yul_node)
        return yul_node
Beispiel #13
0
    def new_node(self, node_type: NodeType, src: Union[str, Dict]) -> YulNode:
        if self._parent_func:
            node = self._parent_func.new_node(node_type, src, self.node_scope)
        else:
            raise SlitherException("standalone yul objects are not supported yet")

        yul_node = YulNode(node, self)
        self._nodes.append(yul_node)
        return yul_node
Beispiel #14
0
def parse_yul_identifier(root: YulScope, _node: YulNode,
                         ast: Dict) -> Optional[Expression]:
    name = ast["name"]

    if name in builtins:
        return Identifier(YulBuiltin(name))

    # check function-scoped variables
    if root.parent_func:
        variable = root.parent_func.get_local_variable_from_name(name)
        if variable:
            return Identifier(variable)

        variable = root.parent_func.contract.get_state_variable_from_name(name)
        if variable:
            return Identifier(variable)

    # check yul-scoped variable
    variable = root.get_yul_local_variable_from_name(name)
    if variable:
        return Identifier(variable.underlying)

    # check yul-scoped function

    func = root.get_yul_local_function_from_name(name)
    if func:
        return Identifier(func.underlying)

    # check for magic suffixes
    if name.endswith("_slot"):
        potential_name = name[:-5]
        var = root.function.contract.get_state_variable_from_name(
            potential_name)
        if var:
            return Identifier(var)
        var = root.function.get_local_variable_from_name(potential_name)
        if var and var.is_storage:
            return Identifier(var)
    if name.endswith("_offset"):
        potential_name = name[:-7]
        var = root.function.contract.get_state_variable_from_name(
            potential_name)
        if var:
            return Identifier(var)

    raise SlitherException(f"unresolved reference to identifier {name}")
Beispiel #15
0
    def _get_source_code(self, contract):
        src_mapping = contract.source_mapping
        content = self._slither.source_code[src_mapping['filename_absolute']]
        start = src_mapping['start']
        end = src_mapping['start'] + src_mapping['length']

        to_patch = []
        # interface must use external
        if self._external_to_public and contract.contract_kind != "interface":
            for f in contract.functions_declared:
                # fallback must be external
                if f.is_fallback or f.is_constructor_variables:
                    continue
                if f.visibility == 'external':
                    attributes_start = (
                        f.parameters_src.source_mapping['start'] +
                        f.parameters_src.source_mapping['length'])
                    attributes_end = f.returns_src.source_mapping['start']
                    attributes = content[attributes_start:attributes_end]
                    regex = re.search(
                        r'((\sexternal)\s+)|(\sexternal)$|(\)external)$',
                        attributes)
                    if regex:
                        to_patch.append(
                            Patch(attributes_start + regex.span()[0] + 1,
                                  'public_to_external'))
                    else:
                        raise SlitherException(
                            f'External keyword not found {f.name} {attributes}'
                        )

                    for var in f.parameters:
                        if var.location == "calldata":
                            calldata_start = var.source_mapping['start']
                            calldata_end = calldata_start + var.source_mapping[
                                'length']
                            calldata_idx = content[
                                calldata_start:calldata_end].find(' calldata ')
                            to_patch.append(
                                Patch(calldata_start + calldata_idx + 1,
                                      'calldata_to_memory'))

        if self._private_to_internal:
            for variable in contract.state_variables_declared:
                if variable.visibility == 'private':
                    print(variable.source_mapping)
                    attributes_start = variable.source_mapping['start']
                    attributes_end = attributes_start + variable.source_mapping[
                        'length']
                    attributes = content[attributes_start:attributes_end]
                    print(attributes)
                    regex = re.search(r' private ', attributes)
                    if regex:
                        to_patch.append(
                            Patch(attributes_start + regex.span()[0] + 1,
                                  'private_to_internal'))
                    else:
                        raise SlitherException(
                            f'private keyword not found {v.name} {attributes}')

        if self._remove_assert:
            for function in contract.functions_and_modifiers_declared:
                for node in function.nodes:
                    for ir in node.irs:
                        if isinstance(
                                ir, SolidityCall
                        ) and ir.function == SolidityFunction('assert(bool)'):
                            to_patch.append(
                                Patch(node.source_mapping['start'],
                                      'line_removal'))
                            logger.info(
                                f'Code commented: {node.expression} ({node.source_mapping_str})'
                            )

        to_patch.sort(key=lambda x: x.index, reverse=True)

        content = content[start:end]
        for patch in to_patch:
            patch_type = patch.patch_type
            index = patch.index
            index = index - start
            if patch_type == 'public_to_external':
                content = content[:index] + 'public' + content[
                    index + len('external'):]
            if patch_type == 'private_to_internal':
                content = content[:index] + 'internal' + content[
                    index + len('private'):]
            elif patch_type == 'calldata_to_memory':
                content = content[:index] + 'memory' + content[
                    index + len('calldata'):]
            else:
                assert patch_type == 'line_removal'
                content = content[:index] + ' // ' + content[index:]

        self._source_codes[contract] = content
Beispiel #16
0
 def min(self) -> int:
     if self.name in MinValues:
         return MinValues[self.name]
     raise SlitherException(f"{self.name} does not have a min value")
Beispiel #17
0
    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"
            )
        if self._parsed:
            raise Exception("Contract analysis can be run only once!")

        # First we save all the contracts in a dict
        # the key is the contractid
        for contract in self._underlying_contract_to_parser:
            if (
                contract.name.startswith("SlitherInternalTopLevelContract")
                and not contract.is_top_level
            ):
                raise SlitherException(
                    """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(
                        contract.source_mapping_str
                    )
                    self._core.contract_name_collisions[contract.name].append(
                        self._core.contracts_as_dict[contract.name].source_mapping_str
                    )
            else:
                self._contracts_by_id[contract.id] = contract
                self._core.contracts_as_dict[contract.name] = contract

        # Update of the inheritance
        for contract_parser in self._underlying_contract_to_parser.values():
            # remove the first elem in linearizedBaseContracts as it is the contract itself
            ancestors = []
            fathers = []
            father_constructors = []
            # try:
            # Resolve linearized base contracts.
            missing_inheritance = False

            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])
                    )
                elif i in self._contracts_by_id:
                    ancestors.append(self._contracts_by_id[i])
                else:
                    missing_inheritance = True

            # 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]))
                elif i in self._contracts_by_id:
                    fathers.append(self._contracts_by_id[i])
                else:
                    missing_inheritance = True

            # Resolve immediate base constructor calls
            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])
                    )
                elif i in self._contracts_by_id:
                    father_constructors.append(self._contracts_by_id[i])
                else:
                    missing_inheritance = True

            contract_parser.underlying_contract.set_inheritance(
                ancestors, fathers, father_constructors
            )

            if missing_inheritance:
                self._core.contracts_with_missing_inheritance.add(
                    contract_parser.underlying_contract
                )
                contract_parser.log_incorrect_parsing(f"Missing inheritance {contract_parser}")
                contract_parser.set_is_analyzed(True)
                contract_parser.delete_content()

        contracts_to_be_analyzed = list(self._underlying_contract_to_parser.values())

        # Any contract can refer another contract enum without need for inheritance
        self._analyze_all_enums(contracts_to_be_analyzed)
        # pylint: disable=expression-not-assigned
        [c.set_is_analyzed(False) for c in self._underlying_contract_to_parser.values()]

        libraries = [
            c for c in contracts_to_be_analyzed if c.underlying_contract.contract_kind == "library"
        ]
        contracts_to_be_analyzed = [
            c for c in contracts_to_be_analyzed if c.underlying_contract.contract_kind != "library"
        ]

        # We first parse the struct/variables/functions/contract
        self._analyze_first_part(contracts_to_be_analyzed, libraries)
        # pylint: disable=expression-not-assigned
        [c.set_is_analyzed(False) for c in self._underlying_contract_to_parser.values()]

        # We analyze the struct and parse and analyze the events
        # A contract can refer in the variables a struct or a event from any contract
        # (without inheritance link)
        self._analyze_second_part(contracts_to_be_analyzed, libraries)
        [c.set_is_analyzed(False) for c in self._underlying_contract_to_parser.values()]

        # Then we analyse state variables, functions and modifiers
        self._analyze_third_part(contracts_to_be_analyzed, libraries)

        self._parsed = True
Beispiel #18
0
 def max(self) -> int:
     if self.name in MaxValues:
         return MaxValues[self.name]
     raise SlitherException(f"{self.name} does not have a max value")
Beispiel #19
0
def parse_yul_unsupported(_root: YulScope, _node: YulNode,
                          ast: Dict) -> Optional[Expression]:
    raise SlitherException(
        f"no parser available for {ast['nodeType']} {json.dumps(ast, indent=2)}"
    )
Beispiel #20
0
def convert_yul_unsupported(root: YulScope, parent: YulNode,
                            ast: Dict) -> YulNode:
    raise SlitherException(
        f"no converter available for {ast['nodeType']} {json.dumps(ast, indent=2)}"
    )
    def parse_top_level_from_loaded_json(
        self, data_loaded: Dict, filename: str
    ):  # pylint: disable=too-many-branches,too-many-statements
        if "nodeType" in data_loaded:
            self._is_compact_ast = True

        if "sourcePaths" in data_loaded:
            for sourcePath in data_loaded["sourcePaths"]:
                if os.path.isfile(sourcePath):
                    self._compilation_unit.core.add_source_code(sourcePath)

        if data_loaded[self.get_key()] == "root":
            logger.error("solc <0.4 is not supported")
            return
        if data_loaded[self.get_key()] == "SourceUnit":
            self._parse_source_unit(data_loaded, filename)
        else:
            logger.error("solc version is not supported")
            return

        for top_level_data in data_loaded[self.get_children()]:
            if top_level_data[self.get_key()] == "ContractDefinition":
                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._compilation_unit)

                self._underlying_contract_to_parser[contract] = contract_parser

            elif top_level_data[self.get_key()] == "PragmaDirective":
                if self._is_compact_ast:
                    pragma = Pragma(top_level_data["literals"])
                else:
                    pragma = Pragma(top_level_data["attributes"]["literals"])
                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"])
                    # TODO investigate unitAlias in version < 0.7 and legacy ast
                    if "unitAlias" in top_level_data:
                        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._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._compilation_unit)
                st_parser = StructureTopLevelSolc(st, top_level_data, self)

                self._compilation_unit.structures_top_level.append(st)
                self._structures_top_level_parser.append(st_parser)

            elif top_level_data[self.get_key()] == "EnumDefinition":
                # Note enum don't need a complex parser, so everything is directly done
                self._parse_enum(top_level_data)

            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._compilation_unit)

                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._compilation_unit)
                func_parser = FunctionSolc(func, top_level_data, None, self)

                self._compilation_unit.functions_top_level.append(func)
                self._functions_top_level_parser.append(func_parser)
                self.add_function_or_modifier_parser(func_parser)

            else:
                raise SlitherException(f"Top level {top_level_data[self.get_key()]} not supported")
Beispiel #22
0
    def _get_source_code(self, contract: Contract):  # pylint: disable=too-many-branches,too-many-statements
        """
        Save the source code of the contract in self._source_codes
        Patch the source code
        :param contract:
        :return:
        """
        src_mapping = contract.source_mapping
        content = self._slither.source_code[
            src_mapping["filename_absolute"]].encode("utf8")
        start = src_mapping["start"]
        end = src_mapping["start"] + src_mapping["length"]

        to_patch = []
        # interface must use external
        if self._external_to_public and contract.contract_kind != "interface":
            for f in contract.functions_declared:
                # fallback must be external
                if f.is_fallback or f.is_constructor_variables:
                    continue
                if f.visibility == "external":
                    attributes_start = (
                        f.parameters_src().source_mapping["start"] +
                        f.parameters_src().source_mapping["length"])
                    attributes_end = f.returns_src().source_mapping["start"]
                    attributes = content[attributes_start:attributes_end]
                    regex = re.search(
                        r"((\sexternal)\s+)|(\sexternal)$|(\)external)$",
                        attributes)
                    if regex:
                        to_patch.append(
                            Patch(
                                attributes_start + regex.span()[0] + 1,
                                "public_to_external",
                            ))
                    else:
                        raise SlitherException(
                            f"External keyword not found {f.name} {attributes}"
                        )

                    for var in f.parameters:
                        if var.location == "calldata":
                            calldata_start = var.source_mapping["start"]
                            calldata_end = calldata_start + var.source_mapping[
                                "length"]
                            calldata_idx = content[
                                calldata_start:calldata_end].find(" calldata ")
                            to_patch.append(
                                Patch(
                                    calldata_start + calldata_idx + 1,
                                    "calldata_to_memory",
                                ))

        if self._private_to_internal:
            for variable in contract.state_variables_declared:
                if variable.visibility == "private":
                    print(variable.source_mapping)
                    attributes_start = variable.source_mapping["start"]
                    attributes_end = attributes_start + variable.source_mapping[
                        "length"]
                    attributes = content[attributes_start:attributes_end]
                    print(attributes)
                    regex = re.search(r" private ", attributes)
                    if regex:
                        to_patch.append(
                            Patch(
                                attributes_start + regex.span()[0] + 1,
                                "private_to_internal",
                            ))
                    else:
                        raise SlitherException(
                            f"private keyword not found {variable.name} {attributes}"
                        )

        if self._remove_assert:
            for function in contract.functions_and_modifiers_declared:
                for node in function.nodes:
                    for ir in node.irs:
                        if isinstance(
                                ir, SolidityCall
                        ) and ir.function == SolidityFunction("assert(bool)"):
                            to_patch.append(
                                Patch(node.source_mapping["start"],
                                      "line_removal"))
                            logger.info(
                                f"Code commented: {node.expression} ({node.source_mapping_str})"
                            )

        to_patch.sort(key=lambda x: x.index, reverse=True)

        content = content[start:end]
        for patch in to_patch:
            patch_type = patch.patch_type
            index = patch.index
            index = index - start
            if patch_type == "public_to_external":
                content = content[:index] + "public" + content[
                    index + len("external"):]
            if patch_type == "private_to_internal":
                content = content[:index] + "internal" + content[
                    index + len("private"):]
            elif patch_type == "calldata_to_memory":
                content = content[:index] + "memory" + content[
                    index + len("calldata"):]
            else:
                assert patch_type == "line_removal"
                content = content[:index] + " // " + content[index:]

        self._source_codes[contract] = content.decode("utf8")
Beispiel #23
0
    def parse_top_level_from_loaded_json(self, data_loaded: Dict,
                                         filename: str):  # pylint: disable=too-many-branches,too-many-statements,too-many-locals
        if "nodeType" in data_loaded:
            self._is_compact_ast = True

        if "sourcePaths" in data_loaded:
            for sourcePath in data_loaded["sourcePaths"]:
                if os.path.isfile(sourcePath):
                    self._compilation_unit.core.add_source_code(sourcePath)

        if data_loaded[self.get_key()] == "root":
            logger.error("solc <0.4 is not supported")
            return
        if data_loaded[self.get_key()] == "SourceUnit":
            self._parse_source_unit(data_loaded, filename)
        else:
            logger.error("solc version is not supported")
            return

        if self.get_children() not in data_loaded:
            return
        scope = self.compilation_unit.get_scope(filename)

        for top_level_data in data_loaded[self.get_children()]:
            if top_level_data[self.get_key()] == "ContractDefinition":
                contract = Contract(self._compilation_unit, scope)
                contract_parser = ContractSolc(self, contract, top_level_data)
                scope.contracts[contract.name] = contract
                if "src" in top_level_data:
                    contract.set_offset(top_level_data["src"],
                                        self._compilation_unit)

                self._underlying_contract_to_parser[contract] = contract_parser

            elif top_level_data[self.get_key()] == "PragmaDirective":
                if self._is_compact_ast:
                    pragma = Pragma(top_level_data["literals"], scope)
                    scope.pragmas.add(pragma)
                else:
                    pragma = Pragma(top_level_data["attributes"]["literals"],
                                    scope)
                    scope.pragmas.add(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(
                        Path(top_level_data["absolutePath"], ),
                        scope,
                    )
                    scope.imports.add(import_directive)
                    # TODO investigate unitAlias in version < 0.7 and legacy ast
                    if "unitAlias" in top_level_data:
                        import_directive.alias = top_level_data["unitAlias"]
                    if "symbolAliases" in top_level_data:
                        symbol_aliases = top_level_data["symbolAliases"]
                        _handle_import_aliases(symbol_aliases,
                                               import_directive, scope)
                else:
                    import_directive = Import(
                        Path(
                            top_level_data["attributes"].get(
                                "absolutePath", ""), ),
                        scope,
                    )
                    scope.imports.add(import_directive)
                    # TODO investigate unitAlias in version < 0.7 and legacy ast
                    if ("attributes" in top_level_data
                            and "unitAlias" in top_level_data["attributes"]):
                        import_directive.alias = top_level_data["attributes"][
                            "unitAlias"]
                import_directive.set_offset(top_level_data["src"],
                                            self._compilation_unit)
                self._compilation_unit.import_directives.append(
                    import_directive)

                get_imported_scope = self.compilation_unit.get_scope(
                    import_directive.filename)
                scope.accessible_scopes.append(get_imported_scope)

            elif top_level_data[self.get_key()] == "StructDefinition":
                scope = self.compilation_unit.get_scope(filename)
                st = StructureTopLevel(self.compilation_unit, scope)
                st.set_offset(top_level_data["src"], self._compilation_unit)
                st_parser = StructureTopLevelSolc(st, top_level_data, self)
                scope.structures[st.name] = 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":
                # Note enum don't need a complex parser, so everything is directly done
                self._parse_enum(top_level_data, filename)

            elif top_level_data[self.get_key()] == "VariableDeclaration":
                var = TopLevelVariable(scope)
                var_parser = TopLevelVariableSolc(var, top_level_data, self)
                var.set_offset(top_level_data["src"], self._compilation_unit)

                self._compilation_unit.variables_top_level.append(var)
                self._variables_top_level_parser.append(var_parser)
                scope.variables[var.name] = var
            elif top_level_data[self.get_key()] == "FunctionDefinition":
                scope = self.compilation_unit.get_scope(filename)
                func = FunctionTopLevel(self._compilation_unit, scope)
                scope.functions.add(func)
                func.set_offset(top_level_data["src"], self._compilation_unit)
                func_parser = FunctionSolc(func, top_level_data, None, self)

                self._compilation_unit.functions_top_level.append(func)
                self._functions_top_level_parser.append(func_parser)
                self.add_function_or_modifier_parser(func_parser)

            elif top_level_data[self.get_key()] == "ErrorDefinition":
                scope = self.compilation_unit.get_scope(filename)
                custom_error = CustomErrorTopLevel(self._compilation_unit,
                                                   scope)
                custom_error.set_offset(top_level_data["src"],
                                        self._compilation_unit)

                custom_error_parser = CustomErrorSolc(custom_error,
                                                      top_level_data, self)
                scope.custom_errors.add(custom_error)
                self._compilation_unit.custom_errors.append(custom_error)
                self._custom_error_parser.append(custom_error_parser)

            elif top_level_data[
                    self.get_key()] == "UserDefinedValueTypeDefinition":
                assert "name" in top_level_data
                alias = top_level_data["name"]
                assert "underlyingType" in top_level_data
                underlying_type = top_level_data["underlyingType"]
                assert ("nodeType" in underlying_type and
                        underlying_type["nodeType"] == "ElementaryTypeName")
                assert "name" in underlying_type

                original_type = ElementaryType(underlying_type["name"])

                user_defined_type = TypeAliasTopLevel(original_type, alias,
                                                      scope)
                user_defined_type.set_offset(top_level_data["src"],
                                             self._compilation_unit)
                scope.user_defined_types[alias] = user_defined_type

            else:
                raise SlitherException(
                    f"Top level {top_level_data[self.get_key()]} not supported"
                )
Beispiel #24
0
    def _analyze_contracts(self):
        if not self._contractsNotParsed:
            logger.info(
                f'No contract were found in {self.filename}, check the correct compilation'
            )
        if self._analyzed:
            raise Exception('Contract analysis can be run only once!')

        # First we save all the contracts in a dict
        # the key is the contractid
        for contract in self._contractsNotParsed:
            if contract.name.startswith('SlitherInternalTopLevelContract'
                                        ) and not contract.is_top_level:
                raise SlitherException(
                    """Your codebase has a contract named 'SlitherInternalTopLevelContract'.
Please rename it, this name is reserved for Slither's internals""")
            if contract.name in self._contracts:
                if contract.id != self._contracts[contract.name].id:
                    self._contract_name_collisions[contract.name].append(
                        contract.source_mapping_str)
                    self._contract_name_collisions[contract.name].append(
                        self._contracts[contract.name].source_mapping_str)
            else:
                self._contracts_by_id[contract.id] = contract
                self._contracts[contract.name] = contract

        # Update of the inheritance
        for contract in self._contractsNotParsed:
            # remove the first elem in linearizedBaseContracts as it is the contract itself
            ancestors = []
            fathers = []
            father_constructors = []
            # try:
            # Resolve linearized base contracts.
            missing_inheritance = False

            for i in contract.linearizedBaseContracts[1:]:
                if i in contract.remapping:
                    ancestors.append(
                        self.get_contract_from_name(contract.remapping[i]))
                elif i in self._contracts_by_id:
                    ancestors.append(self._contracts_by_id[i])
                else:
                    missing_inheritance = True

            # Resolve immediate base contracts
            for i in contract.baseContracts:
                if i in contract.remapping:
                    fathers.append(
                        self.get_contract_from_name(contract.remapping[i]))
                elif i in self._contracts_by_id:
                    fathers.append(self._contracts_by_id[i])
                else:
                    missing_inheritance = True

            # Resolve immediate base constructor calls
            for i in contract.baseConstructorContractsCalled:
                if i in contract.remapping:
                    father_constructors.append(
                        self.get_contract_from_name(contract.remapping[i]))
                elif i in self._contracts_by_id:
                    father_constructors.append(self._contracts_by_id[i])
                else:
                    missing_inheritance = True

            contract.setInheritance(ancestors, fathers, father_constructors)

            if missing_inheritance:
                self._contract_with_missing_inheritance.add(contract)
                contract.log_incorrect_parsing(
                    f'Missing inheritance {contract}')
                contract.set_is_analyzed(True)
                contract.delete_content()

        contracts_to_be_analyzed = self.contracts

        # Any contract can refer another contract enum without need for inheritance
        self._analyze_all_enums(contracts_to_be_analyzed)
        [c.set_is_analyzed(False) for c in self.contracts]

        libraries = [
            c for c in contracts_to_be_analyzed if c.contract_kind == 'library'
        ]
        contracts_to_be_analyzed = [
            c for c in contracts_to_be_analyzed if c.contract_kind != 'library'
        ]

        # We first parse the struct/variables/functions/contract
        self._analyze_first_part(contracts_to_be_analyzed, libraries)
        [c.set_is_analyzed(False) for c in self.contracts]

        # We analyze the struct and parse and analyze the events
        # A contract can refer in the variables a struct or a event from any contract
        # (without inheritance link)
        self._analyze_second_part(contracts_to_be_analyzed, libraries)
        [c.set_is_analyzed(False) for c in self.contracts]

        # Then we analyse state variables, functions and modifiers
        self._analyze_third_part(contracts_to_be_analyzed, libraries)

        self._analyzed = True

        self._convert_to_slithir()

        compute_dependency(self)