F2F: Improve formatting of analysis reports

Submission #253393 by diazz to the challenge
https://www.topcoder.com/challenges/30064536
pull/135/head
Dr. Sergey Pogodin 7 years ago
parent 509ba09936
commit 9f6caa50ff
  1. 2
      README.md
  2. 2
      mythril/analysis/modules/delegatecall.py
  3. 4
      mythril/analysis/modules/dependence_on_predictable_vars.py
  4. 6
      mythril/analysis/modules/ether_send.py
  5. 2
      mythril/analysis/modules/exceptions.py
  6. 2
      mythril/analysis/modules/external_calls.py
  7. 2
      mythril/analysis/modules/suicide.py
  8. 9
      mythril/analysis/report.py
  9. 9
      mythril/support/truffle.py
  10. 2
      security_checks.md
  11. 4
      static/sample_report.md
  12. 2
      tests/cmd_line_test.py
  13. 2
      tests/testdata/outputs_expected/calls.sol.json
  14. 2
      tests/testdata/outputs_expected/calls.sol.markdown
  15. 2
      tests/testdata/outputs_expected/calls.sol.text
  16. 2
      tests/testdata/outputs_expected/ether_send.sol.json
  17. 4
      tests/testdata/outputs_expected/ether_send.sol.markdown
  18. 4
      tests/testdata/outputs_expected/ether_send.sol.text
  19. 8
      tests/testdata/outputs_expected/exceptions.sol.json
  20. 8
      tests/testdata/outputs_expected/exceptions.sol.markdown
  21. 8
      tests/testdata/outputs_expected/exceptions.sol.text
  22. 2
      tests/testdata/outputs_expected/multi_contracts.sol.json
  23. 2
      tests/testdata/outputs_expected/multi_contracts.sol.markdown
  24. 2
      tests/testdata/outputs_expected/multi_contracts.sol.text
  25. 8
      tests/testdata/outputs_expected/rubixi.sol.json
  26. 20
      tests/testdata/outputs_expected/rubixi.sol.markdown
  27. 20
      tests/testdata/outputs_expected/rubixi.sol.text
  28. 8
      tests/testdata/outputs_expected/weak_random.sol.json
  29. 14
      tests/testdata/outputs_expected/weak_random.sol.markdown
  30. 14
      tests/testdata/outputs_expected/weak_random.sol.text

@ -60,7 +60,7 @@ Function name: withdrawfunds()
PC address: 816 PC address: 816
In the function `withdrawfunds()` a non-zero amount of Ether is sent to msg.sender. In the function `withdrawfunds()` a non-zero amount of Ether is sent to msg.sender.
There is a check on storage index 7. This storage slot can be written to by calling the function 'crowdfunding()'. There is a check on storage index 7. This storage slot can be written to by calling the function `crowdfunding()`.
-------------------- --------------------
In file: solidity_examples/ether_send.sol:18 In file: solidity_examples/ether_send.sol:18

@ -62,7 +62,7 @@ def execute(statespace):
func = statespace.find_storage_write(state.environment.active_account.address, idx) func = statespace.find_storage_write(state.environment.active_account.address, idx)
if (func): if (func):
issue.description = "This contract delegates execution to a contract address in storage slot " + str(idx) + ". This storage slot can be written to by calling the function '" + func + "'. " issue.description = "This contract delegates execution to a contract address in storage slot " + str(idx) + ". This storage slot can be written to by calling the function `" + func + "`. "
else: else:
logging.debug("[DELEGATECALL] No storage writes to index " + str(idx)) logging.debug("[DELEGATECALL] No storage writes to index " + str(idx))

@ -39,7 +39,7 @@ def execute(statespace):
address = call.state.get_current_instruction()['address'] address = call.state.get_current_instruction()['address']
description = "In the function `'" + call.node.function_name + "'` " description = "In the function `" + call.node.function_name + "` "
description += "the following predictable state variables are used to determine Ether recipient:\n" description += "the following predictable state variables are used to determine Ether recipient:\n"
# First check: look for predictable state variables in node & call recipient constraints # First check: look for predictable state variables in node & call recipient constraints
@ -64,7 +64,7 @@ def execute(statespace):
for constraint in call.node.constraints + [call.to]: for constraint in call.node.constraints + [call.to]:
if "blockhash" in str(constraint): if "blockhash" in str(constraint):
description = "In the function `'" + call.node.function_name + "'` " description = "In the function `" + call.node.function_name + "` "
if "number" in str(constraint): if "number" in str(constraint):
m = re.search('blockhash\w+(\s\-\s(\d+))*', str(constraint)) m = re.search('blockhash\w+(\s\-\s(\d+))*', str(constraint))
if m and solve(call): if m and solve(call):

@ -39,7 +39,7 @@ def execute(statespace):
interesting = False interesting = False
description = "In the function `'" + call.node.function_name + "'` " description = "In the function `" + call.node.function_name + "` "
if re.search(r'caller', str(call.to)): if re.search(r'caller', str(call.to)):
description += "a non-zero amount of Ether is sent to msg.sender.\n" description += "a non-zero amount of Ether is sent to msg.sender.\n"
@ -60,7 +60,7 @@ def execute(statespace):
func = statespace.find_storage_write(state.environment.active_account.address, idx) func = statespace.find_storage_write(state.environment.active_account.address, idx)
if (func): if (func):
description += "There is a check on storage index " + str(idx) + ". This storage slot can be written to by calling the function '" + func + "'.\n" description += "There is a check on storage index " + str(idx) + ". This storage slot can be written to by calling the function `" + func + "`.\n"
interesting = True interesting = True
else: else:
logging.debug("[ETHER_SEND] No storage writes to index " + str(idx)) logging.debug("[ETHER_SEND] No storage writes to index " + str(idx))
@ -90,7 +90,7 @@ def execute(statespace):
func = statespace.find_storage_write(state.environment.active_account.address, idx) func = statespace.find_storage_write(state.environment.active_account.address, idx)
if (func): if (func):
description += "\nThere is a check on storage index " + str(idx) + ". This storage slot can be written to by calling the function '" + func + "'." description += "\nThere is a check on storage index " + str(idx) + ". This storage slot can be written to by calling the function `" + func + "`."
else: else:
logging.debug("[ETHER_SEND] No storage writes to index " + str(idx)) logging.debug("[ETHER_SEND] No storage writes to index " + str(idx))
can_solve = False can_solve = False

@ -32,7 +32,7 @@ def execute(statespace):
address = state.get_current_instruction()['address'] address = state.get_current_instruction()['address']
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. " 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. "
description += "This is acceptable in most situations. Note however that assert() should only be used to check invariants. Use require() for regular input checking. " description += "This is acceptable in most situations. Note however that `assert()` should only be used to check invariants. Use `require()` for regular input checking. "
debug = "The exception is triggered under the following conditions:\n\n" debug = "The exception is triggered under the following conditions:\n\n"

@ -85,7 +85,7 @@ def execute(statespace):
description += \ description += \
"an address found at storage slot " + str(idx) + ". " + \ "an address found at storage slot " + str(idx) + ". " + \
"This storage slot can be written to by calling the function '" + func + "'. " "This storage slot can be written to by calling the function `" + func + "`. "
user_supplied = True user_supplied = True
if user_supplied: if user_supplied:

@ -69,7 +69,7 @@ def execute(statespace):
func = statespace.find_storage_write(state.environment.active_account.address, idx) func = statespace.find_storage_write(state.environment.active_account.address, idx)
if func: if func:
description += "\nThere is a check on storage index " + str(idx) + ". This storage index can be written to by calling the function '" + func + "'." description += "\nThere is a check on storage index " + str(idx) + ". This storage index can be written to by calling the function `" + func + "`."
break break
else: else:
logging.debug("[UNCHECKED_SUICIDE] No storage writes to index " + str(idx)) logging.debug("[UNCHECKED_SUICIDE] No storage writes to index " + str(idx))

@ -1,7 +1,6 @@
import hashlib import hashlib
import json import json
class Issue: class Issue:
def __init__(self, contract, function, pc, title, _type="Informational", description="", debug=""): def __init__(self, contract, function, pc, title, _type="Informational", description="", debug=""):
@ -92,6 +91,8 @@ class Report:
for key, issue in self.issues.items(): for key, issue in self.issues.items():
if text == "":
text += "# Analysis results for " + issue.filename
text += "\n\n## " + issue.title + "\n\n" text += "\n\n## " + issue.title + "\n\n"
text += "- Type: " + issue.type + "\n" text += "- Type: " + issue.type + "\n"
@ -103,15 +104,15 @@ class Report:
text += "- Function name: `" + issue.function + "`\n" text += "- Function name: `" + issue.function + "`\n"
text += "- PC address: " + str(issue.pc) + "\n\n" text += "- PC address: " + str(issue.pc) + "\n\n"
text += "\n\n### Description\n\n" + issue.description + "\n" text += "### Description\n\n" + issue.description
if issue.filename and issue.lineno: if issue.filename and issue.lineno:
text += "\nIn *%s:%d*\n" % (issue.filename, issue.lineno) text += "\nIn *%s:%d*\n" % (issue.filename, issue.lineno)
if issue.code: if issue.code:
text += "\n```\n" + issue.code + "\n```\n" text += "\n```\n" + issue.code + "\n```"
if self.verbose and issue.debug: if self.verbose and issue.debug:
text += "### Debugging Information\n" + issue.debug + "\n" text += "\n\n### Debugging Information\n" + issue.debug
return text return text

@ -21,6 +21,8 @@ def analyze_truffle_project(args):
files = os.listdir(build_dir) files = os.listdir(build_dir)
isFirstTime: bool = True
for filename in files: for filename in files:
if re.match(r'.*\.json$', filename) and filename != "Migrations.json": if re.match(r'.*\.json$', filename) and filename != "Migrations.json":
@ -46,7 +48,7 @@ def analyze_truffle_project(args):
if not len(issues): if not len(issues):
if (args.outform == 'text' or args.outform == 'markdown'): if (args.outform == 'text' or args.outform == 'markdown'):
print("\n\n# Analysis result for " + name + "\n\nNo issues found.") print("# Analysis result for " + name + "\n\nNo issues found.")
else: else:
result = {'contract': name, 'result': {'success': True, 'error': None, 'issues': []}} result = {'contract': name, 'result': {'success': True, 'error': None, 'issues': []}}
print(json.dumps(result)) print(json.dumps(result))
@ -101,8 +103,7 @@ def analyze_truffle_project(args):
print(json.dumps(result)) print(json.dumps(result))
else: else:
if (args.outform == 'text'): if (args.outform == 'text'):
print("\n\n# Analysis result for " + name + ":\n\n" + report.as_text()) print("# Analysis result for " + name + ":\n\n" + report.as_text())
elif (args.outform == 'markdown'): elif (args.outform == 'markdown'):
print("\n\n# Analysis result for " + name + "\n\n" + report.as_markdown()) print(report.as_markdown())

@ -19,7 +19,7 @@ Detection modules, ideas collection and wish list. Contributions are welcome!
|Transaction order dependence| | | [Front Running](https://consensys.github.io/smart-contract-best-practices/known_attacks/#transaction-ordering-dependence-tod-front-running) | |Transaction order dependence| | | [Front Running](https://consensys.github.io/smart-contract-best-practices/known_attacks/#transaction-ordering-dependence-tod-front-running) |
|Information exposure| | | | |Information exposure| | | |
|Complex fallback function (uses more than 2,300 gas) | A too complex fallback function will cause send() and transfer() from other contracts to fail. To implement this we first need to fully implement gas simulation. | | |Complex fallback function (uses more than 2,300 gas) | A too complex fallback function will cause send() and transfer() from other contracts to fail. To implement this we first need to fully implement gas simulation. | |
|Use require() instead of assert() | Use `assert()` only to check against states which should be completely unreachable. | [Exceptions](mythril/analysis/modules/exceptions.py) | [Solidity docs](https://solidity.readthedocs.io/en/develop/control-structures.html#error-handling-assert-require-revert-and-exceptions)| |Use `require()` instead of `assert()` | Use `assert()` only to check against states which should be completely unreachable. | [Exceptions](mythril/analysis/modules/exceptions.py) | [Solidity docs](https://solidity.readthedocs.io/en/develop/control-structures.html#error-handling-assert-require-revert-and-exceptions)|
|Use of depreciated functions | Use `revert()` instead of `throw()`, `selfdestruct()` instead of `suicide()`, `keccak256()` instead of `sha3()` | | | |Use of depreciated functions | Use `revert()` instead of `throw()`, `selfdestruct()` instead of `suicide()`, `keccak256()` instead of `sha3()` | | |
|Detect tautologies| Detect comparisons that always evaluate to 'true', see also [#54](https://github.com/ConsenSys/mythril/issues/54) | | |Detect tautologies| Detect comparisons that always evaluate to 'true', see also [#54](https://github.com/ConsenSys/mythril/issues/54) | |
|Call depth attack| Depreciated | | [EIP 150 Hard Fork](https://consensys.github.io/smart-contract-best-practices/known_attacks/#call-depth-attack-deprecated)| |Call depth attack| Depreciated | | [EIP 150 Hard Fork](https://consensys.github.io/smart-contract-best-practices/known_attacks/#call-depth-attack-deprecated)|

@ -9,7 +9,7 @@
In the function `withdrawfunds()` a non-zero amount of Ether is sent to msg.sender. In the function `withdrawfunds()` a non-zero amount of Ether is sent to msg.sender.
Call value is balance_at_1461501637330902918203684832716283019655932542975 & address. Call value is balance_at_1461501637330902918203684832716283019655932542975 & address.
There is a check on storage index 7. This storage slot can be written to by calling the function 'crowdfunding()'. There is a check on storage index 7. This storage slot can be written to by calling the function `crowdfunding()`.
In *ether_send.sol:* In *ether_send.sol:*
@ -97,7 +97,7 @@ balances[msg.sender] - _value
- PC address: 1285 - PC address: 1285
### Description ### Description
In the function `'_function_0xe9874106'` the following predictable state variables are used to determine Ether recipient: In the function `_function_0xe9874106` the following predictable state variables are used to determine Ether recipient:
- block.coinbase - block.coinbase

@ -26,7 +26,7 @@ class TruffleTestCase(BaseTestCase):
def test_analysis_truffle_project(self): def test_analysis_truffle_project(self):
truffle_project_root = str(TESTS_DIR / "truffle_project") truffle_project_root = str(TESTS_DIR / "truffle_project")
command = "cd {}; truffle compile; python3 {} --truffle".format(truffle_project_root, MYTH) command = "cd {}; truffle compile; python3 {} --truffle".format(truffle_project_root, MYTH)
self.assertIn("In the function `'withdrawfunds()'` a non-zero amount of Ether is sent to msg.sender.", output_of(command)) self.assertIn("In the function `withdrawfunds()` a non-zero amount of Ether is sent to msg.sender.", output_of(command))
class InfuraTestCase(BaseTestCase): class InfuraTestCase(BaseTestCase):

@ -15,7 +15,7 @@
}, },
{ {
"title": "Message call to external contract", "title": "Message call to external contract",
"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 adresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.", "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 adresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.",
"function": "_function_0xd24b08cc", "function": "_function_0xd24b08cc",
"type": "Warning", "type": "Warning",
"address": 779, "address": 779,

@ -20,7 +20,7 @@ fixed_address.call()
- PC address: 779 - PC address: 779
### Description ### 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 adresses 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 adresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.
In *<TESTDATA>/inputs/calls.sol:29* In *<TESTDATA>/inputs/calls.sol:29*

@ -16,7 +16,7 @@ Type: Warning
Contract: Caller Contract: Caller
Function name: _function_0xd24b08cc Function name: _function_0xd24b08cc
PC address: 779 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 adresses 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 adresses using Solidity's call() construct. Note that attackers might leverage reentrancy attacks to exploit race conditions or manipulate this contract's state.
-------------------- --------------------
In file: <TESTDATA>/inputs/calls.sol:29 In file: <TESTDATA>/inputs/calls.sol:29

@ -4,7 +4,7 @@
"issues": [ "issues": [
{ {
"title": "Ether send", "title": "Ether send",
"description": "In the function `'withdrawfunds()'` a non-zero amount of Ether is sent to msg.sender.\n\nThere is a check on storage index 1. This storage slot can be written to by calling the function 'crowdfunding()'.", "description": "In the function `withdrawfunds()` a non-zero amount of Ether is sent to msg.sender.\n\nThere is a check on storage index 1. This storage slot can be written to by calling the function `crowdfunding()`.",
"function": "withdrawfunds()", "function": "withdrawfunds()",
"type": "Warning", "type": "Warning",
"address": 816, "address": 816,

@ -6,9 +6,9 @@
- PC address: 816 - PC address: 816
### Description ### Description
In the function 'withdrawfunds()' a non-zero amount of Ether is sent to msg.sender. In the function `withdrawfunds()` a non-zero amount of Ether is sent to msg.sender.
There is a check on storage index 1. This storage slot can be written to by calling the function 'crowdfunding()'. There is a check on storage index 1. This storage slot can be written to by calling the function `crowdfunding()`.
In *<TESTDATA>/inputs/ether_send.sol:18* In *<TESTDATA>/inputs/ether_send.sol:18*

@ -3,9 +3,9 @@ Type: Warning
Contract: Crowdfunding Contract: Crowdfunding
Function name: withdrawfunds() Function name: withdrawfunds()
PC address: 816 PC address: 816
In the function `'withdrawfunds()'` a non-zero amount of Ether is sent to msg.sender. In the function `withdrawfunds()` a non-zero amount of Ether is sent to msg.sender.
There is a check on storage index 1. This storage slot can be written to by calling the function 'crowdfunding()'. There is a check on storage index 1. This storage slot can be written to by calling the function `crowdfunding()`.
-------------------- --------------------
In file: <TESTDATA>/inputs/ether_send.sol:18 In file: <TESTDATA>/inputs/ether_send.sol:18

@ -4,7 +4,7 @@
"issues": [ "issues": [
{ {
"title": "Exception state", "title": "Exception state",
"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. ", "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. ",
"function": "_function_0x546455b5", "function": "_function_0x546455b5",
"type": "Informational", "type": "Informational",
"address": 446, "address": 446,
@ -15,7 +15,7 @@
}, },
{ {
"title": "Exception state", "title": "Exception state",
"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. ", "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. ",
"function": "_function_0x92dd38ea", "function": "_function_0x92dd38ea",
"type": "Informational", "type": "Informational",
"address": 484, "address": 484,
@ -26,7 +26,7 @@
}, },
{ {
"title": "Exception state", "title": "Exception state",
"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. ", "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. ",
"function": "_function_0xa08299f1", "function": "_function_0xa08299f1",
"type": "Informational", "type": "Informational",
"address": 506, "address": 506,
@ -37,7 +37,7 @@
}, },
{ {
"title": "Exception state", "title": "Exception state",
"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. ", "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. ",
"function": "_function_0xb34c3610", "function": "_function_0xb34c3610",
"type": "Informational", "type": "Informational",
"address": 531, "address": 531,

@ -6,7 +6,7 @@
- PC address: 446 - PC address: 446
### Description ### 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.
In *<TESTDATA>/inputs/exceptions.sol:16* In *<TESTDATA>/inputs/exceptions.sol:16*
@ -20,7 +20,7 @@ assert(input != 23)
- PC address: 484 - PC address: 484
### Description ### 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.
In *<TESTDATA>/inputs/exceptions.sol:34* In *<TESTDATA>/inputs/exceptions.sol:34*
@ -34,7 +34,7 @@ myarray[index]
- PC address: 506 - PC address: 506
### Description ### 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.
In *<TESTDATA>/inputs/exceptions.sol:24* In *<TESTDATA>/inputs/exceptions.sol:24*
@ -48,7 +48,7 @@ In *<TESTDATA>/inputs/exceptions.sol:24*
- PC address: 531 - PC address: 531
### Description ### 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.
In *<TESTDATA>/inputs/exceptions.sol:7* In *<TESTDATA>/inputs/exceptions.sol:7*

@ -3,7 +3,7 @@ Type: Informational
Contract: Exceptions Contract: Exceptions
Function name: _function_0x546455b5 Function name: _function_0x546455b5
PC address: 446 PC address: 446
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.
-------------------- --------------------
In file: <TESTDATA>/inputs/exceptions.sol:16 In file: <TESTDATA>/inputs/exceptions.sol:16
@ -16,7 +16,7 @@ Type: Informational
Contract: Exceptions Contract: Exceptions
Function name: _function_0x92dd38ea Function name: _function_0x92dd38ea
PC address: 484 PC address: 484
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.
-------------------- --------------------
In file: <TESTDATA>/inputs/exceptions.sol:34 In file: <TESTDATA>/inputs/exceptions.sol:34
@ -29,7 +29,7 @@ Type: Informational
Contract: Exceptions Contract: Exceptions
Function name: _function_0xa08299f1 Function name: _function_0xa08299f1
PC address: 506 PC address: 506
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.
-------------------- --------------------
In file: <TESTDATA>/inputs/exceptions.sol:24 In file: <TESTDATA>/inputs/exceptions.sol:24
@ -42,7 +42,7 @@ Type: Informational
Contract: Exceptions Contract: Exceptions
Function name: _function_0xb34c3610 Function name: _function_0xb34c3610
PC address: 531 PC address: 531
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.
-------------------- --------------------
In file: <TESTDATA>/inputs/exceptions.sol:7 In file: <TESTDATA>/inputs/exceptions.sol:7

@ -4,7 +4,7 @@
"issues": [ "issues": [
{ {
"title": "Ether send", "title": "Ether send",
"description": "In the function `'_function_0x8a4068dd'` a non-zero amount of Ether is sent to msg.sender.\nIt seems that this function can be called without restrictions.", "description": "In the function `_function_0x8a4068dd` a non-zero amount of Ether is sent to msg.sender.\nIt seems that this function can be called without restrictions.",
"function": "_function_0x8a4068dd", "function": "_function_0x8a4068dd",
"type": "Warning", "type": "Warning",
"address": 142, "address": 142,

@ -6,7 +6,7 @@
- PC address: 142 - PC address: 142
### Description ### Description
In the function `'_function_0x8a4068dd'` a non-zero amount of Ether is sent to msg.sender. 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.
In *<TESTDATA>/inputs/multi_contracts.sol:14* In *<TESTDATA>/inputs/multi_contracts.sol:14*

@ -3,7 +3,7 @@ Type: Warning
Contract: Transfer2 Contract: Transfer2
Function name: _function_0x8a4068dd Function name: _function_0x8a4068dd
PC address: 142 PC address: 142
In the function `'_function_0x8a4068dd'` a non-zero amount of Ether is sent to msg.sender. 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.
-------------------- --------------------
In file: <TESTDATA>/inputs/multi_contracts.sol:14 In file: <TESTDATA>/inputs/multi_contracts.sol:14

@ -4,7 +4,7 @@
"issues": [ "issues": [
{ {
"title": "Ether send", "title": "Ether send",
"description": "In the function `'_function_0x4229616d'` a non-zero amount of Ether is sent to an address taken from storage slot 5.\nThere is a check on storage index 5. This storage slot can be written to by calling the function '_function_0x67f809e9'.\n\nThere is a check on storage index 5. This storage slot can be written to by calling the function '_function_0x67f809e9'.\nThere is a check on storage index 1. This storage slot can be written to by calling the function 'fallback'.", "description": "In the function `_function_0x4229616d` a non-zero amount of Ether is sent to an address taken from storage slot 5.\nThere is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`.\n\nThere is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`.\nThere is a check on storage index 1. This storage slot can be written to by calling the function `fallback`.",
"function": "_function_0x4229616d", "function": "_function_0x4229616d",
"type": "Warning", "type": "Warning",
"address": 1599, "address": 1599,
@ -15,7 +15,7 @@
}, },
{ {
"title": "Ether send", "title": "Ether send",
"description": "In the function `'_function_0x686f2c90'` a non-zero amount of Ether is sent to an address taken from storage slot 5.\nThere is a check on storage index 5. This storage slot can be written to by calling the function '_function_0x67f809e9'.\n\nThere is a check on storage index 5. This storage slot can be written to by calling the function '_function_0x67f809e9'.\nThere is a check on storage index 1. This storage slot can be written to by calling the function 'fallback'.", "description": "In the function `_function_0x686f2c90` a non-zero amount of Ether is sent to an address taken from storage slot 5.\nThere is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`.\n\nThere is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`.\nThere is a check on storage index 1. This storage slot can be written to by calling the function `fallback`.",
"function": "_function_0x686f2c90", "function": "_function_0x686f2c90",
"type": "Warning", "type": "Warning",
"address": 1940, "address": 1940,
@ -26,7 +26,7 @@
}, },
{ {
"title": "Exception state", "title": "Exception state",
"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. ", "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. ",
"function": "_function_0x57d4021b", "function": "_function_0x57d4021b",
"type": "Informational", "type": "Informational",
"address": 1653, "address": 1653,
@ -37,7 +37,7 @@
}, },
{ {
"title": "Exception state", "title": "Exception state",
"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. ", "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. ",
"function": "_function_0x9dbc4f9b", "function": "_function_0x9dbc4f9b",
"type": "Informational", "type": "Informational",
"address": 2085, "address": 2085,

@ -6,11 +6,11 @@
- PC address: 1599 - PC address: 1599
### Description ### Description
In the function `'_function_0x4229616d'` a non-zero amount of Ether is sent to an address taken from storage slot 5. In the function `_function_0x4229616d` a non-zero amount of Ether is sent to an address taken from storage slot 5.
There is a check on storage index 5. This storage slot can be written to by calling the function '_function_0x67f809e9'. There is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`.
There is a check on storage index 5. This storage slot can be written to by calling the function '_function_0x67f809e9'. There is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`.
There is a check on storage index 1. This storage slot can be written to by calling the function 'fallback'. There is a check on storage index 1. This storage slot can be written to by calling the function `fallback`.
In *<TESTDATA>/inputs/rubixi.sol:93* In *<TESTDATA>/inputs/rubixi.sol:93*
@ -24,11 +24,11 @@ creator.send(feesToCollect)
- PC address: 1940 - PC address: 1940
### Description ### Description
In the function `'_function_0x686f2c90'` a non-zero amount of Ether is sent to an address taken from storage slot 5. In the function `_function_0x686f2c90` a non-zero amount of Ether is sent to an address taken from storage slot 5.
There is a check on storage index 5. This storage slot can be written to by calling the function '_function_0x67f809e9'. There is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`.
There is a check on storage index 5. This storage slot can be written to by calling the function '_function_0x67f809e9'. There is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`.
There is a check on storage index 1. This storage slot can be written to by calling the function 'fallback'. There is a check on storage index 1. This storage slot can be written to by calling the function `fallback`.
In *<TESTDATA>/inputs/rubixi.sol:75* In *<TESTDATA>/inputs/rubixi.sol:75*
@ -42,7 +42,7 @@ creator.send(collectedFees)
- PC address: 1653 - PC address: 1653
### Description ### 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.
In *<TESTDATA>/inputs/rubixi.sol:131* In *<TESTDATA>/inputs/rubixi.sol:131*
@ -56,7 +56,7 @@ participants[payoutOrder]
- PC address: 2085 - PC address: 2085
### Description ### 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.
In *<TESTDATA>/inputs/rubixi.sol:148* In *<TESTDATA>/inputs/rubixi.sol:148*

@ -3,11 +3,11 @@ Type: Warning
Contract: Rubixi Contract: Rubixi
Function name: _function_0x4229616d Function name: _function_0x4229616d
PC address: 1599 PC address: 1599
In the function `'_function_0x4229616d'` a non-zero amount of Ether is sent to an address taken from storage slot 5. In the function `_function_0x4229616d` a non-zero amount of Ether is sent to an address taken from storage slot 5.
There is a check on storage index 5. This storage slot can be written to by calling the function '_function_0x67f809e9'. There is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`.
There is a check on storage index 5. This storage slot can be written to by calling the function '_function_0x67f809e9'. There is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`.
There is a check on storage index 1. This storage slot can be written to by calling the function 'fallback'. There is a check on storage index 1. This storage slot can be written to by calling the function `fallback`.
-------------------- --------------------
In file: <TESTDATA>/inputs/rubixi.sol:93 In file: <TESTDATA>/inputs/rubixi.sol:93
@ -20,11 +20,11 @@ Type: Warning
Contract: Rubixi Contract: Rubixi
Function name: _function_0x686f2c90 Function name: _function_0x686f2c90
PC address: 1940 PC address: 1940
In the function `'_function_0x686f2c90'` a non-zero amount of Ether is sent to an address taken from storage slot 5. In the function `_function_0x686f2c90` a non-zero amount of Ether is sent to an address taken from storage slot 5.
There is a check on storage index 5. This storage slot can be written to by calling the function '_function_0x67f809e9'. There is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`.
There is a check on storage index 5. This storage slot can be written to by calling the function '_function_0x67f809e9'. There is a check on storage index 5. This storage slot can be written to by calling the function `_function_0x67f809e9`.
There is a check on storage index 1. This storage slot can be written to by calling the function 'fallback'. There is a check on storage index 1. This storage slot can be written to by calling the function `fallback`.
-------------------- --------------------
In file: <TESTDATA>/inputs/rubixi.sol:75 In file: <TESTDATA>/inputs/rubixi.sol:75
@ -37,7 +37,7 @@ Type: Informational
Contract: Rubixi Contract: Rubixi
Function name: _function_0x57d4021b Function name: _function_0x57d4021b
PC address: 1653 PC address: 1653
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.
-------------------- --------------------
In file: <TESTDATA>/inputs/rubixi.sol:131 In file: <TESTDATA>/inputs/rubixi.sol:131
@ -50,7 +50,7 @@ Type: Informational
Contract: Rubixi Contract: Rubixi
Function name: _function_0x9dbc4f9b Function name: _function_0x9dbc4f9b
PC address: 2085 PC address: 2085
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.
-------------------- --------------------
In file: <TESTDATA>/inputs/rubixi.sol:148 In file: <TESTDATA>/inputs/rubixi.sol:148

@ -4,7 +4,7 @@
"issues": [ "issues": [
{ {
"title": "Dependence on predictable environment variable", "title": "Dependence on predictable environment variable",
"description": "In the function `'_function_0xe9874106'` the following predictable state variables are used to determine Ether recipient:\n- block.coinbase\n", "description": "In the function `_function_0xe9874106` the following predictable state variables are used to determine Ether recipient:\n- block.coinbase\n",
"function": "_function_0xe9874106", "function": "_function_0xe9874106",
"type": "Warning", "type": "Warning",
"address": 1285, "address": 1285,
@ -15,7 +15,7 @@
}, },
{ {
"title": "Ether send", "title": "Ether send",
"description": "In the function `'_function_0xe9874106'` a non-zero amount of Ether is sent to an address taken from storage slot 0.\nThere is a check on storage index 0. This storage slot can be written to by calling the function 'fallback'.\n\nThere is a check on storage index 1. This storage slot can be written to by calling the function 'fallback'.\nThere is a check on storage index 1. This storage slot can be written to by calling the function 'fallback'.", "description": "In the function `_function_0xe9874106` a non-zero amount of Ether is sent to an address taken from storage slot 0.\nThere is a check on storage index 0. This storage slot can be written to by calling the function `fallback`.\n\nThere is a check on storage index 1. This storage slot can be written to by calling the function `fallback`.\nThere is a check on storage index 1. This storage slot can be written to by calling the function `fallback`.",
"function": "_function_0xe9874106", "function": "_function_0xe9874106",
"type": "Warning", "type": "Warning",
"address": 1285, "address": 1285,
@ -26,7 +26,7 @@
}, },
{ {
"title": "Exception state", "title": "Exception state",
"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. ", "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. ",
"function": "fallback", "function": "fallback",
"type": "Informational", "type": "Informational",
"address": 356, "address": 356,
@ -37,7 +37,7 @@
}, },
{ {
"title": "Exception state", "title": "Exception state",
"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. ", "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. ",
"function": "_function_0xe9874106", "function": "_function_0xe9874106",
"type": "Informational", "type": "Informational",
"address": 146, "address": 146,

@ -6,7 +6,7 @@
- PC address: 1285 - PC address: 1285
### Description ### Description
In the function `'_function_0xe9874106'` the following predictable state variables are used to determine Ether recipient: In the function `_function_0xe9874106` the following predictable state variables are used to determine Ether recipient:
- block.coinbase - block.coinbase
@ -22,11 +22,11 @@ winningAddress.transfer(prize)
- PC address: 1285 - PC address: 1285
### Description ### Description
In the function `'_function_0xe9874106'` a non-zero amount of Ether is sent to an address taken from storage slot 0. In the function `_function_0xe9874106` a non-zero amount of Ether is sent to an address taken from storage slot 0.
There is a check on storage index 0. This storage slot can be written to by calling the function 'fallback'. There is a check on storage index 0. This storage slot can be written to by calling the function `fallback`.
There is a check on storage index 1. This storage slot can be written to by calling the function 'fallback'. There is a check on storage index 1. This storage slot can be written to by calling the function `fallback`.
There is a check on storage index 1. This storage slot can be written to by calling the function 'fallback'. There is a check on storage index 1. This storage slot can be written to by calling the function `fallback`.
In *<TESTDATA>/inputs/weak_random.sol:47* In *<TESTDATA>/inputs/weak_random.sol:47*
@ -40,7 +40,7 @@ winningAddress.transfer(prize)
- PC address: 356 - PC address: 356
### Description ### 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.
In *<TESTDATA>/inputs/weak_random.sol:11* In *<TESTDATA>/inputs/weak_random.sol:11*
@ -54,7 +54,7 @@ prize / totalTickets
- PC address: 146 - PC address: 146
### Description ### 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.
In *<TESTDATA>/inputs/weak_random.sol:11* In *<TESTDATA>/inputs/weak_random.sol:11*

@ -3,7 +3,7 @@ Type: Warning
Contract: WeakRandom Contract: WeakRandom
Function name: _function_0xe9874106 Function name: _function_0xe9874106
PC address: 1285 PC address: 1285
In the function `'_function_0xe9874106'` the following predictable state variables are used to determine Ether recipient: In the function `_function_0xe9874106` the following predictable state variables are used to determine Ether recipient:
- block.coinbase - block.coinbase
-------------------- --------------------
@ -18,11 +18,11 @@ Type: Warning
Contract: WeakRandom Contract: WeakRandom
Function name: _function_0xe9874106 Function name: _function_0xe9874106
PC address: 1285 PC address: 1285
In the function `'_function_0xe9874106'` a non-zero amount of Ether is sent to an address taken from storage slot 0. In the function `_function_0xe9874106` a non-zero amount of Ether is sent to an address taken from storage slot 0.
There is a check on storage index 0. This storage slot can be written to by calling the function 'fallback'. There is a check on storage index 0. This storage slot can be written to by calling the function `fallback`.
There is a check on storage index 1. This storage slot can be written to by calling the function 'fallback'. There is a check on storage index 1. This storage slot can be written to by calling the function `fallback`.
There is a check on storage index 1. This storage slot can be written to by calling the function 'fallback'. There is a check on storage index 1. This storage slot can be written to by calling the function `fallback`.
-------------------- --------------------
In file: <TESTDATA>/inputs/weak_random.sol:47 In file: <TESTDATA>/inputs/weak_random.sol:47
@ -35,7 +35,7 @@ Type: Informational
Contract: WeakRandom Contract: WeakRandom
Function name: fallback Function name: fallback
PC address: 356 PC address: 356
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.
-------------------- --------------------
In file: <TESTDATA>/inputs/weak_random.sol:11 In file: <TESTDATA>/inputs/weak_random.sol:11
@ -48,7 +48,7 @@ Type: Informational
Contract: WeakRandom Contract: WeakRandom
Function name: _function_0xe9874106 Function name: _function_0xe9874106
PC address: 146 PC address: 146
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.
-------------------- --------------------
In file: <TESTDATA>/inputs/weak_random.sol:11 In file: <TESTDATA>/inputs/weak_random.sol:11

Loading…
Cancel
Save