Merge branch 'master' into ci/cd

pull/229/head
Dr. Sergey Pogodin 7 years ago
commit 284545ea7d
  1. 3
      .editorconfig
  2. 1
      Pipfile
  3. 22
      Pipfile.lock
  4. 12
      README.md
  5. 6
      mythril/analysis/callgraph.py
  6. 86
      mythril/analysis/report.py
  7. 0
      mythril/analysis/templates/callgraph.html
  8. 37
      mythril/analysis/templates/report_as_markdown.jinja2
  9. 29
      mythril/analysis/templates/report_as_text.jinja2
  10. 4
      mythril/interfaces/cli.py
  11. 2
      mythril/support/truffle.py
  12. 1
      requirements.txt
  13. 7
      setup.py
  14. 12
      tests/report_test.py
  15. 2
      tests/testdata/outputs_expected/calls.sol.o.markdown
  16. 2
      tests/testdata/outputs_expected/ether_send.sol.o.markdown
  17. 8
      tests/testdata/outputs_expected/exceptions.sol.o.markdown
  18. 2
      tests/testdata/outputs_expected/kinds_of_calls.sol.o.markdown
  19. 2
      tests/testdata/outputs_expected/metacoin.sol.o.markdown
  20. 2
      tests/testdata/outputs_expected/multi_contracts.sol.markdown
  21. 2
      tests/testdata/outputs_expected/multi_contracts.sol.o.markdown
  22. 2
      tests/testdata/outputs_expected/origin.sol.o.markdown
  23. 2
      tests/testdata/outputs_expected/overflow.sol.markdown
  24. 2
      tests/testdata/outputs_expected/overflow.sol.o.markdown
  25. 2
      tests/testdata/outputs_expected/returnvalue.sol.o.markdown
  26. 2
      tests/testdata/outputs_expected/suicide.sol.markdown
  27. 2
      tests/testdata/outputs_expected/suicide.sol.o.markdown
  28. 2
      tests/testdata/outputs_expected/underflow.sol.markdown
  29. 2
      tests/testdata/outputs_expected/underflow.sol.o.markdown

@ -8,3 +8,6 @@ insert_final_newline = true
indent_style = space
indent_size = 4
charset = utf-8
[*.jinja2]
insert_final_newline = false

@ -17,7 +17,6 @@ eth-account = "*"
eth-tester = "*"
laser-ethereum = ">=0.5.20"
"jinja2" = "*"
attrs = ">=17.0.0"
rlp = "<1.0.0"
[dev-packages]
pylint = "*"

22
Pipfile.lock generated

@ -23,20 +23,6 @@
],
"version": "==0.24.0"
},
"attrdict": {
"hashes": [
"sha256:86aeb6d3809e0344409f8148d7cac9eabce5f0b577c160b5e90d10df3f8d2ad3"
],
"version": "==2.0.0"
},
"attrs": {
"hashes": [
"sha256:1c7960ccfd6a005cd9f7ba884e6316b5e430a3f1a6c37c5f87d8b43f83b54ec9",
"sha256:a17a9573a6f475c99b551c0e0a812707ddda1ec9653bed04c13841404ed6f450"
],
"index": "pypi",
"version": "==17.4.0"
},
"btrees": {
"hashes": [
"sha256:46b02cb69b26a5238db771ea1955b503df73ecf254bb8063af4c61999fc75b5c",
@ -595,14 +581,6 @@
],
"version": "==1.6.3"
},
"attrs": {
"hashes": [
"sha256:1c7960ccfd6a005cd9f7ba884e6316b5e430a3f1a6c37c5f87d8b43f83b54ec9",
"sha256:a17a9573a6f475c99b551c0e0a812707ddda1ec9653bed04c13841404ed6f450"
],
"index": "pypi",
"version": "==17.4.0"
},
"isort": {
"hashes": [
"sha256:1153601da39a25b14ddc54955dbbacbb6b2d19135386699e2ad58517953b34af",

@ -33,11 +33,11 @@ Documentation has moved to the [Wiki page](https://github.com/ConsenSys/mythril/
- [HITBSecConf 2018 conference paper](https://github.com/b-mueller/smashing-smart-contracts/blob/master/smashing-smart-contracts-1of1.pdf)
- [EDCon Toronto 2018 - Mythril: Find bugs and verify security properties in your contracts](https://www.youtube.com/watch?v=NJ9StJThxZY&feature=youtu.be&t=3h3m18s)
## Mythril is Hiring
## Acknowledgements
[ConsenSys Diligence](https://consensys.net/diligence/) is building a dedicated Mythril team. If you're a coder and/or Ethereum security enthusiast who wants to do interesting and challenging work for a decentralized organization, check out the open positions below. Feel free to apply though the form on the careers website. or ping [b-mueller](http://github.com/b-mueller/) on [Gitter](https://gitter.im/ConsenSys/mythril).
- JSON RPC library is adapted from [ethjsonrpc](https://github.com/ConsenSys/ethjsonrpc) (it doesn't seem to be maintained anymore, and I needed to make some changes to it).
- The signature data in `signatures.json` was initially obtained from the [Ethereum Function Signature Database](https://www.4byte.directory).
- Many features, bugfixes and analysis modules have been added by [contributors](https://github.com/b-mueller/mythril/graphs/contributors).
- [Developer - Security Analysis Tools](https://new.consensys.net/careers/?gh_jid=1129067)
- [Developer - Security Analysis Tools (Part Time)](https://new.consensys.net/careers/?gh_jid=1129048)
- [Lead Developer - Security Analysis Engine](https://new.consensys.net/careers/?gh_jid=1127291)
- [Lead Software Engineer - Auditor Security Tools](https://new.consensys.net/careers/?gh_jid=1127282)

@ -105,8 +105,8 @@ def extract_nodes(statespace, color_map):
code_line = re.sub("([0-9a-f]{8})[0-9a-f]+", lambda m: m.group(1) + "(...)", code_line)
code_split.append(code_line)
truncated_code = '\n'.join(code_split) if (len(code_split) < 7) else '\n'.join(
code_split[:6]) + "\n(click to expand +)"
truncated_code = '\n'.join(code_split) if (len(code_split) < 7) \
else '\n'.join(code_split[:6]) + "\n(click to expand +)"
nodes.append({
'id': str(node_key),
@ -145,7 +145,7 @@ def extract_edges(statespace):
def generate_graph(statespace, title="Mythril / Ethereum LASER Symbolic VM", physics=False, phrackify=False):
env = Environment(loader=PackageLoader('mythril.analysis'), autoescape=select_autoescape(['html', 'xml']))
template = env.get_template('graph.html')
template = env.get_template('callgraph.html')
graph_opts = default_opts
accounts = statespace.accounts

@ -1,14 +1,15 @@
import hashlib
import json
from jinja2 import PackageLoader, Environment
class Issue:
def __init__(self, contract, function, pc, title, _type="Informational", description="", debug=""):
def __init__(self, contract, function, address, title, _type="Informational", description="", debug=""):
self.title = title
self.contract = contract
self.function = function
self.pc = pc
self.address = address
self.description = description
self.type = _type
self.debug = debug
@ -18,7 +19,7 @@ class Issue:
def as_dict(self):
issue = {'title': self.title, 'description':self.description, 'function': self.function, 'type': self.type, 'address': self.pc, 'debug': self.debug}
issue = {'title': self.title, 'description':self.description, 'function': self.function, 'type': self.type, 'address': self.address, 'debug': self.debug}
if self.filename and self.lineno:
issue['filename'] = self.filename
@ -30,13 +31,14 @@ class Issue:
return issue
def add_code_info(self, contract):
if self.pc:
codeinfo = contract.get_source_info(self.pc)
if self.address:
codeinfo = contract.get_source_info(self.address)
self.filename = codeinfo.filename
self.code = codeinfo.code
self.lineno = codeinfo.lineno
class Report:
environment = Environment(loader=PackageLoader('mythril.analysis'), trim_blocks=True)
def __init__(self, verbose=False):
self.issues = {}
@ -45,76 +47,24 @@ class Report:
def append_issue(self, issue):
m = hashlib.md5()
m.update((issue.contract + str(issue.pc) + issue.title).encode('utf-8'))
m.update((issue.contract + str(issue.address) + issue.title).encode('utf-8'))
self.issues[m.digest()] = issue
def as_text(self):
text = ""
for key, issue in self.issues.items():
text += "==== " + issue.title + " ====\n"
text += "Type: " + issue.type + "\n"
if len(issue.contract):
text += "Contract: " + issue.contract + "\n"
else:
text += "Contract: Unknown\n"
text += "Function name: " + issue.function + "\n"
text += "PC address: " + str(issue.pc) + "\n"
text += issue.description + "\n--------------------\n"
if issue.filename and issue.lineno:
text += "In file: " + issue.filename + ":" + str(issue.lineno)
if issue.code:
text += "\n\n" + issue.code + "\n\n--------------------\n"
if self.verbose and issue.debug:
text += "\nDEBUGGING INFORMATION:\n\n" + issue.debug + "\n--------------------\n"
text += "\n"
return text
filename = self._file_name()
template = Report.environment.get_template('report_as_text.jinja2')
return template.render(filename=filename, issues=self.issues, verbose=self.verbose)
def as_json(self):
issues = []
for key, issue in self.issues.items():
issues.append(issue.as_dict())
issues = [issue.as_dict() for key, issue in self.issues.items()]
result = {'success': True, 'error': None, 'issues': issues}
return json.dumps(result)
def as_markdown(self):
text = ""
for key, issue in self.issues.items():
if text == "":
if (issue.filename):
text += "# Analysis results for " + issue.filename
text += "\n\n## " + issue.title + "\n\n"
text += "- Type: " + issue.type + "\n"
if len(issue.contract):
text += "- Contract: " + issue.contract + "\n"
else:
text += "- Contract: Unknown\n"
text += "- Function name: `" + issue.function + "`\n"
text += "- PC address: " + str(issue.pc) + "\n\n"
text += "### Description\n\n" + issue.description
if issue.filename and issue.lineno:
text += "\nIn *%s:%d*\n" % (issue.filename, issue.lineno)
if issue.code:
text += "\n```\n" + issue.code + "\n```"
if self.verbose and issue.debug:
text += "\n\n### Debugging Information\n" + issue.debug
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 text
def _file_name(self):
if len(self.issues.values()) > 0:
return list(self.issues.values())[0].filename

@ -0,0 +1,37 @@
# Analysis results for {{ filename }}
{% if issues %}
{% for key, issue in issues.items() %}
## {{ issue.title }}
- Type: {{ issue.type }}
- Contract: {{ issue.contract | default("Unknown") }}
- Function name: `{{ issue.function }}`
- PC address: {{ issue.address }}
### Description
{{ issue.description.rstrip() }}
{% if issue.filename and issue.lineno %}
In file: {{ issue.filename }}:{{ issue.lineno }}
{% endif %}
{% if issue.code %}
### Code
```
{{ issue.code }}
```
{% endif %}
{% if verbose and issue.debug %}
--------------------
### Debugging Information:
{{ issue.debug }}
{% endif %}
{% endfor %}
{% else %}
The analysis was completed successfully. No issues were detected.
{% endif %}

@ -0,0 +1,29 @@
{% if issues %}
{% for key, issue in issues.items() %}
==== {{ issue.title }} ====
Type: {{ issue.type }}
Contract: {{ issue.contract | default("Unknown") }}
Function name: {{ issue.function }}
PC address: {{ issue.address }}
{{ issue.description }}
--------------------
{% if issue.filename and issue.lineno %}
In file: {{ issue.filename }}:{{ issue.lineno }}
{% endif %}
{% if issue.code %}
{{ issue.code }}
--------------------
{% endif %}
{% if verbose and issue.debug %}
--------------------
DEBUGGING INFORMATION:
{{ issue.debug }}
{% endif %}
{% endfor %}
{% else %}
The analysis was completed successfully. No issues were detected.
{% endif %}

@ -202,8 +202,8 @@ def main():
max_depth=args.max_depth)
outputs = {
'json': report.as_json(),
'text': report.as_text() or "The analysis was completed successfully. No issues were detected.",
'markdown': report.as_markdown() or "The analysis was completed successfully. No issues were detected."
'text': report.as_text(),
'markdown': report.as_markdown()
}
print(outputs[args.outform])

@ -80,7 +80,7 @@ def analyze_truffle_project(args):
for issue in issues:
index = helper.get_instruction_index(disassembly.instruction_list, issue.pc)
index = helper.get_instruction_index(disassembly.instruction_list, issue.address)
if index:
try:

@ -18,5 +18,4 @@ eth-rlp>=0.1.0
eth-tester>=0.1.0b21
coverage
jinja2
attrs
pytest

@ -323,7 +323,6 @@ setup(
'eth-tester>=0.1.0b21',
'coverage',
'jinja2',
'attrs',
'rlp<1.0.0'
],
@ -331,7 +330,11 @@ setup(
extras_require={
},
package_data={
'mythril.analysis': ['templates/*']
},
include_package_data=True,
scripts=['myth'],

@ -44,12 +44,12 @@ def reports():
return results
def _assert_empty(changed_files):
def _assert_empty(changed_files, postfix):
""" Asserts there are no changed files and otherwise builds error message"""
message = ""
for input_file in changed_files:
output_expected = (TESTDATA_OUTPUTS_EXPECTED / (input_file.name + ".json")).read_text().splitlines(1)
output_current = (TESTDATA_OUTPUTS_CURRENT / (input_file.name + ".json")).read_text().splitlines(1)
output_expected = (TESTDATA_OUTPUTS_EXPECTED / (input_file.name + postfix)).read_text().splitlines(1)
output_current = (TESTDATA_OUTPUTS_CURRENT / (input_file.name + postfix)).read_text().splitlines(1)
difference = ''.join(difflib.unified_diff(output_expected, output_current))
message += "Found differing file for input: {} \n Difference: \n {} \n".format(str(input_file), str(difference))
@ -75,12 +75,12 @@ def _get_changed_files(postfix, report_builder, reports):
def test_json_report(reports):
_assert_empty(_get_changed_files('.json', lambda report: _fix_path(_fix_debug_data(report.as_json())).strip(), reports))
_assert_empty(_get_changed_files('.json', lambda report: _fix_path(_fix_debug_data(report.as_json())).strip(), reports), '.json')
def test_markdown_report(reports):
_assert_empty(_get_changed_files('.markdown', lambda report: _fix_path(report.as_markdown()), reports))
_assert_empty(_get_changed_files('.markdown', lambda report: _fix_path(report.as_markdown()), reports), '.markdown')
def test_text_report(reports):
_assert_empty(_get_changed_files('.text', lambda report: _fix_path(report.as_text()), reports))
_assert_empty(_get_changed_files('.text', lambda report: _fix_path(report.as_text()), reports), '.text')

@ -108,4 +108,4 @@ The return value of an external call is not checked. Note that execution continu
### Description
The return value of an external call is not checked. Note that execution continue even if the called contract throws.
The return value of an external call is not checked. Note that execution continue even if the called contract throws.

@ -23,4 +23,4 @@ There is a check on storage index 1. This storage slot can be written to by call
### Description
A possible integer overflow exists in the function `invest()`.
The addition or multiplication may result in a value higher than the maximum representable integer.
The addition or multiplication may result in a value higher than the maximum representable integer.

@ -9,7 +9,7 @@
### Description
A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. This is acceptable in most situations. Note however that `assert()` should only be used to check invariants. Use `require()` for regular input checking.
A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. This is acceptable in most situations. Note however that `assert()` should only be used to check invariants. Use `require()` for regular input checking.
## Exception state
@ -20,7 +20,7 @@ A reachable exception (opcode 0xfe) has been detected. This can be caused by typ
### Description
A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. This is acceptable in most situations. Note however that `assert()` should only be used to check invariants. Use `require()` for regular input checking.
A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. This is acceptable in most situations. Note however that `assert()` should only be used to check invariants. Use `require()` for regular input checking.
## Exception state
@ -31,7 +31,7 @@ A reachable exception (opcode 0xfe) has been detected. This can be caused by typ
### Description
A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. This is acceptable in most situations. Note however that `assert()` should only be used to check invariants. Use `require()` for regular input checking.
A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. This is acceptable in most situations. Note however that `assert()` should only be used to check invariants. Use `require()` for regular input checking.
## Exception state
@ -42,4 +42,4 @@ A reachable exception (opcode 0xfe) has been detected. This can be caused by typ
### Description
A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. This is acceptable in most situations. Note however that `assert()` should only be used to check invariants. Use `require()` for regular input checking.
A reachable exception (opcode 0xfe) has been detected. This can be caused by type errors, division by zero, out-of-bounds array access, or assert violations. This is acceptable in most situations. Note however that `assert()` should only be used to check invariants. Use `require()` for regular input checking.

@ -20,4 +20,4 @@ This contract executes a message call to an address provided as a function argum
### Description
The return value of an external call is not checked. Note that execution continue even if the called contract throws.
The return value of an external call is not checked. Note that execution continue even if the called contract throws.

@ -10,4 +10,4 @@
### Description
A possible integer overflow exists in the function `sendToken(address,uint256)`.
The addition or multiplication may result in a value higher than the maximum representable integer.
The addition or multiplication may result in a value higher than the maximum representable integer.

@ -15,4 +15,4 @@ In *<TESTDATA>/inputs/multi_contracts.sol:14*
```
msg.sender.transfer(2 ether)
```
```

@ -10,4 +10,4 @@
### Description
In the function `_function_0x8a4068dd` a non-zero amount of Ether is sent to msg.sender.
It seems that this function can be called without restrictions.
It seems that this function can be called without restrictions.

@ -10,4 +10,4 @@
### Description
Function transferOwnership(address) retrieves the transaction origin (tx.origin) using the ORIGIN opcode. Use tx.sender instead.
See also: https://solidity.readthedocs.io/en/develop/security-considerations.html#tx-origin
See also: https://solidity.readthedocs.io/en/develop/security-considerations.html#tx-origin

@ -49,4 +49,4 @@ In *<TESTDATA>/inputs/overflow.sol:11*
```
balances[msg.sender] - _value
```
```

@ -34,4 +34,4 @@ The addition or multiplication may result in a value higher than the maximum rep
### Description
A possible integer underflow exists in the function `sendeth(address,uint256)`.
The subtraction may result in a value < 0.
The subtraction may result in a value < 0.

@ -31,4 +31,4 @@ This contract executes a message call to to another contract. Make sure that the
### Description
The return value of an external call is not checked. Note that execution continue even if the called contract throws.
The return value of an external call is not checked. Note that execution continue even if the called contract throws.

@ -16,4 +16,4 @@ In *<TESTDATA>/inputs/suicide.sol:4*
```
selfdestruct(addr)
```
```

@ -11,4 +11,4 @@
The function `_function_0xcbf0b0c0` executes the SUICIDE instruction. The remaining Ether is sent to an address provided as a function argument.
It seems that this function can be called without restrictions.
It seems that this function can be called without restrictions.

@ -49,4 +49,4 @@ In *<TESTDATA>/inputs/underflow.sol:11*
```
balances[msg.sender] - _value
```
```

@ -34,4 +34,4 @@ The addition or multiplication may result in a value higher than the maximum rep
### Description
A possible integer underflow exists in the function `sendeth(address,uint256)`.
The subtraction may result in a value < 0.
The subtraction may result in a value < 0.

Loading…
Cancel
Save