Ejemplo n.º 1
0
def all_declarations_public(source: str) -> str:
    """
    Using a regex it makes public all the variable inside source
    :param source:
    :param ast:
    :return: edited_source: str
    """

    ast = asthelper.compile_from_source(source)

    nodes = asthelper.find_all_nodes(ast, {
        'nodeType': 'VariableDeclaration',
        'stateVariable': True
    })
    destination = source

    declaration_re = re.compile(
        r"(address|string|bool|bytes?\d*|u?int\d*|mapping.*\))(\[\d*\]|)?\s*(public|private|internal|external|)?\s+(\w+)\s*(;|=)"
    )

    for n in nodes:
        start, length, _ = n['src'].split(':')
        start = int(start)
        length = int(length)
        line = source[start:start + length + 1]
        #    print("before: ", line)
        left, right = tuple(destination.split(line, 1))
        middle = declaration_re.sub(r"\g<1>\g<2> public \g<4> \g<5>", line)
        destination = left + middle + right
    #    print("after: ", middle)

    return destination
    def before_test(self, source):
        ast_json = asthelper.compile_from_source(source)
        function_name = 'testFunction'

        function_node = asthelper.find_node(ast_json, {
            'nodeType': 'FunctionDefinition',
            'name': function_name
        })
        function_body = function_node['body']
        function_statements = function_body['statements']

        # Enumerate all the stataments with a require
        require_nodes = [
            i for (i, x) in enumerate(function_statements)
            if asthelper.find_node(x, {'name': 'require'})
        ]

        if not require_nodes:
            raise Exception("No requires")

        # stataments_under_inspection = All the stataments of the function until the last require (included)
        statements_under_inspection = function_statements[:require_nodes[-1] +
                                                          1]

        # Remove event emit statements
        # Remove banned statements
        events_definition_nodes = asthelper.find_all_nodes(
            ast_json, {'nodeType': 'EventDefinition'})

        return events_definition_nodes, statements_under_inspection
Ejemplo n.º 3
0
def inject_getter_functions(source: str):
    """
    Create getters for array's length member
    :param source: original source code
    :return: edited source code
    """
    ast = asthelper.compile_from_source(source)

    state_variables = \
        [x for x in asthelper.find_all_nodes(ast, {'nodeType': 'VariableDeclaration', 'stateVariable': True})]

    state_variables_list = [
        x for x in state_variables
        if 't_array' in x['typeDescriptions']['typeIdentifier']
    ]
    state_variables_names = [x['name'] for x in state_variables_list]

    edited_source = source
    for state_variable in state_variables_names:
        edited_source = _inject_getter_function(edited_source, state_variable)

    return edited_source
Ejemplo n.º 4
0
def convert_reassignment(source: str) -> str:
    regex = r"^[\s|\t]*([^\s]+)\s+(\+|\-|\*)?=\s+([^;]*)"
    ast = asthelper.compile_from_source(source)
    assigment_nodes = asthelper.find_all_nodes(ast,
                                               {'nodeType': '^Assignment$'})

    edited_source = source

    for node in assigment_nodes:
        start, length, _ = node['src'].split(':')
        start = int(start)
        length = int(length)
        line = source[start:start + length + 1]
        line_rgx = r'^[\s|\t]*' + re.escape(line)
        pieces = re.compile(line_rgx, re.M).split(edited_source, 1)

        if len(pieces) != 2:
            # not exact "LINE" match found
            continue

        left, right = tuple(pieces)
        name = 'var nvar_{1}'.format(node['typeDescriptions']['typeString'],
                                     node['id'])

        new_line = line
        matches = re.search(regex, line, re.M)
        if matches and matches.group(2) is None:
            subst = name + ' = \g<3>'
            new_line = re.sub(regex, subst, new_line, re.M)
        elif matches and matches.group(2):
            subst = name + ' = \g<1> \g<2> \g<3>'
            new_line = re.sub(regex, subst, new_line, re.M)

        edited_source = left + new_line + right

    return edited_source
Ejemplo n.º 5
0
def visit_ast(ast_json: list, function_name: str) -> dict:
	"""
	Given the AST of the complete solidity source file and
	the function name of the function under test, visit_ast
	checks if the function has a list of valid statements
	which precede the last require statements (using the
	sub-function filter_statements) and collect information
	for later analysis such as the candidate variables
	for an overflow, variable declarations, accessed state
	variables and the require expressions.

	:param ast_json: ast of the file generated using solc
	:param function_name: str, function name under test
	:return: inspected_parameters: dict
	"""
	function_nodes = asthelper.find_all_nodes(ast_json, {'nodeType': 'FunctionDefinition', 'name': "^" + function_name + "$"})
	function_nodes_with_body = [x for x in function_nodes if x['body'] and x['body']['statements']]

	if not function_nodes_with_body:
		raise Exception("AstVisitErr", "No function definition")

	function_node = function_nodes_with_body[0]

	function_body = function_node['body']
	function_statements = function_body['statements']

	# ====
	# ==== EXTRACT STATEMENTS AND CHECKS VALIDITY
	# ====

	# Enumerate all the stataments with a require
	require_nodes = [i for (i, x) in enumerate(function_statements) if asthelper.find_node(x, {'name': '^require$'})]

	statements_under_inspection = function_statements

	# Remove event emit statements
	# Remove banned statements
	events_definition_nodes = asthelper.find_all_nodes(ast_json, {'nodeType': '^EventDefinition$'})

	filtered_statements = filter_statements(events_definition_nodes, statements_under_inspection)

	if not filtered_statements:
		raise Exception("AstVisitErr", "Not handled statements")

	# ====
	# ==== LOCAL VARIABLE DECLARATION
	# ====
	local_variables = [x for x in filtered_statements if x['nodeType'] == 'VariableDeclarationStatement']

	# remove local variable decleration without initial values
	local_variables = list(filter(lambda a: 'initialValue' in a and a['initialValue'], local_variables))

	# ====
	# ==== REQUIRES
	# ====

	"""
	Description: Test the function _add_require_condition_with_overflow

	Each require contains an expression, this expression, which is then used
	to evaluate a boolean formula can lead to an overflow. So it is important
	to extract from the expression which contains the candidate to overflow value
	an implicit symbolic variable that is then added as a normal overflow candidate.
	"""

	expression_candidates = []
	expressions_map = []
	requires_nodes = [x for x in filtered_statements if asthelper.find_node(x, {'name': '^require$'})]
	for require_node in requires_nodes:
		require_exp = expressionhelper.Expression(require_node['expression']['arguments'][0])
		candidate_check_results = check_for_overflow_candidate(require_exp)
		if candidate_check_results:
			candidates_exp = [x[0] for x in candidate_check_results]
			expression_map = [x[1] for x in candidate_check_results]
			expression_candidates += candidates_exp
			expressions_map += expression_map


	# ====
	# ==== CANDIDATE FOR OVERFLOW
	# ====
	# Check if there is a at least one candidate for overflow

	local_variables_candidates = []
	for lv in local_variables:
		name = lv['declarations'][0]['name']
		lv_exp = expressionhelper.Expression(lv['initialValue'])
		lv_exp.dic['name'] = name # set a name to the expression
		candidate_check_results = check_for_overflow_candidate(lv_exp)
		if candidate_check_results:
			candidate_exp, expression_map = candidate_check_results[0]
			local_variables_candidates.append(candidate_exp)

	variables_candidate_for_overflow = local_variables_candidates + expression_candidates

	if not variables_candidate_for_overflow:
		raise Exception("AstVisitErr", "No variable is a good candidate for overflow ")

	# ====
	# ==== ACCESSED STATE VARIABLES
	# ====
	# Enumerate all the state variables
	state_variables_declaration = \
		[x for x in asthelper.find_all_nodes(ast_json, {'nodeType': 'VariableDeclaration', 'stateVariable': True})]

	# Find the accessed variables
	# No repetition of variable accessed
	# multiple times
	accessed_state_variables = []
	for state_variable_declaration in state_variables_declaration:
		state_id = state_variable_declaration['id']
		for statement in filtered_statements:
			access = asthelper.find_node(statement, {'nodeType': 'Identifier', 'referencedDeclaration': state_id})
			if access:
				accessed_state_variables.append(state_variable_declaration)
				break

	# ====
	# ==== FORMAL PARAMETERS
	# ====
	# List the input parameters of the function under test
	formal_parameters = function_node['parameters']['parameters']

	map_id_variable_name = {}
	local_variables_tmp = [x['declarations'][0] for x in local_variables ]

	for v in local_variables_tmp + formal_parameters + accessed_state_variables:
		map_id_variable_name[str(v['id'])] = v['name']

	# append 'this' identifier
	this_identifiers = asthelper.find_all_nodes(ast_json, {'nodeType': 'Identifier', 'name': 'this'})

	for this_id in this_identifiers:
		map_id_variable_name[str(this_id['id'])] = this_id['name']
		map_id_variable_name[str(this_id['referencedDeclaration'])] = this_id['name']

	return {
		'formal_parameters': formal_parameters,
		'local_variables': local_variables,
		'require_nodes': requires_nodes,
		'require_expression_map': expressions_map,
		'candidate_for_overflow': variables_candidate_for_overflow,
		'accessed_state_variables': accessed_state_variables,
		'map_id_variable_name': map_id_variable_name
	}
Ejemplo n.º 6
0
def ifrevert2require(source: str) -> str:
    """
    Conver the assigment to newly created declarations
    :param source:
    :return:
    """
    ast = asthelper.compile_from_source(source)
    if_nodes = asthelper.find_all_nodes(ast, {'nodeType': '^IfStatement$'})

    edited_source = source

    for node in if_nodes:

        try:
            reverts = asthelper.find_node(node['trueBody'], {
                'nodeType': '^Identifier$',
                'name': '^revert$'
            })

            # Check for return false
            return_false = False
            if 'statements' in node['trueBody']:
                if_statements = node['trueBody']['statements']
                if len(if_statements) == 1:
                    return_statement = asthelper.find_node(
                        node['trueBody'], {'nodeType': '^Return$'})
                    if return_statement and asthelper.find_node(
                            node['trueBody'], {
                                'nodeType': '^Literal$',
                                'value': '^false$'
                            }):
                        return_false = True
            elif 'expression' in node['trueBody']:
                return_statement = asthelper.find_node(
                    node['trueBody'], {'nodeType': '^Return$'})
                if return_statement and asthelper.find_node(
                        node['trueBody'], {
                            'nodeType': '^Literal$',
                            'value': '^false$'
                        }):
                    return_false = True

            if reverts or return_false:
                start, length, _ = node['src'].split(':')
                start = int(start)
                length = int(length)
                line = source[start:start + length + 1]
                left, right = tuple(edited_source.split(line, 1))

                # other way
                # new_line = line.replace('if', 'require')
                # sa = new_line.split(')')
                # new_line = ')'.join(sa[:-2]) # remove the portion after the last if closed bracket

                if_condition = asthelper.astnode.AstNode(
                    None, node['condition']).to_sol_string()
                require_statement = 'require(({}) == false);\n'.format(
                    if_condition)
                edited_source = left + require_statement + right
        except Exception as e:
            # an error while editing some if, maybe contains a function
            # just skip
            continue

    return edited_source