diff --git a/requirements.txt b/requirements.txt index 8ba3132d..22729266 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,3 +19,5 @@ eth-tester>=0.1.0b21 coverage jinja2 pytest +mock +pytest_mock diff --git a/tests/analysis/__init__.py b/tests/analysis/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/analysis/delegatecall_test.py b/tests/analysis/delegatecall_test.py new file mode 100644 index 00000000..8a4d4ad5 --- /dev/null +++ b/tests/analysis/delegatecall_test.py @@ -0,0 +1,113 @@ +from mythril.analysis.modules.delegatecall import execute, _concrete_call, _symbolic_call +from mythril.analysis.ops import Call, Variable, VarType +from mythril.analysis.symbolic import SymExecWrapper +from laser.ethereum.svm import GlobalState, Node, Environment, Account +import pytest +import mock +from mock import patch +import pytest_mock + +def test_concrete_call(): + # arrange + address = "0x10" + state = GlobalState(None, None) + node = Node("example") + to = Variable(1, VarType.CONCRETE) + meminstart = Variable(1, VarType.CONCRETE) + + call = Call(node, state, None, None, to, None) + + state.mstate.memory = ["placeholder", "calldata_bling_0"] + + node.contract_name = "the contract name" + node.function_name = "the function name" + + # act + issues = _concrete_call(call, state, address, meminstart) + + # assert + issue = issues[0] + assert issue.address == address + assert issue.contract == node.contract_name + assert issue.function == node.function_name + assert issue.title == "Call data forwarded with delegatecall()" + assert issue.type == 'Informational' + assert issue.description == "This contract forwards its call data via DELEGATECALL in its fallback function." \ + " This means that any function in the called contract can be executed." \ + " Note that the callee contract will have access to the storage of the " \ + "calling contract.\n DELEGATECALL target: 0x1" + + +def test_symbolic_call(mocker): + # arrange + address = "0x10" + + active_account = Account(address) + environment = Environment(active_account, None, None, None, None, None) + state = GlobalState(None, environment) + state.mstate.memory = ["placeholder", "calldata_bling_0"] + + + node = Node("example") + node.contract_name = "the contract name" + node.function_name = "the function name" + + to = Variable("storage_1", VarType.SYMBOLIC) + call = Call(node, state, None, "Type: ", to, None) + + + mocker.patch.object(SymExecWrapper, "__init__", lambda x, y: None) + statespace = SymExecWrapper(1) + + mocker.patch.object(statespace, 'find_storage_write') + statespace.find_storage_write.return_value = "Function name" + + + # act + issues = _symbolic_call(call, state, address, statespace) + + # assert + issue = issues[0] + assert issue.address == address + assert issue.contract == node.contract_name + assert issue.function == node.function_name + assert issue.title == 'Type: to a user-supplied address' + assert issue.type == 'Informational' + assert issue.description == 'This contract delegates execution to a contract address in storage slot 1.' \ + ' This storage slot can be written to by calling the function `Function name`. ' \ + 'Be aware that the called contract gets unrestricted access to this contract\'s state.' + + +@patch('laser.ethereum.svm.GlobalState.get_current_instruction') +@patch('mythril.analysis.modules.delegatecall._concrete_call') +@patch('mythril.analysis.modules.delegatecall._symbolic_call') +def test_delegate_call(sym_mock, concrete_mock, curr_instruction): + # arrange + # sym_mock = mocker.patch.object(delegatecall, "_symbolic_call") + # concrete_mock = mocker.patch.object(delegatecall, "_concrete_call") + sym_mock.return_value = [] + concrete_mock.return_value = [] + curr_instruction.return_value = {'address': '0x10'} + + active_account = Account('0x10') + environment = Environment(active_account, None, None, None, None, None) + state = GlobalState(None, environment) + state.mstate.memory = ["placeholder", "calldata_bling_0"] + state.mstate.stack = [1, 2, 3] + assert state.get_current_instruction() == {'address': '0x10'} + + node = Node("example") + node.contract_name = "the contract name" + node.function_name = "fallback" + + to = Variable("storage_1", VarType.SYMBOLIC) + call = Call(node, state, None, "DELEGATECALL", to, None) + + statespace = mock.MagicMock() + statespace.calls = [call] + + # act + issues = execute(statespace) + + # assert + assert concrete_mock.call_count == 1