Skip to content

Commit

Permalink
Fixes issues with python 3.5 dict ordering
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuata committed Jun 5, 2018
1 parent 11a2bb7 commit f5a46ef
Show file tree
Hide file tree
Showing 13 changed files with 177 additions and 124 deletions.
11 changes: 6 additions & 5 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ jobs:
LC_ALL: C.UTF-8
LANG: C.UTF-8

- save_cache:
key: tox-env-{{ checksum "/home/mythril/tox.ini" }}-{{ checksum "/home/mythril/setup.py" }}
paths:
- .tox/py*
- /root/.cache/pip/wheels/

- run:
background: true
name: Launch of background geth instance
Expand All @@ -51,11 +57,6 @@ jobs:
- store_artifacts:
path: /home/mythril/.tox/output

- save_cache:
key: tox-env-{{ checksum "/home/mythril/tox.ini" }}-{{ checksum "/home/mythril/setup.py" }}
paths:
- .tox/py*

- run:
name: Ensuring that setup script is functional
command: python3 setup.py install
Expand Down
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pylint = "*"
yapf = "*"
pytest = "*"
pytest-mock = "*"
pytest-cov = "*"

[requires]

Expand Down
16 changes: 10 additions & 6 deletions mythril/analysis/report.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import hashlib
import json
import operator
from jinja2 import PackageLoader, Environment

class Issue:
Expand Down Expand Up @@ -45,25 +46,28 @@ def __init__(self, verbose=False):
self.verbose = verbose
pass

def sorted_issues(self):
issue_list = [issue.as_dict() for key, issue in self.issues.items()]
return sorted(issue_list, key=operator.itemgetter('address', 'title'))

def append_issue(self, issue):
m = hashlib.md5()
m.update((issue.contract + str(issue.address) + issue.title).encode('utf-8'))
self.issues[m.digest()] = issue

def as_text(self):
filename = self._file_name()
name = self._file_name()
template = Report.environment.get_template('report_as_text.jinja2')
return template.render(filename=filename, issues=self.issues, verbose=self.verbose)
return template.render(filename=name, issues=self.sorted_issues(), verbose=self.verbose)

def as_json(self):
issues = [issue.as_dict() for key, issue in self.issues.items()]
result = {'success': True, 'error': None, 'issues': issues}
return json.dumps(result)
result = {'success': True, 'error': None, 'issues': self.sorted_issues()}
return json.dumps(result, sort_keys=True)

def as_markdown(self):
filename = self._file_name()
template = Report.environment.get_template('report_as_markdown.jinja2')
return template.render(filename=filename, issues=self.issues, verbose=self.verbose)
return template.render(filename=filename, issues=self.sorted_issues(), verbose=self.verbose)

def _file_name(self):
if len(self.issues.values()) > 0:
Expand Down
2 changes: 1 addition & 1 deletion mythril/analysis/templates/report_as_markdown.jinja2
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Analysis results for {{ filename }}
{% if issues %}
{% for key, issue in issues.items() %}
{% for issue in issues %}

## {{ issue.title }}

Expand Down
2 changes: 1 addition & 1 deletion mythril/analysis/templates/report_as_text.jinja2
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{% if issues %}
{% for key, issue in issues.items() %}
{% for issue in issues %}
==== {{ issue.title }} ====
Type: {{ issue.type }}
Contract: {{ issue.contract | default("Unknown") }}
Expand Down
46 changes: 44 additions & 2 deletions tests/report_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def _fix_debug_data(json_str):
read_json = json.loads(json_str)
for issue in read_json["issues"]:
issue["debug"] = "<DEBUG-DATA>"
return json.dumps(read_json, indent=4)
return json.dumps(read_json, sort_keys=True)


def _generate_report(input_file):
Expand Down Expand Up @@ -56,6 +56,29 @@ def _assert_empty(changed_files, postfix):

assert message == "", message

def _assert_empty_json(changed_files):
""" Asserts there are no changed files and otherwise builds error message"""
postfix = ".json"
expected = []
actual = []

def ordered(obj):
if isinstance(obj, dict):
return sorted((k, ordered(v)) for k, v in obj.items())
elif isinstance(obj, list):
return sorted(ordered(x) for x in obj)
else:
return obj

for input_file in changed_files:
output_expected = json.loads((TESTDATA_OUTPUTS_EXPECTED / (input_file.name + postfix)).read_text())
output_current = json.loads((TESTDATA_OUTPUTS_CURRENT / (input_file.name + postfix)).read_text())

if not ordered(output_expected.items()) == ordered(output_current.items()):
expected.append(output_expected)
actual.append(output_current)

assert expected == actual

def _get_changed_files(postfix, report_builder, reports):
"""
Expand All @@ -74,8 +97,27 @@ def _get_changed_files(postfix, report_builder, reports):
yield input_file


def _get_changed_files_json(report_builder, reports):
postfix = ".json"
def ordered(obj):
if isinstance(obj, dict):
return sorted((k, ordered(v)) for k, v in obj.items())
elif isinstance(obj, list):
return sorted(ordered(x) for x in obj)
else:
return obj

for report, input_file in reports:
output_expected = TESTDATA_OUTPUTS_EXPECTED / (input_file.name + postfix)
output_current = TESTDATA_OUTPUTS_CURRENT / (input_file.name + postfix)
output_current.write_text(report_builder(report))

if not ordered(json.loads(output_expected.read_text())) == ordered(json.loads(output_current.read_text())):
yield input_file


def test_json_report(reports):
_assert_empty(_get_changed_files('.json', lambda report: _fix_path(_fix_debug_data(report.as_json())).strip(), reports), '.json')
_assert_empty_json(_get_changed_files_json(lambda report: _fix_path(_fix_debug_data(report.as_json())).strip(), reports))


def test_markdown_report(reports):
Expand Down
70 changes: 35 additions & 35 deletions tests/testdata/outputs_expected/calls.sol.o.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -11,49 +11,27 @@

This contract executes a message call to to another contract. Make sure that the called contract is trusted and does not execute user-supplied code.

## Message call to external contract

- Type: Warning
- Contract: Unknown
- Function name: `_function_0xd24b08cc`
- PC address: 779

### Description

This contract executes a message call to an address found at storage slot 1. This storage slot can be written to by calling the function `_function_0x2776b163`. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.

## Message call to external contract
## Unchecked CALL return value

- Type: Informational
- Contract: Unknown
- Function name: `_function_0xe11f493e`
- PC address: 858

### Description

This contract executes a message call to to another contract. Make sure that the called contract is trusted and does not execute user-supplied code.

## State change after external call

- Type: Warning
- Contract: Unknown
- Function name: `_function_0xe11f493e`
- PC address: 869
- Function name: `_function_0x5a6814ec`
- PC address: 661

### 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.
The return value of an external call is not checked. Note that execution continue even if the called contract throws.

## Message call to external contract

- Type: Warning
- Contract: Unknown
- Function name: `_function_0xe1d10f79`
- PC address: 912
- Function name: `_function_0xd24b08cc`
- PC address: 779

### Description

This contract executes a message call to an address provided as a function argument. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.
This contract executes a message call to an address found at storage slot 1. This storage slot can be written to by calling the function `_function_0x2776b163`. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.

## Transaction order dependence

Expand All @@ -70,23 +48,23 @@ A possible transaction order independence vulnerability exists in function _func

- Type: Informational
- Contract: Unknown
- Function name: `_function_0x5a6814ec`
- PC address: 661
- Function name: `_function_0xd24b08cc`
- PC address: 779

### Description

The return value of an external call is not checked. Note that execution continue even if the called contract throws.

## Unchecked CALL return value
## Message call to external contract

- Type: Informational
- Contract: Unknown
- Function name: `_function_0xd24b08cc`
- PC address: 779
- Function name: `_function_0xe11f493e`
- PC address: 858

### Description

The return value of an external call is not checked. Note that execution continue even if the called contract throws.
This contract executes a message call to to another contract. Make sure that the called contract is trusted and does not execute user-supplied code.

## Unchecked CALL return value

Expand All @@ -99,6 +77,28 @@ The return value of an external call is not checked. Note that execution continu

The return value of an external call is not checked. Note that execution continue even if the called contract throws.

## State change after external call

- Type: Warning
- Contract: Unknown
- Function name: `_function_0xe11f493e`
- PC address: 869

### 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.

## Message call to external contract

- Type: Warning
- Contract: Unknown
- Function name: `_function_0xe1d10f79`
- PC address: 912

### Description

This contract executes a message call to an address provided as a function argument. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.

## Unchecked CALL return value

- Type: Informational
Expand Down
58 changes: 29 additions & 29 deletions tests/testdata/outputs_expected/calls.sol.o.text
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,20 @@ PC address: 661
This contract executes a message call to to another contract. Make sure that the called contract is trusted and does not execute user-supplied code.
--------------------

==== Message call to external contract ====
Type: Warning
Contract: Unknown
Function name: _function_0xd24b08cc
PC address: 779
This contract executes a message call to an address found at storage slot 1. This storage slot can be written to by calling the function `_function_0x2776b163`. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.
--------------------

==== Message call to external contract ====
==== Unchecked CALL return value ====
Type: Informational
Contract: Unknown
Function name: _function_0xe11f493e
PC address: 858
This contract executes a message call to to another contract. Make sure that the called contract is trusted and does not execute user-supplied code.
--------------------

==== State change after external call ====
Type: Warning
Contract: Unknown
Function name: _function_0xe11f493e
PC address: 869
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.
Function name: _function_0x5a6814ec
PC address: 661
The return value of an external call is not checked. Note that execution continue even if the called contract throws.
--------------------

==== Message call to external contract ====
Type: Warning
Contract: Unknown
Function name: _function_0xe1d10f79
PC address: 912
This contract executes a message call to an address provided as a function argument. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.
Function name: _function_0xd24b08cc
PC address: 779
This contract executes a message call to an address found at storage slot 1. This storage slot can be written to by calling the function `_function_0x2776b163`. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.
--------------------

==== Transaction order dependence ====
Expand All @@ -49,17 +33,17 @@ A possible transaction order independence vulnerability exists in function _func
==== Unchecked CALL return value ====
Type: Informational
Contract: Unknown
Function name: _function_0x5a6814ec
PC address: 661
Function name: _function_0xd24b08cc
PC address: 779
The return value of an external call is not checked. Note that execution continue even if the called contract throws.
--------------------

==== Unchecked CALL return value ====
==== Message call to external contract ====
Type: Informational
Contract: Unknown
Function name: _function_0xd24b08cc
PC address: 779
The return value of an external call is not checked. Note that execution continue even if the called contract throws.
Function name: _function_0xe11f493e
PC address: 858
This contract executes a message call to to another contract. Make sure that the called contract is trusted and does not execute user-supplied code.
--------------------

==== Unchecked CALL return value ====
Expand All @@ -70,6 +54,22 @@ PC address: 858
The return value of an external call is not checked. Note that execution continue even if the called contract throws.
--------------------

==== State change after external call ====
Type: Warning
Contract: Unknown
Function name: _function_0xe11f493e
PC address: 869
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.
--------------------

==== Message call to external contract ====
Type: Warning
Contract: Unknown
Function name: _function_0xe1d10f79
PC address: 912
This contract executes a message call to an address provided as a function argument. Generally, it is not recommended to call user-supplied addresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.
--------------------

==== Unchecked CALL return value ====
Type: Informational
Contract: Unknown
Expand Down
Loading

0 comments on commit f5a46ef

Please sign in to comment.