diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 38039960..7517c44a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,7 +10,7 @@ If you have a small question or aren't sure if you should create an issue for yo # Coding If you want to help out with the development of Mythril then you can take a look at our issues or [Waffle board](https://waffle.io/ConsenSys/mythril). -Before you start working on an issue pkease stop by on Discord to message a collaborator, this way we can assign you to the issue making sure nobody does double work. We can also provide you with support through Discord if there are any questions during the development process. +Before you start working on an issue please stop by on Discord to message a collaborator, this way we can assign you to the issue making sure nobody does double work. We can also provide you with support through Discord if there are any questions during the development process. ## New ideas Before you start working on a new idea, it's useful to create an issue on GitHub, that way we know what you want to implement and that you are working on it. Additionally, it might happen that your feature does not fit with our roadmap, in which case it would be unfortunate if you have already spent some time working on it. diff --git a/mythril/analysis/call_helpers.py b/mythril/analysis/call_helpers.py index 50285d3f..6cb796df 100644 --- a/mythril/analysis/call_helpers.py +++ b/mythril/analysis/call_helpers.py @@ -15,10 +15,9 @@ def get_call_from_state(state: GlobalState) -> Union[Call, None]: instruction = state.get_current_instruction() op = instruction["opcode"] - stack = state.mstate.stack - if op in ("CALL", "CALLCODE"): + if op in ("CALL", "CALLCODE", "STATICCALL"): gas, to, value, meminstart, meminsz, memoutstart, memoutsz = ( get_variable(stack[-1]), get_variable(stack[-2]), @@ -29,7 +28,7 @@ def get_call_from_state(state: GlobalState) -> Union[Call, None]: get_variable(stack[-7]), ) - if to.type == VarType.CONCRETE and to.val < 5: + if to.type == VarType.CONCRETE and 0 < to.val < 5: return None if meminstart.type == VarType.CONCRETE and meminsz.type == VarType.CONCRETE: diff --git a/mythril/analysis/modules/ether_thief.py b/mythril/analysis/modules/ether_thief.py index d382ca07..b0550918 100644 --- a/mythril/analysis/modules/ether_thief.py +++ b/mythril/analysis/modules/ether_thief.py @@ -97,8 +97,6 @@ class EtherThief(DetectionModule): transaction_sequence = solver.get_transaction_sequence(state, constraints) - debug = json.dumps(transaction_sequence, indent=4) - issue = Issue( contract=state.environment.active_account.contract_name, function_name=state.environment.active_function_name, @@ -111,7 +109,7 @@ class EtherThief(DetectionModule): description_tail="Arbitrary senders other than the contract creator can withdraw ETH from the contract" + " account without previously having sent an equivalent amount of ETH to it. This is likely to be" + " a vulnerability.", - debug=debug, + transaction_sequence=transaction_sequence, gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used), ) except UnsatError: diff --git a/mythril/analysis/modules/exceptions.py b/mythril/analysis/modules/exceptions.py index 8eaf6400..3e9dcfe3 100644 --- a/mythril/analysis/modules/exceptions.py +++ b/mythril/analysis/modules/exceptions.py @@ -36,7 +36,6 @@ def _analyze_state(state) -> list: transaction_sequence = solver.get_transaction_sequence( state, state.mstate.constraints ) - debug = json.dumps(transaction_sequence, indent=4) issue = Issue( contract=state.environment.active_account.contract_name, @@ -48,7 +47,7 @@ def _analyze_state(state) -> list: description_head="A reachable exception has been detected.", description_tail=description_tail, bytecode=state.environment.code.bytecode, - debug=debug, + transaction_sequence=transaction_sequence, gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used), ) return [issue] diff --git a/mythril/analysis/modules/external_calls.py b/mythril/analysis/modules/external_calls.py index 95eb428e..577553f2 100644 --- a/mythril/analysis/modules/external_calls.py +++ b/mythril/analysis/modules/external_calls.py @@ -36,6 +36,7 @@ def _analyze_state(state): try: constraints = copy(state.mstate.constraints) + transaction_sequence = solver.get_transaction_sequence( state, constraints + [UGT(gas, symbol_factory.BitVecVal(2300, 256))] ) @@ -46,7 +47,6 @@ def _analyze_state(state): constraints += [to == 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF] transaction_sequence = solver.get_transaction_sequence(state, constraints) - debug = json.dumps(transaction_sequence, indent=4) description_head = "A call to a user-supplied address is executed." description_tail = ( "The callee address of an external message call can be set by " @@ -65,7 +65,7 @@ def _analyze_state(state): severity="Medium", description_head=description_head, description_tail=description_tail, - debug=debug, + transaction_sequence=transaction_sequence, gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used), ) @@ -92,7 +92,7 @@ def _analyze_state(state): severity="Low", description_head=description_head, description_tail=description_tail, - debug=debug, + transaction_sequence=transaction_sequence, gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used), ) diff --git a/mythril/analysis/modules/integer.py b/mythril/analysis/modules/integer.py index 0c6582a0..28f9f8d5 100644 --- a/mythril/analysis/modules/integer.py +++ b/mythril/analysis/modules/integer.py @@ -306,10 +306,9 @@ class IntegerOverflowUnderflowModule(DetectionModule): description_head=self._get_description_head(annotation, _type), description_tail=self._get_description_tail(annotation, _type), gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used), + transaction_sequence=transaction_sequence, ) - issue.debug = json.dumps(transaction_sequence, indent=4) - if annotation.operator == "subtraction": self._underflow_cache[address] = True else: diff --git a/mythril/analysis/modules/state_change_external_calls.py b/mythril/analysis/modules/state_change_external_calls.py new file mode 100644 index 00000000..6a394009 --- /dev/null +++ b/mythril/analysis/modules/state_change_external_calls.py @@ -0,0 +1,172 @@ +from mythril.analysis.swc_data import REENTRANCY +from mythril.analysis.modules.base import DetectionModule +from mythril.analysis.report import Issue +from mythril.laser.smt import symbol_factory, UGT, BitVec, Or +from mythril.laser.ethereum.state.global_state import GlobalState +from mythril.laser.ethereum.state.annotation import StateAnnotation +from mythril.analysis import solver +from mythril.exceptions import UnsatError +from typing import List, cast, Optional +from copy import copy + +import logging + +log = logging.getLogger(__name__) + +DESCRIPTION = """ + +Check whether there is a state change of the contract after the execution of an external call +""" + + +class StateChangeCallsAnnotation(StateAnnotation): + def __init__(self, call_state: GlobalState, user_defined_address: bool) -> None: + self.call_state = call_state + self.state_change_states = [] # type: List[GlobalState] + self.user_defined_address = user_defined_address + + def __copy__(self): + new_annotation = StateChangeCallsAnnotation( + self.call_state, self.user_defined_address + ) + new_annotation.state_change_states = self.state_change_states[:] + return new_annotation + + def get_issue(self, global_state: GlobalState) -> Optional[Issue]: + if not self.state_change_states: + return None + + severity = "Medium" if self.user_defined_address else "Low" + address = global_state.get_current_instruction()["address"] + logging.debug( + "[EXTERNAL_CALLS] Detected state changes at addresses: {}".format(address) + ) + description_head = ( + "The contract account state is changed after an external call. " + ) + description_tail = ( + "Consider that the called contract could re-enter the function before this " + "state change takes place. This can lead to business logic vulnerabilities." + ) + + return Issue( + contract=global_state.environment.active_account.contract_name, + function_name=global_state.environment.active_function_name, + address=address, + title="State change after external call", + severity=severity, + description_head=description_head, + description_tail=description_tail, + swc_id=REENTRANCY, + bytecode=global_state.environment.code.bytecode, + ) + + +class StateChange(DetectionModule): + """This module searches for state change after low level calls (e.g. call.value()) that + forward gas to the callee.""" + + def __init__(self): + """""" + super().__init__( + name="State Change After External calls", + swc_id=REENTRANCY, + description=DESCRIPTION, + entrypoint="callback", + pre_hooks=[ + "CALL", + "SSTORE", + "DELEGATECALL", + "STATICCALL", + "CREATE", + "CREATE2", + "CALLCODE", + ], + ) + + def execute(self, state: GlobalState): + self._issues.extend(self._analyze_state(state)) + return self.issues + + @staticmethod + def _add_external_call(global_state: GlobalState) -> None: + gas = global_state.mstate.stack[-1] + to = global_state.mstate.stack[-2] + try: + constraints = copy(global_state.mstate.constraints) + solver.get_model( + constraints + + [ + UGT(gas, symbol_factory.BitVecVal(2300, 256)), + Or( + to > symbol_factory.BitVecVal(16, 256), + to == symbol_factory.BitVecVal(0, 256), + ), + ] + ) + + # Check whether we can also set the callee address + try: + constraints += [to == 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF] + solver.get_model(constraints) + + global_state.annotate(StateChangeCallsAnnotation(global_state, True)) + except UnsatError: + global_state.annotate(StateChangeCallsAnnotation(global_state, False)) + except UnsatError: + pass + + @staticmethod + def _analyze_state(global_state: GlobalState) -> List[Issue]: + + annotations = cast( + List[StateChangeCallsAnnotation], + list(global_state.get_annotations(StateChangeCallsAnnotation)), + ) + op_code = global_state.get_current_instruction()["opcode"] + + if len(annotations) == 0: + if op_code in ("SSTORE", "CREATE", "CREATE2"): + return [] + if op_code in ("SSTORE", "CREATE", "CREATE2"): + for annotation in annotations: + annotation.state_change_states.append(global_state) + + # Record state changes following from a transfer of ether + if op_code in ("CALL", "DELEGATECALL", "CALLCODE"): + value = global_state.mstate.stack[-3] # type: BitVec + if StateChange._balance_change(value, global_state): + for annotation in annotations: + annotation.state_change_states.append(global_state) + + # Record external calls + if op_code in ("CALL", "DELEGATECALL", "CALLCODE"): + StateChange._add_external_call(global_state) + + # Check for vulnerabilities + vulnerabilities = [] + for annotation in annotations: + if not annotation.state_change_states: + continue + vulnerabilities.append(annotation.get_issue(global_state)) + return vulnerabilities + + @staticmethod + def _balance_change(value: BitVec, global_state: GlobalState) -> bool: + if not value.symbolic: + assert value.value is not None + return value.value > 0 + + else: + constraints = copy(global_state.mstate.constraints) + + try: + solver.get_model( + constraints + [value > symbol_factory.BitVecVal(0, 256)] + ) + return True + except UnsatError: + return False + + +detector = StateChange() diff --git a/mythril/analysis/modules/suicide.py b/mythril/analysis/modules/suicide.py index 62bc3e00..b618babe 100644 --- a/mythril/analysis/modules/suicide.py +++ b/mythril/analysis/modules/suicide.py @@ -76,7 +76,6 @@ class SuicideModule(DetectionModule): ) description_tail = "Arbitrary senders can kill this contract." - debug = json.dumps(transaction_sequence, indent=4) self._cache_address[instruction["address"]] = True issue = Issue( @@ -89,7 +88,7 @@ class SuicideModule(DetectionModule): severity="High", description_head=description_head, description_tail=description_tail, - debug=debug, + transaction_sequence=transaction_sequence, gas_used=(state.mstate.min_gas_used, state.mstate.max_gas_used), ) return [issue] diff --git a/mythril/analysis/report.py b/mythril/analysis/report.py index e1f73887..98bafc0e 100644 --- a/mythril/analysis/report.py +++ b/mythril/analysis/report.py @@ -31,7 +31,7 @@ class Issue: severity=None, description_head="", description_tail="", - debug="", + transaction_sequence=None, ): """ @@ -55,7 +55,6 @@ class Issue: self.description_tail = description_tail self.description = "%s\n%s" % (description_head, description_tail) self.severity = severity - self.debug = debug self.swc_id = swc_id self.min_gas_used, self.max_gas_used = gas_used self.filename = None @@ -64,6 +63,24 @@ class Issue: self.source_mapping = None self.discovery_time = time() - StartTime().global_start_time self.bytecode_hash = get_code_hash(bytecode) + if transaction_sequence is None: + self.tx_sequence_users = None + self.transaction_sequence_jsonv2 = None + else: + self.tx_sequence_users = json.dumps(transaction_sequence, indent=4) + self.transaction_sequence_jsonv2 = self.add_block_data(transaction_sequence) + + @staticmethod + def add_block_data(transaction_sequence: Dict): + for step in transaction_sequence["steps"]: + step["gasLimit"] = "0x7d000" + step["gasPrice"] = "0x773594000" + step["blockCoinbase"] = "0xcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb" + step["blockDifficulty"] = "0xa7d7343662e26" + step["blockGasLimit"] = "0x7d0000" + step["blockNumber"] = "0x66e393" + step["blockTime"] = "0x5bfa4639" + return transaction_sequence @property def as_dict(self): @@ -79,7 +96,7 @@ class Issue: "function": self.function, "severity": self.severity, "address": self.address, - "debug": self.debug, + "tx_sequence": self.tx_sequence_users, "min_gas_used": self.min_gas_used, "max_gas_used": self.max_gas_used, "sourceMap": self.source_mapping, @@ -168,6 +185,7 @@ class Report: """ name = self._file_name() template = Report.environment.get_template("report_as_text.jinja2") + return template.render( filename=name, issues=self.sorted_issues(), verbose=self.verbose ) @@ -203,7 +221,9 @@ class Report: title = SWC_TO_TITLE[issue.swc_id] except KeyError: title = "Unspecified Security Issue" - + extra = {"discoveryTime": int(issue.discovery_time * 10 ** 9)} + if issue.transaction_sequence_jsonv2: + extra["testCase"] = str(issue.transaction_sequence_jsonv2) _issues.append( { "swcID": "SWC-" + issue.swc_id, @@ -214,7 +234,7 @@ class Report: }, "severity": issue.severity, "locations": [{"sourceMap": "%d:1:%d" % (issue.address, idx)}], - "extra": {"discoveryTime": int(issue.discovery_time * 10 ** 9)}, + "extra": extra, } ) meta_data = self._get_exception_data() diff --git a/mythril/analysis/solver.py b/mythril/analysis/solver.py index 34b751ce..47dcb0b0 100644 --- a/mythril/analysis/solver.py +++ b/mythril/analysis/solver.py @@ -1,9 +1,11 @@ """This module contains analysis module helpers to solve path constraints.""" from typing import Dict, List from z3 import sat, unknown, FuncInterp +from copy import copy import z3 from mythril.laser.ethereum.state.global_state import GlobalState +from mythril.laser.ethereum.state.constraints import Constraints from mythril.laser.ethereum.transaction import BaseTransaction from mythril.laser.smt import simplify, UGE, Optimize, symbol_factory from mythril.laser.ethereum.time_handler import time_handler @@ -77,7 +79,9 @@ def pretty_print_model(model): return ret -def get_transaction_sequence(global_state: GlobalState, constraints) -> Dict: +def get_transaction_sequence( + global_state: GlobalState, constraints: Constraints +) -> Dict: """Generate concrete transaction sequence. :param global_state: GlobalState to generate transaction sequence for @@ -86,18 +90,11 @@ def get_transaction_sequence(global_state: GlobalState, constraints) -> Dict: transaction_sequence = global_state.world_state.transaction_sequence - # gaslimit & gasprice don't exist yet tx_template = { "origin": None, "value": None, "address": None, "input": None, - "gasLimit": "", - "blockCoinbase": "", - "blockDifficulty": "", - "blockGasLimit": "", - "blockNumber": "", - "blockTime": "", } # type: Dict[str, str] concrete_transactions = [] @@ -123,7 +120,7 @@ def get_transaction_sequence(global_state: GlobalState, constraints) -> Dict: creation_tx_ids.append(tx_id) model = get_model(tx_constraints, minimize=minimize) - + min_price_dict = {} # type: Dict[str, int] for transaction in transactions: tx_id = str(transaction.id) concrete_transaction = tx_template.copy() @@ -133,16 +130,20 @@ def get_transaction_sequence(global_state: GlobalState, constraints) -> Dict: for b in transaction.call_data.concrete(model) ] ) - - concrete_transaction["value"] = ( - "0x%x" - % model.eval(transaction.call_value.raw, model_completion=True).as_long() - ) - concrete_transaction["origin"] = "0x" + ( + value = model.eval(transaction.call_value.raw, model_completion=True).as_long() + concrete_transaction["value"] = "0x%x" % value + origin = "0x" + ( "%x" % model.eval(transaction.caller.raw, model_completion=True).as_long() ).zfill(40) + concrete_transaction["origin"] = origin concrete_transaction["address"] = "%s" % transaction.callee_account.address concrete_transactions.append(concrete_transaction) + min_price_dict[origin] = min_price_dict.get(origin, 0) + value + + initial_state = copy(global_state.world_state.initial_state_account) + + for account, data in initial_state["accounts"].items(): + data["balance"] = min_price_dict.get(account, 0) steps = { "initialState": global_state.world_state.initial_state_account, "steps": concrete_transactions, diff --git a/mythril/analysis/symbolic.py b/mythril/analysis/symbolic.py index 969a9ae5..d82cf8ca 100644 --- a/mythril/analysis/symbolic.py +++ b/mythril/analysis/symbolic.py @@ -85,11 +85,14 @@ class SymExecWrapper: requires_statespace = ( compulsory_statespace or len(get_detection_modules("post", modules)) > 0 ) - self.accounts = { - address: account, - hex(CREATOR_ADDRESS): creator_account, - hex(ATTACKER_ADDRESS): attacker_account, - } + if not contract.creation_code: + self.accounts = {address: account, hex(ATTACKER_ADDRESS): attacker_account} + else: + self.accounts = { + address: account, + hex(CREATOR_ADDRESS): creator_account, + hex(ATTACKER_ADDRESS): attacker_account, + } self.laser = svm.LaserEVM( self.accounts, @@ -106,7 +109,6 @@ class SymExecWrapper: plugin_loader = LaserPluginLoader(self.laser) plugin_loader.load(PluginFactory.build_mutation_pruner_plugin()) plugin_loader.load(PluginFactory.build_instruction_coverage_plugin()) - plugin_loader.load(PluginFactory.build_set_initial_state_plugin()) self.laser.register_hooks( hook_type="pre", diff --git a/mythril/analysis/templates/report_as_markdown.jinja2 b/mythril/analysis/templates/report_as_markdown.jinja2 index e6952535..289d1871 100644 --- a/mythril/analysis/templates/report_as_markdown.jinja2 +++ b/mythril/analysis/templates/report_as_markdown.jinja2 @@ -24,11 +24,11 @@ In file: {{ issue.filename }}:{{ issue.lineno }} {{ issue.code }} ``` {% endif %} -{% if verbose and issue.debug %} +{% if verbose and issue.tx_sequence %} -------------------- ### Debugging Information: -{{ issue.debug }} +{{ issue.tx_sequence }} {% endif %} {% endfor %} diff --git a/mythril/analysis/templates/report_as_text.jinja2 b/mythril/analysis/templates/report_as_text.jinja2 index 08edb6cb..da962583 100644 --- a/mythril/analysis/templates/report_as_text.jinja2 +++ b/mythril/analysis/templates/report_as_text.jinja2 @@ -18,11 +18,11 @@ In file: {{ issue.filename }}:{{ issue.lineno }} -------------------- {% endif %} -{% if verbose and issue.debug %} +{% if verbose and issue.tx_sequence %} -------------------- Transaction Sequence: -{{ issue.debug }} +{{ issue.tx_sequence }} {% endif %} {% endfor %} diff --git a/mythril/interfaces/cli.py b/mythril/interfaces/cli.py index a3e00adb..adb0e8f2 100644 --- a/mythril/interfaces/cli.py +++ b/mythril/interfaces/cli.py @@ -433,6 +433,7 @@ def execute_command( contract=analyzer.contracts[0], enable_physics=args.enable_physics, phrackify=args.phrack, + transaction_count=args.transaction_count, ) try: @@ -507,7 +508,7 @@ def parse_args(parser: argparse.ArgumentParser, args: argparse.Namespace) -> Non quick_commands(args) config = set_config(args) leveldb_search(config, args) - dissasembler = MythrilDisassembler( + disassembler = MythrilDisassembler( eth=config.eth, solc_version=args.solv, solc_args=args.solc_args, @@ -515,16 +516,16 @@ def parse_args(parser: argparse.ArgumentParser, args: argparse.Namespace) -> Non ) if args.truffle: try: - dissasembler.analyze_truffle_project(args) + disassembler.analyze_truffle_project(args) except FileNotFoundError: print( "Build directory not found. Make sure that you start the analysis from the project root, and that 'truffle compile' has executed successfully." ) sys.exit() - address = get_code(dissasembler, args) + address = get_code(disassembler, args) execute_command( - disassembler=dissasembler, address=address, parser=parser, args=args + disassembler=disassembler, address=address, parser=parser, args=args ) except CriticalError as ce: exit_with_error(args.outform, str(ce)) diff --git a/mythril/laser/ethereum/call.py b/mythril/laser/ethereum/call.py index bb86b7a8..da63928d 100644 --- a/mythril/laser/ethereum/call.py +++ b/mythril/laser/ethereum/call.py @@ -49,7 +49,6 @@ def get_call_parameters( callee_account = None call_data = get_call_data(global_state, memory_input_offset, memory_input_size) - if int(callee_address, 16) >= 5 or int(callee_address, 16) == 0: callee_account = get_callee_account( global_state, callee_address, dynamic_loader diff --git a/mythril/laser/ethereum/plugins/implementations/save_initial_world_state.py b/mythril/laser/ethereum/plugins/implementations/save_initial_world_state.py deleted file mode 100644 index e3526d1f..00000000 --- a/mythril/laser/ethereum/plugins/implementations/save_initial_world_state.py +++ /dev/null @@ -1,47 +0,0 @@ -from mythril.laser.ethereum.svm import LaserEVM -from mythril.laser.ethereum.plugins.plugin import LaserPlugin -from mythril.laser.ethereum.state.world_state import WorldState - -from typing import List - -import logging - -log = logging.getLogger(__name__) - - -class SaveInitialWorldState(LaserPlugin): - """SaveInitialWorldState - This plugin is used to save initial world state so it can be used for the output to display - - """ - - def __init__(self): - pass - - def initialize(self, symbolic_vm: LaserEVM): - """ - :param symbolic_vm: - :return: - """ - - @symbolic_vm.laser_hook("end_contract_creation") - def set_standard_initial_state(openstates: List[WorldState]): - """ - This function initializes the initial state to all the open states - :param openstates: - :return: - """ - accounts = openstates[0].accounts - initial_state = openstates[0].initial_state_account - initial_state[ - "accounts" - ] = {} # This variable persists for all world states. - for address, account in accounts.items(): - if address == "0x" + "0" * 40: - continue - initial_state["accounts"][address] = { - "nounce": account.nonce, - "balance": "", - "code": account.code.bytecode, - "storage": {}, - } diff --git a/mythril/laser/ethereum/plugins/plugin_factory.py b/mythril/laser/ethereum/plugins/plugin_factory.py index 98a5011e..1ce61b34 100644 --- a/mythril/laser/ethereum/plugins/plugin_factory.py +++ b/mythril/laser/ethereum/plugins/plugin_factory.py @@ -30,11 +30,3 @@ class PluginFactory: ) return InstructionCoveragePlugin() - - @staticmethod - def build_set_initial_state_plugin() -> LaserPlugin: - from mythril.laser.ethereum.plugins.implementations.save_initial_world_state import ( - SaveInitialWorldState, - ) - - return SaveInitialWorldState() diff --git a/mythril/laser/ethereum/svm.py b/mythril/laser/ethereum/svm.py index e535a71f..241f4796 100644 --- a/mythril/laser/ethereum/svm.py +++ b/mythril/laser/ethereum/svm.py @@ -103,20 +103,8 @@ class LaserEVM: self._start_sym_exec_hooks = [] # type: List[Callable] self._stop_sym_exec_hooks = [] # type: List[Callable] - self._end_contract_creation_hooks = [] # type: List[Callable] - self.iprof = InstructionProfiler() if enable_iprof else None - self.laser_hooks_dict = { - "add_world_state": self._add_world_state_hooks, - "execute_state": self._execute_state_hooks, - "start_sym_exec": self._start_sym_exec_hooks, - "stop_sym_exec": self._stop_sym_exec_hooks, - "start_sym_trans": self._start_sym_trans_hooks, - "stop_sym_trans": self._stop_sym_trans_hooks, - "end_contract_creation": self._end_contract_creation_hooks, - } - log.info("LASER EVM initialized with dynamic loader: " + str(dynamic_loader)) @property @@ -127,11 +115,13 @@ class LaserEVM: """ return self.world_state.accounts - def set_standard_initial_state(self, accounts: Dict[str, Account]): + def set_standard_initial_state( + self, accounts: Dict[str, Account], ignore_addr=True + ): initial_state = self.world_state.initial_state_account initial_state["accounts"] = {} # This variable persists for all world states. for address, account in accounts.items(): - if address == "0x" + "0" * 40: + if ignore_addr and address == "0x" + "0" * 40: continue initial_state["accounts"][address] = { "nounce": account.nonce, @@ -158,11 +148,13 @@ class LaserEVM: if main_address: log.info("Starting message call transaction to {}".format(main_address)) + self.set_standard_initial_state(self.world_state.accounts) self._execute_transactions(main_address) elif creation_code: log.info("Starting contract creation transaction") + self.set_standard_initial_state(self.world_state.accounts) created_account = execute_contract_creation( self, creation_code, contract_name ) @@ -177,8 +169,9 @@ class LaserEVM: "Increase the resources for creation execution (--max-depth or --create-timeout)" ) else: - for hook in self._end_contract_creation_hooks: - hook(self.open_states) + self.set_standard_initial_state( + self.open_states[0].accounts, ignore_addr=True + ) self._execute_transactions(created_account.address) @@ -319,7 +312,7 @@ class LaserEVM: ) except TransactionStartSignal as start_signal: - # Setup new global state + # Is a MessageCall to contract, setup new global state new_global_state = start_signal.transaction.initial_global_state() new_global_state.transaction_stack = copy( @@ -512,8 +505,18 @@ class LaserEVM: def register_laser_hooks(self, hook_type: str, hook: Callable): """registers the hook with this Laser VM""" - if hook_type in self.laser_hooks_dict: - self.laser_hooks_dict[hook_type].append(hook) + if hook_type == "add_world_state": + self._add_world_state_hooks.append(hook) + elif hook_type == "execute_state": + self._execute_state_hooks.append(hook) + elif hook_type == "start_sym_exec": + self._start_sym_exec_hooks.append(hook) + elif hook_type == "stop_sym_exec": + self._stop_sym_exec_hooks.append(hook) + elif hook_type == "start_sym_trans": + self._start_sym_trans_hooks.append(hook) + elif hook_type == "stop_sym_trans": + self._stop_sym_trans_hooks.append(hook) else: raise ValueError( "Invalid hook type %s. Must be one of {add_world_state}", hook_type diff --git a/tests/report_test.py b/tests/report_test.py index 30e9cabd..6fe596f9 100644 --- a/tests/report_test.py +++ b/tests/report_test.py @@ -17,7 +17,8 @@ def _fix_path(text): def _fix_debug_data(json_str): read_json = json.loads(json_str) for issue in read_json["issues"]: - issue["debug"] = "" + issue["tx_sequence"] = "" + return json.dumps(read_json, sort_keys=True, indent=4) @@ -25,6 +26,7 @@ def _add_jsonv2_stubs(json_str): read_json = json.loads(json_str) for issue in read_json[0]["issues"]: issue["extra"]["discoveryTime"] = "" + issue["extra"]["testCase"] = "" return json.dumps(read_json, sort_keys=True, indent=4) diff --git a/tests/testdata/outputs_expected/calls.sol.o.json b/tests/testdata/outputs_expected/calls.sol.o.json index 40f93d02..d8308ab6 100644 --- a/tests/testdata/outputs_expected/calls.sol.o.json +++ b/tests/testdata/outputs_expected/calls.sol.o.json @@ -4,7 +4,6 @@ { "address": 661, "contract": "Unknown", - "debug": "", "description": "The contract executes an external message call.\nAn external function call to a fixed contract address is executed. Make sure that the callee contract has been reviewed carefully.", "function": "thisisfine()", "max_gas_used": 1254, @@ -12,12 +11,12 @@ "severity": "Low", "sourceMap": null, "swc-id": "107", - "title": "External Call To Fixed Address" + "title": "External Call To Fixed Address", + "tx_sequence": "" }, { "address": 661, "contract": "Unknown", - "debug": "", "description": "The return value of a message call is not checked.\nExternal calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states.", "function": "thisisfine()", "max_gas_used": 35972, @@ -25,12 +24,12 @@ "severity": "Low", "sourceMap": null, "swc-id": "104", - "title": "Unchecked Call Return Value" + "title": "Unchecked Call Return Value", + "tx_sequence": "" }, { "address": 779, "contract": "Unknown", - "debug": "", "description": "The contract executes an external message call.\nAn external function call to a fixed contract address is executed. Make sure that the callee contract has been reviewed carefully.", "function": "callstoredaddress()", "max_gas_used": 1298, @@ -38,12 +37,12 @@ "severity": "Low", "sourceMap": null, "swc-id": "107", - "title": "External Call To Fixed Address" + "title": "External Call To Fixed Address", + "tx_sequence": "" }, { "address": 779, "contract": "Unknown", - "debug": "", "description": "The return value of a message call is not checked.\nExternal calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states.", "function": "callstoredaddress()", "max_gas_used": 36016, @@ -51,12 +50,12 @@ "severity": "Low", "sourceMap": null, "swc-id": "104", - "title": "Unchecked Call Return Value" + "title": "Unchecked Call Return Value", + "tx_sequence": "" }, { "address": 858, "contract": "Unknown", - "debug": "", "description": "The contract executes an external message call.\nAn external function call to a fixed contract address is executed. Make sure that the callee contract has been reviewed carefully.", "function": "reentrancy()", "max_gas_used": 1320, @@ -64,12 +63,12 @@ "severity": "Low", "sourceMap": null, "swc-id": "107", - "title": "External Call To Fixed Address" + "title": "External Call To Fixed Address", + "tx_sequence": "" }, { "address": 858, "contract": "Unknown", - "debug": "", "description": "The return value of a message call is not checked.\nExternal calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states.", "function": "reentrancy()", "max_gas_used": 61052, @@ -77,12 +76,25 @@ "severity": "Low", "sourceMap": null, "swc-id": "104", - "title": "Unchecked Call Return Value" + "title": "Unchecked Call Return Value", + "tx_sequence": "" + }, + { + "address": 869, + "contract": "Unknown", + "description": "The contract account state is changed after an external call. \nConsider that the called contract could re-enter the function before this state change takes place. This can lead to business logic vulnerabilities.", + "function": "reentrancy()", + "max_gas_used": null, + "min_gas_used": null, + "severity": "Low", + "sourceMap": null, + "swc-id": "107", + "title": "State change after external call", + "tx_sequence": "" }, { "address": 912, "contract": "Unknown", - "debug": "", "description": "A call to a user-supplied address is executed.\nThe callee address of an external message call can be set by the caller. Note that the callee can contain arbitrary code and may re-enter any function in this contract. Review the business logic carefully to prevent averse effects on the contract state.", "function": "calluseraddress(address)", "max_gas_used": 616, @@ -90,12 +102,12 @@ "severity": "Medium", "sourceMap": null, "swc-id": "107", - "title": "External Call To User-Supplied Address" + "title": "External Call To User-Supplied Address", + "tx_sequence": "" }, { "address": 912, "contract": "Unknown", - "debug": "", "description": "The return value of a message call is not checked.\nExternal calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states.", "function": "calluseraddress(address)", "max_gas_used": 35336, @@ -103,7 +115,8 @@ "severity": "Low", "sourceMap": null, "swc-id": "104", - "title": "Unchecked Call Return Value" + "title": "Unchecked Call Return Value", + "tx_sequence": "" } ], "success": true diff --git a/tests/testdata/outputs_expected/calls.sol.o.jsonv2 b/tests/testdata/outputs_expected/calls.sol.o.jsonv2 index da380624..ffe5b632 100644 --- a/tests/testdata/outputs_expected/calls.sol.o.jsonv2 +++ b/tests/testdata/outputs_expected/calls.sol.o.jsonv2 @@ -7,7 +7,8 @@ "tail": "An external function call to a fixed contract address is executed. Make sure that the callee contract has been reviewed carefully." }, "extra": { - "discoveryTime": "" + "discoveryTime": "", + "testCase": "" }, "locations": [ { @@ -24,7 +25,8 @@ "tail": "An external function call to a fixed contract address is executed. Make sure that the callee contract has been reviewed carefully." }, "extra": { - "discoveryTime": "" + "discoveryTime": "", + "testCase": "" }, "locations": [ { @@ -41,7 +43,8 @@ "tail": "An external function call to a fixed contract address is executed. Make sure that the callee contract has been reviewed carefully." }, "extra": { - "discoveryTime": "" + "discoveryTime": "", + "testCase": "" }, "locations": [ { @@ -58,7 +61,8 @@ "tail": "The callee address of an external message call can be set by the caller. Note that the callee can contain arbitrary code and may re-enter any function in this contract. Review the business logic carefully to prevent averse effects on the contract state." }, "extra": { - "discoveryTime": "" + "discoveryTime": "", + "testCase": "" }, "locations": [ { @@ -69,13 +73,32 @@ "swcID": "SWC-107", "swcTitle": "Reentrancy" }, + { + "description": { + "head": "The contract account state is changed after an external call. ", + "tail": "Consider that the called contract could re-enter the function before this state change takes place. This can lead to business logic vulnerabilities." + }, + "extra": { + "discoveryTime": "", + "testCase": "" + }, + "locations": [ + { + "sourceMap": "869:1:0" + } + ], + "severity": "Low", + "swcID": "SWC-107", + "swcTitle": "Reentrancy" + }, { "description": { "head": "The return value of a message call is not checked.", "tail": "External calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states." }, "extra": { - "discoveryTime": "" + "discoveryTime": "", + "testCase": "" }, "locations": [ { @@ -92,7 +115,8 @@ "tail": "External calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states." }, "extra": { - "discoveryTime": "" + "discoveryTime": "", + "testCase": "" }, "locations": [ { @@ -109,7 +133,8 @@ "tail": "External calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states." }, "extra": { - "discoveryTime": "" + "discoveryTime": "", + "testCase": "" }, "locations": [ { @@ -126,7 +151,8 @@ "tail": "External calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states." }, "extra": { - "discoveryTime": "" + "discoveryTime": "", + "testCase": "" }, "locations": [ { diff --git a/tests/testdata/outputs_expected/calls.sol.o.markdown b/tests/testdata/outputs_expected/calls.sol.o.markdown index 3ad3e237..b45544be 100644 --- a/tests/testdata/outputs_expected/calls.sol.o.markdown +++ b/tests/testdata/outputs_expected/calls.sol.o.markdown @@ -78,6 +78,19 @@ An external function call to a fixed contract address is executed. Make sure tha The return value of a message call is not checked. External calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states. +## State change after external call +- SWC ID: 107 +- Severity: Low +- Contract: Unknown +- Function name: `reentrancy()` +- PC address: 869 +- Estimated Gas Usage: None - None + +### Description + +The contract account state is changed after an external call. +Consider that the called contract could re-enter the function before this state change takes place. This can lead to business logic vulnerabilities. + ## External Call To User-Supplied Address - SWC ID: 107 - Severity: Medium diff --git a/tests/testdata/outputs_expected/calls.sol.o.text b/tests/testdata/outputs_expected/calls.sol.o.text index dfbfa338..27706fd1 100644 --- a/tests/testdata/outputs_expected/calls.sol.o.text +++ b/tests/testdata/outputs_expected/calls.sol.o.text @@ -64,6 +64,17 @@ The return value of a message call is not checked. External calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states. -------------------- +==== State change after external call ==== +SWC ID: 107 +Severity: Low +Contract: Unknown +Function name: reentrancy() +PC address: 869 +Estimated Gas Usage: None - None +The contract account state is changed after an external call. +Consider that the called contract could re-enter the function before this state change takes place. This can lead to business logic vulnerabilities. +-------------------- + ==== External Call To User-Supplied Address ==== SWC ID: 107 Severity: Medium diff --git a/tests/testdata/outputs_expected/ether_send.sol.o.jsonv2 b/tests/testdata/outputs_expected/ether_send.sol.o.jsonv2 index 3bce29c2..9f1597a1 100644 --- a/tests/testdata/outputs_expected/ether_send.sol.o.jsonv2 +++ b/tests/testdata/outputs_expected/ether_send.sol.o.jsonv2 @@ -1 +1,11 @@ -[{"issues": [], "meta": {}, "sourceFormat": "evm-byzantium-bytecode", "sourceList": ["0x3746c7c2ae7b0d4c3f8b1905df9a7ea169b9f93bec68a10a00b4c9d27a18c6fb"], "sourceType": "raw-bytecode"}] +[ + { + "issues": [], + "meta": {}, + "sourceFormat": "evm-byzantium-bytecode", + "sourceList": [ + "0x3746c7c2ae7b0d4c3f8b1905df9a7ea169b9f93bec68a10a00b4c9d27a18c6fb" + ], + "sourceType": "raw-bytecode" + } +] \ No newline at end of file diff --git a/tests/testdata/outputs_expected/exceptions.sol.o.json b/tests/testdata/outputs_expected/exceptions.sol.o.json index e529a0ae..19030e55 100644 --- a/tests/testdata/outputs_expected/exceptions.sol.o.json +++ b/tests/testdata/outputs_expected/exceptions.sol.o.json @@ -4,7 +4,6 @@ { "address": 446, "contract": "Unknown", - "debug": "", "description": "A reachable exception has been detected.\nIt is possible to trigger an exception (opcode 0xfe). Exceptions can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking.", "function": "assert3(uint256)", "max_gas_used": 301, @@ -12,12 +11,12 @@ "severity": "Low", "sourceMap": null, "swc-id": "110", - "title": "Exception State" + "title": "Exception State", + "tx_sequence": "" }, { "address": 484, "contract": "Unknown", - "debug": "", "description": "A reachable exception has been detected.\nIt is possible to trigger an exception (opcode 0xfe). Exceptions can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking.", "function": "arrayaccess(uint256)", "max_gas_used": 351, @@ -25,12 +24,12 @@ "severity": "Low", "sourceMap": null, "swc-id": "110", - "title": "Exception State" + "title": "Exception State", + "tx_sequence": "" }, { "address": 506, "contract": "Unknown", - "debug": "", "description": "A reachable exception has been detected.\nIt is possible to trigger an exception (opcode 0xfe). Exceptions can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking.", "function": "divisionby0(uint256)", "max_gas_used": 367, @@ -38,12 +37,12 @@ "severity": "Low", "sourceMap": null, "swc-id": "110", - "title": "Exception State" + "title": "Exception State", + "tx_sequence": "" }, { "address": 531, "contract": "Unknown", - "debug": "", "description": "A reachable exception has been detected.\nIt is possible to trigger an exception (opcode 0xfe). Exceptions can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking.", "function": "assert1()", "max_gas_used": 363, @@ -51,7 +50,8 @@ "severity": "Low", "sourceMap": null, "swc-id": "110", - "title": "Exception State" + "title": "Exception State", + "tx_sequence": "" } ], "success": true diff --git a/tests/testdata/outputs_expected/exceptions.sol.o.jsonv2 b/tests/testdata/outputs_expected/exceptions.sol.o.jsonv2 index 032cfc01..43b6ca48 100644 --- a/tests/testdata/outputs_expected/exceptions.sol.o.jsonv2 +++ b/tests/testdata/outputs_expected/exceptions.sol.o.jsonv2 @@ -7,7 +7,8 @@ "tail": "It is possible to trigger an exception (opcode 0xfe). Exceptions can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking." }, "extra": { - "discoveryTime": "" + "discoveryTime": "", + "testCase": "" }, "locations": [ { @@ -24,7 +25,8 @@ "tail": "It is possible to trigger an exception (opcode 0xfe). Exceptions can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking." }, "extra": { - "discoveryTime": "" + "discoveryTime": "", + "testCase": "" }, "locations": [ { @@ -41,7 +43,8 @@ "tail": "It is possible to trigger an exception (opcode 0xfe). Exceptions can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking." }, "extra": { - "discoveryTime": "" + "discoveryTime": "", + "testCase": "" }, "locations": [ { @@ -58,7 +61,8 @@ "tail": "It is possible to trigger an exception (opcode 0xfe). Exceptions can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. Note that explicit `assert()` should only be used to check invariants. Use `require()` for regular input checking." }, "extra": { - "discoveryTime": "" + "discoveryTime": "", + "testCase": "" }, "locations": [ { diff --git a/tests/testdata/outputs_expected/kinds_of_calls.sol.o.json b/tests/testdata/outputs_expected/kinds_of_calls.sol.o.json index b6bc99ac..9a419db2 100644 --- a/tests/testdata/outputs_expected/kinds_of_calls.sol.o.json +++ b/tests/testdata/outputs_expected/kinds_of_calls.sol.o.json @@ -4,7 +4,6 @@ { "address": 618, "contract": "Unknown", - "debug": "", "description": "The return value of a message call is not checked.\nExternal calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states.", "function": "_function_0x141f32ff", "max_gas_used": 35865, @@ -12,12 +11,12 @@ "severity": "Low", "sourceMap": null, "swc-id": "104", - "title": "Unchecked Call Return Value" + "title": "Unchecked Call Return Value", + "tx_sequence": "" }, { "address": 618, "contract": "Unknown", - "debug": "", "description": "Use of callcode is deprecated.\nThe callcode method executes code of another contract in the context of the caller account. Due to a bug in the implementation it does not persist sender and value over the call. It was therefore deprecated and may be removed in the future. Use the delegatecall method instead.", "function": "_function_0x141f32ff", "max_gas_used": 1141, @@ -25,12 +24,12 @@ "severity": "Medium", "sourceMap": null, "swc-id": "111", - "title": "Use of callcode" + "title": "Use of callcode", + "tx_sequence": "" }, { "address": 849, "contract": "Unknown", - "debug": "", "description": "The return value of a message call is not checked.\nExternal calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states.", "function": "_function_0x9b58bc26", "max_gas_used": 35928, @@ -38,12 +37,12 @@ "severity": "Low", "sourceMap": null, "swc-id": "104", - "title": "Unchecked Call Return Value" + "title": "Unchecked Call Return Value", + "tx_sequence": "" }, { "address": 1038, "contract": "Unknown", - "debug": "", "description": "A call to a user-supplied address is executed.\nThe callee address of an external message call can be set by the caller. Note that the callee can contain arbitrary code and may re-enter any function in this contract. Review the business logic carefully to prevent averse effects on the contract state.", "function": "_function_0xeea4c864", "max_gas_used": 1229, @@ -51,12 +50,12 @@ "severity": "Medium", "sourceMap": null, "swc-id": "107", - "title": "External Call To User-Supplied Address" + "title": "External Call To User-Supplied Address", + "tx_sequence": "" }, { "address": 1038, "contract": "Unknown", - "debug": "", "description": "The return value of a message call is not checked.\nExternal calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states.", "function": "_function_0xeea4c864", "max_gas_used": 35953, @@ -64,7 +63,8 @@ "severity": "Low", "sourceMap": null, "swc-id": "104", - "title": "Unchecked Call Return Value" + "title": "Unchecked Call Return Value", + "tx_sequence": "" } ], "success": true diff --git a/tests/testdata/outputs_expected/kinds_of_calls.sol.o.jsonv2 b/tests/testdata/outputs_expected/kinds_of_calls.sol.o.jsonv2 index 4f0d13e0..3dfdad2d 100644 --- a/tests/testdata/outputs_expected/kinds_of_calls.sol.o.jsonv2 +++ b/tests/testdata/outputs_expected/kinds_of_calls.sol.o.jsonv2 @@ -7,7 +7,8 @@ "tail": "The callcode method executes code of another contract in the context of the caller account. Due to a bug in the implementation it does not persist sender and value over the call. It was therefore deprecated and may be removed in the future. Use the delegatecall method instead." }, "extra": { - "discoveryTime": "" + "discoveryTime": "", + "testCase": "" }, "locations": [ { @@ -24,7 +25,8 @@ "tail": "The callee address of an external message call can be set by the caller. Note that the callee can contain arbitrary code and may re-enter any function in this contract. Review the business logic carefully to prevent averse effects on the contract state." }, "extra": { - "discoveryTime": "" + "discoveryTime": "", + "testCase": "" }, "locations": [ { @@ -41,7 +43,8 @@ "tail": "External calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states." }, "extra": { - "discoveryTime": "" + "discoveryTime": "", + "testCase": "" }, "locations": [ { @@ -58,7 +61,8 @@ "tail": "External calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states." }, "extra": { - "discoveryTime": "" + "discoveryTime": "", + "testCase": "" }, "locations": [ { @@ -75,7 +79,8 @@ "tail": "External calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states." }, "extra": { - "discoveryTime": "" + "discoveryTime": "", + "testCase": "" }, "locations": [ { diff --git a/tests/testdata/outputs_expected/metacoin.sol.o.jsonv2 b/tests/testdata/outputs_expected/metacoin.sol.o.jsonv2 index c669f304..40de69b4 100644 --- a/tests/testdata/outputs_expected/metacoin.sol.o.jsonv2 +++ b/tests/testdata/outputs_expected/metacoin.sol.o.jsonv2 @@ -1 +1,11 @@ -[{"issues": [], "meta": {}, "sourceFormat": "evm-byzantium-bytecode", "sourceList": ["0x0e6f727bb3301e02d3be831bf34357522fd2f1d40e90dff8e2214553b06b5f6c"], "sourceType": "raw-bytecode"}] +[ + { + "issues": [], + "meta": {}, + "sourceFormat": "evm-byzantium-bytecode", + "sourceList": [ + "0x0e6f727bb3301e02d3be831bf34357522fd2f1d40e90dff8e2214553b06b5f6c" + ], + "sourceType": "raw-bytecode" + } +] \ No newline at end of file diff --git a/tests/testdata/outputs_expected/multi_contracts.sol.o.json b/tests/testdata/outputs_expected/multi_contracts.sol.o.json index 8ade4afb..cf2fd3af 100644 --- a/tests/testdata/outputs_expected/multi_contracts.sol.o.json +++ b/tests/testdata/outputs_expected/multi_contracts.sol.o.json @@ -4,7 +4,6 @@ { "address": 142, "contract": "Unknown", - "debug": "", "description": "Anyone can withdraw ETH from the contract account.\nArbitrary senders other than the contract creator can withdraw ETH from the contract account without previously having sent an equivalent amount of ETH to it. This is likely to be a vulnerability.", "function": "transfer()", "max_gas_used": 467, @@ -12,7 +11,8 @@ "severity": "High", "sourceMap": null, "swc-id": "105", - "title": "Unprotected Ether Withdrawal" + "title": "Unprotected Ether Withdrawal", + "tx_sequence": "" } ], "success": true diff --git a/tests/testdata/outputs_expected/multi_contracts.sol.o.jsonv2 b/tests/testdata/outputs_expected/multi_contracts.sol.o.jsonv2 index 21672449..ec36d8ca 100644 --- a/tests/testdata/outputs_expected/multi_contracts.sol.o.jsonv2 +++ b/tests/testdata/outputs_expected/multi_contracts.sol.o.jsonv2 @@ -7,7 +7,8 @@ "tail": "Arbitrary senders other than the contract creator can withdraw ETH from the contract account without previously having sent an equivalent amount of ETH to it. This is likely to be a vulnerability." }, "extra": { - "discoveryTime": "" + "discoveryTime": "", + "testCase": "" }, "locations": [ { diff --git a/tests/testdata/outputs_expected/nonascii.sol.o.jsonv2 b/tests/testdata/outputs_expected/nonascii.sol.o.jsonv2 index be2cc307..0667ad8c 100644 --- a/tests/testdata/outputs_expected/nonascii.sol.o.jsonv2 +++ b/tests/testdata/outputs_expected/nonascii.sol.o.jsonv2 @@ -1 +1,11 @@ -[{"issues": [], "meta": {}, "sourceFormat": "evm-byzantium-bytecode", "sourceList": ["0x11a78eb09819f505ba4f10747e6d1f7a44480e602c67573b7abac2f733a85d93"], "sourceType": "raw-bytecode"}] +[ + { + "issues": [], + "meta": {}, + "sourceFormat": "evm-byzantium-bytecode", + "sourceList": [ + "0x11a78eb09819f505ba4f10747e6d1f7a44480e602c67573b7abac2f733a85d93" + ], + "sourceType": "raw-bytecode" + } +] \ No newline at end of file diff --git a/tests/testdata/outputs_expected/origin.sol.o.json b/tests/testdata/outputs_expected/origin.sol.o.json index d9c74bc1..6d79baf7 100644 --- a/tests/testdata/outputs_expected/origin.sol.o.json +++ b/tests/testdata/outputs_expected/origin.sol.o.json @@ -4,7 +4,6 @@ { "address": 317, "contract": "Unknown", - "debug": "", "description": "Use of tx.origin is deprecated.\nThe smart contract retrieves the transaction origin (tx.origin) using msg.origin. Use of msg.origin is deprecated and the instruction may be removed in the future. Use msg.sender instead.\nSee also: https://solidity.readthedocs.io/en/develop/security-considerations.html#tx-origin", "function": "transferOwnership(address)", "max_gas_used": 1051, @@ -12,7 +11,8 @@ "severity": "Medium", "sourceMap": null, "swc-id": "111", - "title": "Use of tx.origin" + "title": "Use of tx.origin", + "tx_sequence": "" } ], "success": true diff --git a/tests/testdata/outputs_expected/origin.sol.o.jsonv2 b/tests/testdata/outputs_expected/origin.sol.o.jsonv2 index 27322fde..ec679550 100644 --- a/tests/testdata/outputs_expected/origin.sol.o.jsonv2 +++ b/tests/testdata/outputs_expected/origin.sol.o.jsonv2 @@ -7,7 +7,8 @@ "tail": "The smart contract retrieves the transaction origin (tx.origin) using msg.origin. Use of msg.origin is deprecated and the instruction may be removed in the future. Use msg.sender instead.\nSee also: https://solidity.readthedocs.io/en/develop/security-considerations.html#tx-origin" }, "extra": { - "discoveryTime": "" + "discoveryTime": "", + "testCase": "" }, "locations": [ { diff --git a/tests/testdata/outputs_expected/overflow.sol.o.json b/tests/testdata/outputs_expected/overflow.sol.o.json index 89fdb839..9d8be1ec 100644 --- a/tests/testdata/outputs_expected/overflow.sol.o.json +++ b/tests/testdata/outputs_expected/overflow.sol.o.json @@ -4,7 +4,6 @@ { "address": 567, "contract": "Unknown", - "debug": "", "description": "The binary subtraction can underflow.\nThe operands of the subtraction operation are not sufficiently constrained. The subtraction could therefore result in an integer underflow. Prevent the underflow by checking inputs or ensure sure that the underflow is caught by an assertion.", "function": "sendeth(address,uint256)", "max_gas_used": 78155, @@ -12,12 +11,12 @@ "severity": "High", "sourceMap": null, "swc-id": "101", - "title": "Integer Underflow" + "title": "Integer Underflow", + "tx_sequence": "" }, { "address": 649, "contract": "Unknown", - "debug": "", "description": "The binary subtraction can underflow.\nThe operands of the subtraction operation are not sufficiently constrained. The subtraction could therefore result in an integer underflow. Prevent the underflow by checking inputs or ensure sure that the underflow is caught by an assertion.", "function": "sendeth(address,uint256)", "max_gas_used": 78155, @@ -25,7 +24,8 @@ "severity": "High", "sourceMap": null, "swc-id": "101", - "title": "Integer Underflow" + "title": "Integer Underflow", + "tx_sequence": "" } ], "success": true diff --git a/tests/testdata/outputs_expected/overflow.sol.o.jsonv2 b/tests/testdata/outputs_expected/overflow.sol.o.jsonv2 index dfcc29d5..6610f574 100644 --- a/tests/testdata/outputs_expected/overflow.sol.o.jsonv2 +++ b/tests/testdata/outputs_expected/overflow.sol.o.jsonv2 @@ -7,7 +7,8 @@ "tail": "The operands of the subtraction operation are not sufficiently constrained. The subtraction could therefore result in an integer underflow. Prevent the underflow by checking inputs or ensure sure that the underflow is caught by an assertion." }, "extra": { - "discoveryTime": "" + "discoveryTime": "", + "testCase": "" }, "locations": [ { @@ -24,7 +25,8 @@ "tail": "The operands of the subtraction operation are not sufficiently constrained. The subtraction could therefore result in an integer underflow. Prevent the underflow by checking inputs or ensure sure that the underflow is caught by an assertion." }, "extra": { - "discoveryTime": "" + "discoveryTime": "", + "testCase": "" }, "locations": [ { diff --git a/tests/testdata/outputs_expected/returnvalue.sol.o.json b/tests/testdata/outputs_expected/returnvalue.sol.o.json index b31986bd..67302beb 100644 --- a/tests/testdata/outputs_expected/returnvalue.sol.o.json +++ b/tests/testdata/outputs_expected/returnvalue.sol.o.json @@ -4,7 +4,6 @@ { "address": 196, "contract": "Unknown", - "debug": "", "description": "The contract executes an external message call.\nAn external function call to a fixed contract address is executed. Make sure that the callee contract has been reviewed carefully.", "function": "callchecked()", "max_gas_used": 1210, @@ -12,12 +11,12 @@ "severity": "Low", "sourceMap": null, "swc-id": "107", - "title": "External Call To Fixed Address" + "title": "External Call To Fixed Address", + "tx_sequence": "" }, { "address": 285, "contract": "Unknown", - "debug": "", "description": "The contract executes an external message call.\nAn external function call to a fixed contract address is executed. Make sure that the callee contract has been reviewed carefully.", "function": "callnotchecked()", "max_gas_used": 1232, @@ -25,12 +24,12 @@ "severity": "Low", "sourceMap": null, "swc-id": "107", - "title": "External Call To Fixed Address" + "title": "External Call To Fixed Address", + "tx_sequence": "" }, { "address": 285, "contract": "Unknown", - "debug": "", "description": "The return value of a message call is not checked.\nExternal calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states.", "function": "callnotchecked()", "max_gas_used": 35950, @@ -38,7 +37,8 @@ "severity": "Low", "sourceMap": null, "swc-id": "104", - "title": "Unchecked Call Return Value" + "title": "Unchecked Call Return Value", + "tx_sequence": "" } ], "success": true diff --git a/tests/testdata/outputs_expected/returnvalue.sol.o.jsonv2 b/tests/testdata/outputs_expected/returnvalue.sol.o.jsonv2 index 03fb9c0d..ccc35239 100644 --- a/tests/testdata/outputs_expected/returnvalue.sol.o.jsonv2 +++ b/tests/testdata/outputs_expected/returnvalue.sol.o.jsonv2 @@ -7,7 +7,8 @@ "tail": "An external function call to a fixed contract address is executed. Make sure that the callee contract has been reviewed carefully." }, "extra": { - "discoveryTime": "" + "discoveryTime": "", + "testCase": "" }, "locations": [ { @@ -24,7 +25,8 @@ "tail": "An external function call to a fixed contract address is executed. Make sure that the callee contract has been reviewed carefully." }, "extra": { - "discoveryTime": "" + "discoveryTime": "", + "testCase": "" }, "locations": [ { @@ -41,7 +43,8 @@ "tail": "External calls return a boolean value. If the callee contract halts with an exception, 'false' is returned and execution continues in the caller. It is usually recommended to wrap external calls into a require statement to prevent unexpected states." }, "extra": { - "discoveryTime": "" + "discoveryTime": "", + "testCase": "" }, "locations": [ { diff --git a/tests/testdata/outputs_expected/suicide.sol.o.json b/tests/testdata/outputs_expected/suicide.sol.o.json index 054f1981..1c98a444 100644 --- a/tests/testdata/outputs_expected/suicide.sol.o.json +++ b/tests/testdata/outputs_expected/suicide.sol.o.json @@ -4,7 +4,6 @@ { "address": 146, "contract": "Unknown", - "debug": "", "description": "The contract can be killed by anyone.\nAnyone can kill this contract and withdraw its balance to an arbitrary address.", "function": "kill(address)", "max_gas_used": 263, @@ -12,7 +11,8 @@ "severity": "High", "sourceMap": null, "swc-id": "106", - "title": "Unprotected Selfdestruct" + "title": "Unprotected Selfdestruct", + "tx_sequence": "" } ], "success": true diff --git a/tests/testdata/outputs_expected/suicide.sol.o.jsonv2 b/tests/testdata/outputs_expected/suicide.sol.o.jsonv2 index c492c24c..30daf88a 100644 --- a/tests/testdata/outputs_expected/suicide.sol.o.jsonv2 +++ b/tests/testdata/outputs_expected/suicide.sol.o.jsonv2 @@ -7,7 +7,8 @@ "tail": "Anyone can kill this contract and withdraw its balance to an arbitrary address." }, "extra": { - "discoveryTime": "" + "discoveryTime": "", + "testCase": "" }, "locations": [ { diff --git a/tests/testdata/outputs_expected/underflow.sol.o.json b/tests/testdata/outputs_expected/underflow.sol.o.json index 6a2464d5..0db997f7 100644 --- a/tests/testdata/outputs_expected/underflow.sol.o.json +++ b/tests/testdata/outputs_expected/underflow.sol.o.json @@ -4,7 +4,6 @@ { "address": 567, "contract": "Unknown", - "debug": "", "description": "The binary subtraction can underflow.\nThe operands of the subtraction operation are not sufficiently constrained. The subtraction could therefore result in an integer underflow. Prevent the underflow by checking inputs or ensure sure that the underflow is caught by an assertion.", "function": "sendeth(address,uint256)", "max_gas_used": 52861, @@ -12,12 +11,12 @@ "severity": "High", "sourceMap": null, "swc-id": "101", - "title": "Integer Underflow" + "title": "Integer Underflow", + "tx_sequence": "" }, { "address": 649, "contract": "Unknown", - "debug": "", "description": "The binary subtraction can underflow.\nThe operands of the subtraction operation are not sufficiently constrained. The subtraction could therefore result in an integer underflow. Prevent the underflow by checking inputs or ensure sure that the underflow is caught by an assertion.", "function": "sendeth(address,uint256)", "max_gas_used": 52861, @@ -25,7 +24,8 @@ "severity": "High", "sourceMap": null, "swc-id": "101", - "title": "Integer Underflow" + "title": "Integer Underflow", + "tx_sequence": "" } ], "success": true diff --git a/tests/testdata/outputs_expected/underflow.sol.o.jsonv2 b/tests/testdata/outputs_expected/underflow.sol.o.jsonv2 index 94854e04..60cc52f2 100644 --- a/tests/testdata/outputs_expected/underflow.sol.o.jsonv2 +++ b/tests/testdata/outputs_expected/underflow.sol.o.jsonv2 @@ -7,7 +7,8 @@ "tail": "The operands of the subtraction operation are not sufficiently constrained. The subtraction could therefore result in an integer underflow. Prevent the underflow by checking inputs or ensure sure that the underflow is caught by an assertion." }, "extra": { - "discoveryTime": "" + "discoveryTime": "", + "testCase": "" }, "locations": [ { @@ -24,7 +25,8 @@ "tail": "The operands of the subtraction operation are not sufficiently constrained. The subtraction could therefore result in an integer underflow. Prevent the underflow by checking inputs or ensure sure that the underflow is caught by an assertion." }, "extra": { - "discoveryTime": "" + "discoveryTime": "", + "testCase": "" }, "locations": [ {