Beispiel #1
0
    def _mutate(self):

        result = dict()

        for contract in self.fortress.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.fortress.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 _explore_structures_declaration(fortress, structures, result, target,
                                    convert):
    for st in structures:
        # Explore the variable declared within the structure (VariableStructure)
        _explore_variables_declaration(fortress, st.elems.values(), result,
                                       target, convert)

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

            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 = fortress.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)
def _explore_contract(fortress, contract, result, target, convert):
    _explore_variables_declaration(fortress, contract.state_variables, result,
                                   target, convert)
    _explore_structures_declaration(fortress, contract.structures, result,
                                    target, convert)
    _explore_functions(fortress, contract.functions_and_modifiers, result,
                       target, convert)
    _explore_enums(fortress, 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 = fortress.source_code[filename_source_code].encode(
            "utf8")[full_txt_start:full_txt_end]

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

        # 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)
def _explore_functions(fortress, functions, result, target, convert):
    for function in functions:
        _explore_variables_declaration(fortress, function.variables, result,
                                       target, convert, True)
        _explore_irs(fortress, 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, fortress)

            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 = fortress.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)
Beispiel #5
0
def _patch(fortress, result, in_file, modify_loc_start, modify_loc_end):
    in_file_str = fortress.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",
        )
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.fortress.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,
    )
def _patch(fortress, result, in_file, pragma, modify_loc_start,
           modify_loc_end):  # pylint: disable=too-many-arguments
    in_file_str = fortress.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(fortress, result, in_file, solc_version, modify_loc_start,
           modify_loc_end):
    in_file_str = fortress.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(fortress, result, in_file, modify_loc_start):
    in_file_str = fortress.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,
        "",
    )
def _explore_events_declaration(fortress, events, result, target, convert):
    for event in events:
        # Explore the parameters
        _explore_variables_declaration(fortress, 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, fortress)

            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)
def _patch(fortress, result, in_file, modify_loc_start, modify_loc_end):
    in_file_str = fortress.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!"
        )
def _explore_irs(fortress, 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 = fortress.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, fortress)

                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,
                    )
def _explore_enums(fortress, enums, result, target, convert):
    for enum in enums:
        if enum == target:
            old_str = enum.name
            new_str = convert(old_str, fortress)

            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 = fortress.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)
Beispiel #14
0
def _patch(  # pylint: disable=too-many-arguments
    fortress, result, in_file, match_text, replace_text, modify_loc_start, modify_loc_end
):
    in_file_str = fortress.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 _explore_variables_declaration(  # pylint: disable=too-many-arguments,too-many-locals,too-many-nested-blocks
        fortress,
        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 = fortress.source_code[filename_source_code].encode(
            "utf8")[full_txt_start:full_txt_end]

        _explore_type(
            fortress,
            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, fortress)

            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 = fortress.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)
def _explore_type(  # pylint: disable=too-many-arguments,too-many-locals,too-many-branches
        fortress, 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, fortress)

                loc_start = start
                if _is_var_declaration(fortress, 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, fortress)

                loc_start = start
                if _is_var_declaration(fortress, 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(fortress,
                                           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 = fortress.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, fortress)

                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, fortress)

                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(
                    fortress,
                    result,
                    target,
                    convert,
                    custom_type.type_to,
                    filename_source_code,
                    loc_start,
                    loc_end,
                )