def generate_result(self, info, additional_fields=None):
        output = Output(info,
                        additional_fields,
                        markdown_root=self.contract.slither.markdown_root)

        output.data["check"] = self.ARGUMENT

        return output
Пример #2
0
    def generate_result(self, info, additional_fields=None):
        output = Output(info,
                        additional_fields,
                        standard_format=self.STANDARD_JSON,
                        markdown_root=self.slither.markdown_root)

        output.data['check'] = self.ARGUMENT
        output.data['impact'] = classification_txt[self.IMPACT]
        output.data['confidence'] = classification_txt[self.CONFIDENCE]

        return output
Пример #3
0
    def generate_result(
        self,
        info: Union[str, List[Union[str, SupportedOutput]]],
        additional_fields: Optional[Dict] = None,
    ) -> Output:
        output = Output(
            info,
            additional_fields,
            markdown_root=self.contract.compilation_unit.core.markdown_root)

        output.data["check"] = self.ARGUMENT

        return output
Пример #4
0
    def generate_result(
        self,
        info: Union[str, List[Union[str, SupportedOutput]]],
        additional_fields: Optional[Dict] = None,
    ) -> Output:
        output = Output(
            info,
            additional_fields,
            standard_format=self.STANDARD_JSON,
            markdown_root=self.slither.markdown_root,
        )

        output.data["check"] = self.ARGUMENT
        output.data["impact"] = classification_txt[self.IMPACT]
        output.data["confidence"] = classification_txt[self.CONFIDENCE]

        return output
Пример #5
0
def check_initialization(contract):

    results = {
        'Initializable-present': False,
        'Initializable-inherited': False,
        'Initializable.initializer()-present': False,
        'missing-initializer-modifier': [],
        'initialize_target': {},
        'missing-calls': [],
        'multiple-calls': []
    }

    error_found = False

    logger.info(
        green(
            '\n## Run initialization checks... (see https://github.com/crytic/slither/wiki/Upgradeability-Checks#initialization-checks)'
        ))

    # Check if the Initializable contract is present
    initializable = contract.slither.get_contract_from_name('Initializable')
    if initializable is None:
        logger.info(
            yellow(
                'Initializable contract not found, the contract does not follow a standard initalization schema.'
            ))
        return results
    results['Initializable-present'] = True

    # Check if the Initializable contract is inherited
    if initializable not in contract.inheritance:
        logger.info(
            yellow('The logic contract does not call the initializer.'))
        return results
    results['Initializable-inherited'] = True

    # Check if the Initializable contract is inherited
    initializer = contract.get_modifier_from_canonical_name(
        'Initializable.initializer()')
    if initializer is None:
        logger.info(yellow('Initializable.initializer() does not exist'))
        return results
    results['Initializable.initializer()-present'] = True

    # Check if a init function lacks the initializer modifier
    initializer_modifier_missing = False
    all_init_functions = _get_initialize_functions(contract)
    for f in all_init_functions:
        if not initializer in f.modifiers:
            initializer_modifier_missing = True
            info = f'{f.canonical_name} does not call the initializer modifier'
            logger.info(red(info))
            res = Output(info)
            res.add(f)
            results['missing-initializer-modifier'].append(res.data)

    if not initializer_modifier_missing:
        logger.info(
            green('All the init functions have the initializer modifier'))

    # Check if we can determine the initialize function that will be called
    # TODO: handle MultipleInitTarget
    try:
        most_derived_init = _get_most_derived_init(contract)
    except MultipleInitTarget:
        logger.info(red('Too many init targets'))
        return results

    if most_derived_init is None:
        init_info = f'{contract.name} has no initialize function\n'
        logger.info(green(init_info))
        results['initialize_target'] = {}
        return results
    # results['initialize_target'] is set at the end, as we want to print it last

    # Check if an initialize function is not called from the most_derived_init function
    missing_call = False
    all_init_functions_called = _get_all_internal_calls(most_derived_init) + [
        most_derived_init
    ]
    missing_calls = [
        f for f in all_init_functions if not f in all_init_functions_called
    ]
    for f in missing_calls:
        info = f'Missing call to {f.canonical_name} in {most_derived_init.canonical_name}'
        logger.info(red(info))
        res = Output(info)
        res.add(f, {"is_most_derived_init_function": False})
        res.add(most_derived_init, {"is_most_derived_init_function": True})
        results['missing-calls'].append(res.data)
        missing_call = True
    if not missing_call:
        logger.info(green('No missing call to an init function found'))

    # Check if an init function is called multiple times
    double_calls = list(
        set([
            f for f in all_init_functions_called
            if all_init_functions_called.count(f) > 1
        ]))
    double_calls_found = False
    for f in double_calls:
        info = f'{f.canonical_name} is called multiple times in {most_derived_init.full_name}'
        logger.info(red(info))
        res = Output(info)
        res.add(f)
        results['multiple-calls'].append(res.data)
        double_calls_found = True
    if not double_calls_found:
        logger.info(green('No double call to init functions found'))

    # Print the initialize_target info

    init_info = f'{contract.name} needs to be initialized by {most_derived_init.full_name}\n'
    logger.info(
        green(
            'Check the deployement script to ensure that these functions are called:\n'
            + init_info))
    res = Output(init_info)
    res.add(most_derived_init)
    results['initialize_target'] = res.data

    if not error_found:
        logger.info(green('No error found'))

    return results
Пример #6
0
def constant_conformance_check(contract_v1, contract_v2):

    results = {
        "became_constants": [],
        "were_constants": [],
        "not_found_in_v2": [],
    }

    logger.info(
        green(
            '\n## Run variable constants conformance check... (see https://github.com/crytic/slither/wiki/Upgradeability-Checks)'
        ))
    error_found = False

    state_variables_v1 = contract_v1.state_variables
    state_variables_v2 = contract_v2.state_variables

    v2_additional_variables = len(state_variables_v2) - len(state_variables_v1)
    if v2_additional_variables < 0:
        v2_additional_variables = 0

    # We keep two index, because we need to have them out of sync if v2
    # has additional non constant variables
    idx_v1 = 0
    idx_v2 = 0
    while idx_v1 < len(state_variables_v1):

        state_v1 = contract_v1.state_variables[idx_v1]
        if len(state_variables_v2) <= idx_v2:
            break

        state_v2 = contract_v2.state_variables[idx_v2]

        if state_v2:
            if state_v1.is_constant:
                if not state_v2.is_constant:

                    # If v2 has additional non constant variables, we need to skip them
                    if (state_v1.name != state_v2.name or state_v1.type !=
                            state_v2.type) and v2_additional_variables > 0:
                        v2_additional_variables -= 1
                        idx_v2 += 1
                        continue

                    info = f'{state_v1.canonical_name} ({state_v1.source_mapping_str}) was constant and {state_v2.canonical_name} is not ({state_v2.source_mapping_str})'
                    logger.info(red(info))

                    res = Output(info)
                    res.add(state_v1)
                    res.add(state_v2)
                    results['were_constants'].append(res.data)
                    error_found = True

            elif state_v2.is_constant:
                info = f'{state_v1.canonical_name} ({state_v1.source_mapping_str}) was not constant but {state_v2.canonical_name} is ({state_v2.source_mapping_str})'
                logger.info(red(info))

                res = Output(info)
                res.add(state_v1)
                res.add(state_v2)
                results['became_constants'].append(res.data)
                error_found = True

        else:
            info = f'{state_v1.canonical_name} not found in {contract_v2.name}, not check was done'
            logger.info(yellow(info))

            res = Output(info)
            res.add(state_v1)
            res.add(contract_v2)
            results['not_found_in_v2'].append(res.data)

            error_found = True

        idx_v1 += 1
        idx_v2 += 1

    if not error_found:
        logger.info(green('No error found'))

    return results
Пример #7
0
def compare_function_ids(implem, proxy):

    results = {
        'function-id-collision':[],
        'shadowing':[],
    }
    
    logger.info(green('\n## Run function ids checks... (see https://github.com/crytic/slither/wiki/Upgradeability-Checks#functions-ids-checks)'))

    signatures_implem = get_signatures(implem)
    signatures_proxy = get_signatures(proxy)

    signatures_ids_implem = {get_function_id(s): s for s in signatures_implem}
    signatures_ids_proxy = {get_function_id(s): s for s in signatures_proxy}

    error_found = False
    for (k, _) in signatures_ids_implem.items():
        if k in signatures_ids_proxy:
            error_found = True
            if signatures_ids_implem[k] != signatures_ids_proxy[k]:

                implem_function = _get_function_or_variable(implem, signatures_ids_implem[k])
                proxy_function = _get_function_or_variable(proxy, signatures_ids_proxy[k])

                info = f'Function id collision found: {implem_function.canonical_name} ({implem_function.source_mapping_str}) {proxy_function.canonical_name} ({proxy_function.source_mapping_str})'
                logger.info(red(info))
                res = Output(info)
                res.add(implem_function)
                res.add(proxy_function)
                results['function-id-collision'].append(res.data)
                
            else:

                implem_function = _get_function_or_variable(implem, signatures_ids_implem[k])
                proxy_function = _get_function_or_variable(proxy, signatures_ids_proxy[k])

                info = f'Shadowing between {implem_function.canonical_name} ({implem_function.source_mapping_str}) and {proxy_function.canonical_name} ({proxy_function.source_mapping_str})'
                logger.info(red(info))

                res = Output(info)
                res.add(implem_function)
                res.add(proxy_function)
                results['shadowing'].append(res.data)

    if not error_found:
        logger.info(green('No error found'))

    return results
Пример #8
0
def compare_variables_order(contract1, contract2, missing_variable_check=True):

    results = {
        'missing_variables': [],
        'different-variables': [],
        'extra-variables': []
    }

    logger.info(
        green(
            f'\n## Run variables ordering checks between {contract1.name} and {contract2.name}... (see https://github.com/crytic/slither/wiki/Upgradeability-Checks#variables-order-checks)'
        ))

    order1 = [
        variable for variable in contract1.state_variables
        if not variable.is_constant
    ]
    order2 = [
        variable for variable in contract2.state_variables
        if not variable.is_constant
    ]

    error_found = False
    idx = 0
    for idx in range(0, len(order1)):
        variable1 = order1[idx]
        if len(order2) <= idx:
            if missing_variable_check:
                info = f'Variable only in {contract1.name}: {variable1.name} ({variable1.source_mapping_str})'
                logger.info(yellow(info))

                res = Output(info)
                res.add(variable1)
                results['missing_variables'].append(res.data)

                error_found = True
            continue

        variable2 = order2[idx]

        if (variable1.name != variable2.name) or (variable1.type !=
                                                  variable2.type):
            info = f'Different variables between {contract1.name} and {contract2.name}:\n'
            info += f'\t Variable {idx} in {contract1.name}: {variable1.name} {variable1.type} ({variable1.source_mapping_str})\n'
            info += f'\t Variable {idx} in {contract2.name}: {variable2.name} {variable2.type} ({variable2.source_mapping_str})\n'
            logger.info(red(info))

            res = Output(info, additional_fields={'index': idx})
            res.add(variable1)
            res.add(variable2)
            results['different-variables'].append(res.data)

            error_found = True

    idx = idx + 1

    while idx < len(order2):
        variable2 = order2[idx]

        info = f'Extra variables in {contract2.name}: {variable2.name} ({variable2.source_mapping_str})\n'
        logger.info(yellow(info))
        res = Output(info, additional_fields={'index': idx})
        res.add(variable2)
        results['extra-variables'].append(res.data)
        idx = idx + 1

    if not error_found:
        logger.info(green('No error found'))

    return results