def _patch(  # pylint: disable=too-many-arguments
    compilation_unit: SlitherCompilationUnit,
    result,
    in_file,
    match_text,
    replace_text,
    modify_loc_start,
    modify_loc_end,
):
    in_file_str = compilation_unit.core.source_code[in_file].encode("utf8")
    old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end]
    # Add keyword `constant` before the variable name
    (new_str_of_interest,
     num_repl) = re.subn(match_text, replace_text,
                         old_str_of_interest.decode("utf-8"), 1)
    if num_repl != 0:
        create_patch(
            result,
            in_file,
            modify_loc_start,
            modify_loc_end,
            old_str_of_interest,
            new_str_of_interest,
        )

    else:
        raise FormatError("State variable not found?!")
def remove_assignement(variable: Variable, contract: Contract, result: Dict):
    """
    Remove the variable's initial assignement

    :param variable:
    :param contract:
    :param result:
    :return:
    """
    # Retrieve the file
    in_file = contract.source_mapping["filename_absolute"]
    # Retrieve the source code
    in_file_str = contract.compilation_unit.core.source_code[in_file]

    # Get the string
    start = variable.source_mapping["start"]
    stop = variable.expression.source_mapping["start"]
    old_str = in_file_str[start:stop]

    new_str = old_str[:old_str.find("=")]

    create_patch(
        result,
        in_file,
        start,
        stop + variable.expression.source_mapping["length"],
        old_str,
        new_str,
    )
Exemple #3
0
def _explore_contract(slither, contract, result, target, convert):
    _explore_variables_declaration(slither, contract.state_variables, result, target, convert)
    _explore_structures_declaration(slither, contract.structures, result, target, convert)
    _explore_functions(slither, contract.functions_and_modifiers, result, target, convert)
    _explore_enums(slither, contract.enums, result, target, convert)

    if contract == target:
        filename_source_code = contract.source_mapping["filename_absolute"]
        full_txt_start = contract.source_mapping["start"]
        full_txt_end = full_txt_start + contract.source_mapping["length"]
        full_txt = slither.source_code[filename_source_code].encode("utf8")[
            full_txt_start:full_txt_end
        ]

        old_str = contract.name
        new_str = convert(old_str, slither)

        # The name is after the space
        matches = re.finditer(b"contract[ ]*", full_txt)
        # Look for the end offset of the largest list of ' '
        loc_start = full_txt_start + max(matches, key=lambda x: len(x.group())).end()

        loc_end = loc_start + len(old_str)

        create_patch(result, filename_source_code, loc_start, loc_end, old_str, new_str)
Exemple #4
0
    def _mutate(self):

        result = dict()

        for contract in self.slither.contracts:

            for function in contract.functions_declared + contract.modifiers_declared:

                for node in function.nodes:
                    if node.type == NodeType.IF:
                        # Retrieve the file
                        in_file = contract.source_mapping["filename_absolute"]
                        # Retrieve the source code
                        in_file_str = contract.slither.source_code[in_file]

                        # Get the string
                        start = node.source_mapping["start"]
                        stop = start + node.source_mapping["length"]
                        old_str = in_file_str[start:stop]

                        # Replace the expression with true
                        new_str = "true"

                        create_patch(result, in_file, start, stop, old_str, new_str)

        return result
def _patch(slither, result, in_file, modify_loc_start, modify_loc_end):
    in_file_str = slither.source_code[in_file].encode('utf8')
    old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end]
    # Search for 'public' keyword which is in-between the function name and modifier name (if present)
    # regex: 'public' could have spaces around or be at the end of the line
    m = re.search(r'((\spublic)\s+)|(\spublic)$|(\)public)$',
                  old_str_of_interest.decode('utf-8'))
    if m is None:
        # No visibility specifier exists; public by default.
        create_patch(
            result,
            in_file,
            # start after the function definition's closing paranthesis
            modify_loc_start +
            len(old_str_of_interest.decode('utf-8').split(')')[0]) + 1,
            # end is same as start because we insert the keyword `external` at that location
            modify_loc_start +
            len(old_str_of_interest.decode('utf-8').split(')')[0]) + 1,
            "",
            " external")  # replace_text is `external`
    else:
        create_patch(
            result,
            in_file,
            # start at the keyword `public`
            modify_loc_start + m.span()[0] + 1,
            # end after the keyword `public` = start + len('public'')
            modify_loc_start + m.span()[0] + 1 + len('public'),
            "public",
            "external")
Exemple #6
0
def _explore_functions(slither, functions, result, target, convert):
    for function in functions:
        _explore_variables_declaration(slither, function.variables, result, target, convert, True)
        _explore_irs(slither, function.all_slithir_operations(), result, target, convert)

        if isinstance(target, Function) and function.canonical_name == target.canonical_name:
            old_str = function.name
            new_str = convert(old_str, slither)

            filename_source_code = function.source_mapping["filename_absolute"]
            full_txt_start = function.source_mapping["start"]
            full_txt_end = full_txt_start + function.source_mapping["length"]
            full_txt = slither.source_code[filename_source_code].encode("utf8")[
                full_txt_start:full_txt_end
            ]

            # The name is after the space
            if isinstance(target, Modifier):
                matches = re.finditer(b"modifier([ ]*)", full_txt)
            else:
                matches = re.finditer(b"function([ ]*)", full_txt)
            # Look for the end offset of the largest list of ' '
            loc_start = full_txt_start + max(matches, key=lambda x: len(x.group())).end()
            loc_end = loc_start + len(old_str)

            create_patch(result, filename_source_code, loc_start, loc_end, old_str, new_str)
Exemple #7
0
def _explore_structures_declaration(slither, structures, result, target,
                                    convert):
    for st in structures:
        # Explore the variable declared within the structure (VariableStructure)
        _explore_variables_declaration(slither, st.elems.values(), result,
                                       target, convert)

        # If the structure is the target
        if st == target:
            old_str = st.name
            new_str = convert(old_str, slither)

            filename_source_code = st.source_mapping["filename_absolute"]
            full_txt_start = st.source_mapping["start"]
            full_txt_end = full_txt_start + st.source_mapping["length"]
            full_txt = slither.source_code[filename_source_code].encode(
                "utf8")[full_txt_start:full_txt_end]

            # The name is after the space
            matches = re.finditer(b"struct[ ]*", full_txt)
            # Look for the end offset of the largest list of ' '
            loc_start = full_txt_start + max(
                matches, key=lambda x: len(x.group())).end()
            loc_end = loc_start + len(old_str)

            create_patch(result, filename_source_code, loc_start, loc_end,
                         old_str, new_str)
Exemple #8
0
def _patch(slither, result, in_file, pragma, modify_loc_start, modify_loc_end):
    in_file_str = slither.source_code[in_file].encode('utf8')
    old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end]
    create_patch(result,
                 in_file,
                 int(modify_loc_start),
                 int(modify_loc_end),
                 old_str_of_interest,
                 pragma)
Exemple #9
0
def _patch(slither, result, in_file, pragma, modify_loc_start, modify_loc_end):  # pylint: disable=too-many-arguments
    in_file_str = slither.source_code[in_file].encode("utf8")
    old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end]
    create_patch(
        result,
        in_file,
        int(modify_loc_start),
        int(modify_loc_end),
        old_str_of_interest,
        pragma,
    )
def _patch(slither, result, in_file, solc_version, modify_loc_start,
           modify_loc_end):
    in_file_str = slither.source_code[in_file].encode("utf8")
    old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end]
    create_patch(
        result,
        in_file,
        int(modify_loc_start),
        int(modify_loc_end),
        old_str_of_interest,
        solc_version,
    )
def _patch(slither, result, in_file, match_text, replace_text,
           modify_loc_start, modify_loc_end):
    in_file_str = slither.source_code[in_file].encode('utf8')
    old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end]
    # Add keyword `constant` before the variable name
    (new_str_of_interest,
     num_repl) = re.subn(match_text, replace_text,
                         old_str_of_interest.decode('utf-8'), 1)
    if num_repl != 0:
        create_patch(result, in_file, modify_loc_start, modify_loc_end,
                     old_str_of_interest, new_str_of_interest)

    else:
        raise FormatError("State variable not found?!")
Exemple #12
0
def _patch(slither, result, in_file, modify_loc_start):
    in_file_str = slither.source_code[in_file].encode('utf8')
    old_str_of_interest = in_file_str[modify_loc_start:]
    old_str = old_str_of_interest.decode('utf-8').partition(';')[0]\
             + old_str_of_interest.decode('utf-8').partition(';')[1]

    create_patch(
        result,
        in_file,
        int(modify_loc_start),
        # Remove the entire declaration until the semicolon
        int(modify_loc_start +
            len(old_str_of_interest.decode('utf-8').partition(';')[0]) + 1),
        old_str,
        "")
Exemple #13
0
def _explore_events_declaration(slither, events, result, target, convert):
    for event in events:
        # Explore the parameters
        _explore_variables_declaration(slither, event.elems, result, target, convert)

        # If the event is the target
        if event == target:
            filename_source_code = event.source_mapping["filename_absolute"]

            old_str = event.name
            new_str = convert(old_str, slither)

            loc_start = event.source_mapping["start"]
            loc_end = loc_start + len(old_str)

            create_patch(result, filename_source_code, loc_start, loc_end, old_str, new_str)
Exemple #14
0
def _patch(slither, result, in_file, modify_loc_start, modify_loc_end):
    in_file_str = slither.source_code[in_file].encode('utf8')
    old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end]
    # Find the keywords view|pure|constant and remove them
    m = re.search("(view|pure|constant)", old_str_of_interest.decode('utf-8'))
    if m:
        create_patch(
            result,
            in_file,
            modify_loc_start + m.span()[0],
            modify_loc_start + m.span()[1],
            m.groups(0)[0],  # this is view|pure|constant
            "")
    else:
        raise FormatError(
            "No view/pure/constant specifier exists. Regex failed to remove specifier!"
        )
Exemple #15
0
def _patch(compilation_unit: SlitherCompilationUnit, result, in_file,
           modify_loc_start):
    in_file_str = compilation_unit.core.source_code[in_file].encode("utf8")
    old_str_of_interest = in_file_str[modify_loc_start:]
    old_str = (old_str_of_interest.decode("utf-8").partition(";")[0] +
               old_str_of_interest.decode("utf-8").partition(";")[1])

    create_patch(
        result,
        in_file,
        int(modify_loc_start),
        # Remove the entire declaration until the semicolon
        int(modify_loc_start +
            len(old_str_of_interest.decode("utf-8").partition(";")[0]) + 1),
        old_str,
        "",
    )
Exemple #16
0
def _explore_irs(slither, irs, result, target, convert):
    # pylint: disable=too-many-locals
    if irs is None:
        return
    for ir in irs:
        for v in get_ir_variables(ir):
            if target == v or (
                isinstance(target, Function)
                and isinstance(v, Function)
                and v.canonical_name == target.canonical_name
            ):
                source_mapping = ir.expression.source_mapping
                filename_source_code = source_mapping["filename_absolute"]
                full_txt_start = source_mapping["start"]
                full_txt_end = full_txt_start + source_mapping["length"]
                full_txt = slither.source_code[filename_source_code].encode("utf8")[
                    full_txt_start:full_txt_end
                ]

                if not target.name.encode("utf8") in full_txt:
                    raise FormatError(f"{target} not found in {full_txt} ({source_mapping}")

                old_str = target.name.encode("utf8")
                new_str = convert(old_str, slither)

                counter = 0
                # Can be found multiple time on the same IR
                # We patch one by one
                while old_str in full_txt:
                    target_found_at = full_txt.find((old_str))

                    full_txt = full_txt[target_found_at + 1 :]
                    counter += target_found_at

                    loc_start = full_txt_start + counter
                    loc_end = loc_start + len(old_str)

                    create_patch(
                        result,
                        filename_source_code,
                        loc_start,
                        loc_end,
                        old_str,
                        new_str,
                    )
Exemple #17
0
def _explore_enums(slither, enums, result, target, convert):
    for enum in enums:
        if enum == target:
            old_str = enum.name
            new_str = convert(old_str, slither)

            filename_source_code = enum.source_mapping["filename_absolute"]
            full_txt_start = enum.source_mapping["start"]
            full_txt_end = full_txt_start + enum.source_mapping["length"]
            full_txt = slither.source_code[filename_source_code].encode("utf8")[
                full_txt_start:full_txt_end
            ]

            # The name is after the space
            matches = re.finditer(b"enum([ ]*)", full_txt)
            # Look for the end offset of the largest list of ' '
            loc_start = full_txt_start + max(matches, key=lambda x: len(x.group())).end()
            loc_end = loc_start + len(old_str)

            create_patch(result, filename_source_code, loc_start, loc_end, old_str, new_str)
Exemple #18
0
def _explore_type(  # pylint: disable=too-many-arguments,too-many-locals,too-many-branches
    slither, result, target, convert, custom_type, filename_source_code, start, end
):
    if isinstance(custom_type, UserDefinedType):
        # Patch type based on contract/enum
        if isinstance(custom_type.type, (Enum, Contract)):
            if custom_type.type == target:
                old_str = custom_type.type.name
                new_str = convert(old_str, slither)

                loc_start = start
                if _is_var_declaration(slither, filename_source_code, start):
                    loc_end = loc_start + len("var")
                else:
                    loc_end = loc_start + len(old_str)

                create_patch(result, filename_source_code, loc_start, loc_end, old_str, new_str)

        else:
            # Patch type based on structure
            assert isinstance(custom_type.type, Structure)
            if custom_type.type == target:
                old_str = custom_type.type.name
                new_str = convert(old_str, slither)

                loc_start = start
                if _is_var_declaration(slither, filename_source_code, start):
                    loc_end = loc_start + len("var")
                else:
                    loc_end = loc_start + len(old_str)

                create_patch(result, filename_source_code, loc_start, loc_end, old_str, new_str)

            # Structure contain a list of elements, that might need patching
            # .elems return a list of VariableStructure
            _explore_variables_declaration(
                slither, custom_type.type.elems.values(), result, target, convert
            )

    if isinstance(custom_type, MappingType):
        # Mapping has three steps:
        # Convert the "from" type
        # Convert the "to" type
        # Convert nested type in the "to"
        # Ex: mapping (mapping (badName => uint) => uint)

        # Do the comparison twice, so we can factor together the re matching
        # mapping can only have elementary type in type_from
        if isinstance(custom_type.type_to, (UserDefinedType, MappingType)) or target in [
            custom_type.type_from,
            custom_type.type_to,
        ]:

            full_txt_start = start
            full_txt_end = end
            full_txt = slither.source_code[filename_source_code].encode("utf8")[
                full_txt_start:full_txt_end
            ]
            re_match = re.match(RE_MAPPING, full_txt)
            assert re_match

            if custom_type.type_from == target:
                old_str = custom_type.type_from.name
                new_str = convert(old_str, slither)

                loc_start = start + re_match.start(1)
                loc_end = loc_start + len(old_str)

                create_patch(result, filename_source_code, loc_start, loc_end, old_str, new_str)

            if custom_type.type_to == target:
                old_str = custom_type.type_to.name
                new_str = convert(old_str, slither)

                loc_start = start + re_match.start(2)
                loc_end = loc_start + len(old_str)

                create_patch(result, filename_source_code, loc_start, loc_end, old_str, new_str)

            if isinstance(custom_type.type_to, (UserDefinedType, MappingType)):
                loc_start = start + re_match.start(2)
                loc_end = start + re_match.end(2)
                _explore_type(
                    slither,
                    result,
                    target,
                    convert,
                    custom_type.type_to,
                    filename_source_code,
                    loc_start,
                    loc_end,
                )
Exemple #19
0
def _explore_variables_declaration(  # pylint: disable=too-many-arguments,too-many-locals,too-many-nested-blocks
    slither, variables, result, target, convert, patch_comment=False
):
    for variable in variables:
        # First explore the type of the variable
        filename_source_code = variable.source_mapping["filename_absolute"]
        full_txt_start = variable.source_mapping["start"]
        full_txt_end = full_txt_start + variable.source_mapping["length"]
        full_txt = slither.source_code[filename_source_code].encode("utf8")[
            full_txt_start:full_txt_end
        ]

        _explore_type(
            slither,
            result,
            target,
            convert,
            variable.type,
            filename_source_code,
            full_txt_start,
            variable.source_mapping["start"] + variable.source_mapping["length"],
        )

        # If the variable is the target
        if variable == target:
            old_str = variable.name
            new_str = convert(old_str, slither)

            loc_start = full_txt_start + full_txt.find(old_str.encode("utf8"))
            loc_end = loc_start + len(old_str)

            create_patch(result, filename_source_code, loc_start, loc_end, old_str, new_str)

            # Patch comment only makes sense for local variable declaration in the parameter list
            if patch_comment and isinstance(variable, LocalVariable):
                if "lines" in variable.source_mapping and variable.source_mapping["lines"]:
                    func = variable.function
                    end_line = func.source_mapping["lines"][0]
                    if variable in func.parameters:
                        idx = len(func.parameters) - func.parameters.index(variable) + 1
                        first_line = end_line - idx - 2

                        potential_comments = slither.source_code[filename_source_code].encode(
                            "utf8"
                        )
                        potential_comments = potential_comments.splitlines(keepends=True)[
                            first_line : end_line - 1
                        ]

                        idx_beginning = func.source_mapping["start"]
                        idx_beginning += -func.source_mapping["starting_column"] + 1
                        idx_beginning += -sum([len(c) for c in potential_comments])

                        old_comment = f"@param {old_str}".encode("utf8")

                        for line in potential_comments:
                            idx = line.find(old_comment)
                            if idx >= 0:
                                loc_start = idx + idx_beginning
                                loc_end = loc_start + len(old_comment)
                                new_comment = f"@param {new_str}".encode("utf8")

                                create_patch(
                                    result,
                                    filename_source_code,
                                    loc_start,
                                    loc_end,
                                    old_comment,
                                    new_comment,
                                )

                                break
                            idx_beginning += len(line)