Merge pull request #362 from crytic/dev-formatters

Make generate_json_result generating the JSON elements automatically
pull/367/head
Feist Josselin 5 years ago committed by GitHub
commit a347e545b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      plugin_example/slither_my_plugin/detectors/example.py
  2. 9
      slither/__main__.py
  3. 4
      slither/core/declarations/pragma_directive.py
  4. 6
      slither/core/slither_core.py
  5. 20
      slither/core/source_mapping/source_mapping.py
  6. 2
      slither/core/variables/local_variable.py
  7. 71
      slither/detectors/abstract_detector.py
  8. 24
      slither/detectors/attributes/const_functions.py
  9. 14
      slither/detectors/attributes/constant_pragma.py
  10. 6
      slither/detectors/attributes/incorrect_solc.py
  11. 18
      slither/detectors/attributes/locked_ether.py
  12. 9
      slither/detectors/erc/incorrect_erc20_interface.py
  13. 11
      slither/detectors/erc/incorrect_erc721_interface.py
  14. 13
      slither/detectors/erc/unindexed_event_parameters.py
  15. 10
      slither/detectors/examples/backdoor.py
  16. 15
      slither/detectors/functions/arbitrary_send.py
  17. 7
      slither/detectors/functions/complex_function.py
  18. 15
      slither/detectors/functions/external_function.py
  19. 10
      slither/detectors/functions/suicidal.py
  20. 94
      slither/detectors/naming_convention/naming_convention.py
  21. 16
      slither/detectors/operations/block_timestamp.py
  22. 13
      slither/detectors/operations/low_level_calls.py
  23. 16
      slither/detectors/operations/unused_return_values.py
  24. 14
      slither/detectors/operations/void_constructor.py
  25. 31
      slither/detectors/reentrancy/reentrancy_benign.py
  26. 30
      slither/detectors/reentrancy/reentrancy_eth.py
  27. 23
      slither/detectors/reentrancy/reentrancy_read_before_write.py
  28. 12
      slither/detectors/shadowing/abstract.py
  29. 37
      slither/detectors/shadowing/builtin_symbols.py
  30. 38
      slither/detectors/shadowing/local.py
  31. 11
      slither/detectors/shadowing/state.py
  32. 12
      slither/detectors/source/rtlo.py
  33. 11
      slither/detectors/statements/assembly.py
  34. 9
      slither/detectors/statements/calls_in_loop.py
  35. 13
      slither/detectors/statements/controlled_delegatecall.py
  36. 16
      slither/detectors/statements/deprecated_calls.py
  37. 11
      slither/detectors/statements/incorrect_strict_equality.py
  38. 11
      slither/detectors/statements/too_many_digits.py
  39. 10
      slither/detectors/statements/tx_origin.py
  40. 7
      slither/detectors/variables/possible_const_state_variables.py
  41. 12
      slither/detectors/variables/uninitialized_local_variables.py
  42. 15
      slither/detectors/variables/uninitialized_state_variables.py
  43. 10
      slither/detectors/variables/uninitialized_storage_variables.py
  44. 9
      slither/detectors/variables/unused_state_variables.py
  45. 32
      slither/printers/abstract_printer.py
  46. 6
      slither/printers/call/call_graph.py
  47. 6
      slither/printers/functions/authorization.py
  48. 6
      slither/printers/functions/cfg.py
  49. 4
      slither/printers/guidance/echidna.py
  50. 4
      slither/printers/inheritance/inheritance.py
  51. 6
      slither/printers/inheritance/inheritance_graph.py
  52. 83
      slither/printers/summary/constructor_calls.py
  53. 15
      slither/printers/summary/contract.py
  54. 6
      slither/printers/summary/data_depenency.py
  55. 6
      slither/printers/summary/function.py
  56. 6
      slither/printers/summary/function_ids.py
  57. 9
      slither/printers/summary/human_summary.py
  58. 6
      slither/printers/summary/modifier_calls.py
  59. 6
      slither/printers/summary/require_calls.py
  60. 32
      slither/printers/summary/slithir.py
  61. 22
      slither/printers/summary/slithir_ssa.py
  62. 7
      slither/printers/summary/variable_order.py
  63. 4
      slither/slither.py
  64. 6
      slither/tools/erc_conformance/__main__.py
  65. 8
      slither/tools/erc_conformance/erc/erc20.py
  66. 57
      slither/tools/erc_conformance/erc/ercs.py
  67. 43
      slither/tools/kspec_coverage/analysis.py
  68. 12
      slither/tools/upgradeability/__main__.py
  69. 28
      slither/tools/upgradeability/check_initialization.py
  70. 8
      slither/tools/upgradeability/check_variable_initialization.py
  71. 32
      slither/tools/upgradeability/compare_function_ids.py
  72. 22
      slither/tools/upgradeability/compare_variables_order.py
  73. 26
      slither/tools/upgradeability/constant_checks.py
  74. 381
      slither/utils/json_utils.py
  75. 454
      slither/utils/output.py
  76. 22
      tests/expected_json/arbitrary_send-0.5.1.arbitrary-send.json
  77. 4
      tests/expected_json/arbitrary_send-0.5.1.arbitrary-send.txt
  78. 22
      tests/expected_json/arbitrary_send.arbitrary-send.json
  79. 4
      tests/expected_json/arbitrary_send.arbitrary-send.txt
  80. 11
      tests/expected_json/backdoor.backdoor.json
  81. 6
      tests/expected_json/backdoor.backdoor.txt
  82. 11
      tests/expected_json/backdoor.suicidal.json
  83. 4
      tests/expected_json/backdoor.suicidal.txt
  84. 66
      tests/expected_json/const_state_variables.constable-states.json
  85. 16
      tests/expected_json/const_state_variables.constable-states.txt
  86. 11
      tests/expected_json/constant-0.5.1.constant-function.json
  87. 4
      tests/expected_json/constant-0.5.1.constant-function.txt
  88. 33
      tests/expected_json/constant.constant-function.json
  89. 8
      tests/expected_json/constant.constant-function.txt
  90. 178
      tests/expected_json/controlled_delegatecall.controlled-delegatecall.json
  91. 8
      tests/expected_json/controlled_delegatecall.controlled-delegatecall.txt
  92. 88
      tests/expected_json/deprecated_calls.deprecated-standards.json
  93. 20
      tests/expected_json/deprecated_calls.deprecated-standards.txt
  94. 44
      tests/expected_json/erc20_indexed.erc20-indexed.json
  95. 12
      tests/expected_json/erc20_indexed.erc20-indexed.txt
  96. 55
      tests/expected_json/external_function.external-function.json
  97. 756
      tests/expected_json/incorrect_equality.incorrect-equality.json
  98. 28
      tests/expected_json/incorrect_equality.incorrect-equality.txt
  99. 216
      tests/expected_json/incorrect_erc20_interface.erc20-interface.json
  100. 16
      tests/expected_json/incorrect_erc20_interface.erc20-interface.txt
  101. Some files were not shown because too many files have changed in this diff Show More

@ -23,6 +23,6 @@ class Example(AbstractDetector):
info = 'This is an example!' info = 'This is an example!'
json = self.generate_json_result(info) json = self.generate_result(info)
return [json] return [json]

@ -19,7 +19,7 @@ from slither.detectors.abstract_detector import (AbstractDetector,
from slither.printers import all_printers from slither.printers import all_printers
from slither.printers.abstract_printer import AbstractPrinter from slither.printers.abstract_printer import AbstractPrinter
from slither.slither import Slither from slither.slither import Slither
from slither.utils.json_utils import output_json from slither.utils.output import output_to_json
from slither.utils.output_capture import StandardOutputCapture from slither.utils.output_capture import StandardOutputCapture
from slither.utils.colors import red, yellow, set_colorization_enabled from slither.utils.colors import red, yellow, set_colorization_enabled
from slither.utils.command_line import (output_detectors, output_results_to_markdown, from slither.utils.command_line import (output_detectors, output_results_to_markdown,
@ -331,6 +331,11 @@ def parse_args(detector_classes, printer_classes):
action='store', action='store',
default=defaults_flag_in_config['json-types']) default=defaults_flag_in_config['json-types'])
group_misc.add_argument('--markdown-root',
help='URL for markdown generation',
action='store',
default="")
group_misc.add_argument('--disable-color', group_misc.add_argument('--disable-color',
help='Disable output colorization', help='Disable output colorization',
action='store_true', action='store_true',
@ -633,7 +638,7 @@ def main_impl(all_detector_classes, all_printer_classes):
'stderr': StandardOutputCapture.get_stderr_output() 'stderr': StandardOutputCapture.get_stderr_output()
} }
StandardOutputCapture.disable() StandardOutputCapture.disable()
output_json(None if outputting_json_stdout else args.json, output_error, json_results) output_to_json(None if outputting_json_stdout else args.json, output_error, json_results)
# Exit with the appropriate status code # Exit with the appropriate status code
if output_error: if output_error:

@ -17,5 +17,9 @@ class Pragma(SourceMapping):
def version(self): def version(self):
return ''.join(self.directive[1:]) return ''.join(self.directive[1:])
@property
def name(self):
return self.version
def __str__(self): def __str__(self):
return 'pragma '+''.join(self.directive) return 'pragma '+''.join(self.directive)

@ -38,6 +38,8 @@ class Slither(Context):
self._generate_patches = False self._generate_patches = False
self._markdown_root = ""
################################################################################### ###################################################################################
################################################################################### ###################################################################################
# region Source code # region Source code
@ -69,6 +71,10 @@ class Slither(Context):
with open(path, encoding='utf8', newline='') as f: with open(path, encoding='utf8', newline='') as f:
self.source_code[path] = f.read() self.source_code[path] = f.read()
@property
def markdown_root(self):
return self._markdown_root
# endregion # endregion
################################################################################### ###################################################################################
################################################################################### ###################################################################################

@ -132,16 +132,22 @@ class SourceMapping(Context):
else: else:
self._source_mapping = self._convert_source_mapping(offset, slither) self._source_mapping = self._convert_source_mapping(offset, slither)
def _get_lines_str(self, line_descr=""):
@property
def source_mapping_str(self):
lines = self.source_mapping.get('lines', None) lines = self.source_mapping.get('lines', None)
if not lines: if not lines:
lines = '' lines = ''
elif len(lines) == 1: elif len(lines) == 1:
lines = '#{}'.format(lines[0]) lines = '#{}{}'.format(line_descr, lines[0])
else: else:
lines = '#{}-{}'.format(lines[0], lines[-1]) lines = '#{}{}-{}{}'.format(line_descr, lines[0], line_descr, lines[-1])
return '{}{}'.format(self.source_mapping['filename_short'], lines) return lines
def source_mapping_to_markdown(self, markdown_root):
lines = self._get_lines_str(line_descr="L")
return f'{markdown_root}{self.source_mapping["filename_relative"]}{lines}'
@property
def source_mapping_str(self):
lines = self._get_lines_str()
return f'{self.source_mapping["filename_short"]}{lines}'

@ -52,6 +52,6 @@ class LocalVariable(ChildFunction, Variable):
@property @property
def canonical_name(self): def canonical_name(self):
return self.name return '{}.{}'.format(self.function.canonical_name, self.name)

@ -4,7 +4,7 @@ import re
from slither.utils.colors import green, yellow, red from slither.utils.colors import green, yellow, red
from slither.formatters.exceptions import FormatImpossible from slither.formatters.exceptions import FormatImpossible
from slither.formatters.utils.patches import apply_patch, create_diff from slither.formatters.utils.patches import apply_patch, create_diff
from slither.utils import json_utils from slither.utils.output import Output
class IncorrectDetectorInitialization(Exception): class IncorrectDetectorInitialization(Exception):
@ -49,6 +49,8 @@ class AbstractDetector(metaclass=abc.ABCMeta):
WIKI_EXPLOIT_SCENARIO = '' WIKI_EXPLOIT_SCENARIO = ''
WIKI_RECOMMENDATION = '' WIKI_RECOMMENDATION = ''
STANDARD_JSON = True
def __init__(self, slither, logger): def __init__(self, slither, logger):
self.slither = slither self.slither = slither
self.contracts = slither.contracts self.contracts = slither.contracts
@ -101,10 +103,12 @@ class AbstractDetector(metaclass=abc.ABCMeta):
@abc.abstractmethod @abc.abstractmethod
def _detect(self): def _detect(self):
"""TODO Documentation""" """TODO Documentation"""
return return []
def detect(self): def detect(self):
all_results = self._detect() all_results = self._detect()
# Keep only dictionaries
all_results = [r.data for r in all_results]
results = [] results = []
# only keep valid result, and remove dupplicate # only keep valid result, and remove dupplicate
[results.append(r) for r in all_results if self.slither.valid_result(r) and r not in results] [results.append(r) for r in all_results if self.slither.valid_result(r) and r not in results]
@ -168,62 +172,17 @@ class AbstractDetector(metaclass=abc.ABCMeta):
def color(self): def color(self):
return classification_colors[self.IMPACT] return classification_colors[self.IMPACT]
def generate_json_result(self, info, additional_fields=None): def generate_result(self, info, additional_fields=None):
d = json_utils.generate_json_result(info, additional_fields) output = Output(info,
additional_fields,
d['check'] = self.ARGUMENT standard_format=self.STANDARD_JSON,
d['impact'] = classification_txt[self.IMPACT] markdown_root=self.slither.markdown_root)
d['confidence'] = classification_txt[self.CONFIDENCE]
return d output.data['check'] = self.ARGUMENT
output.data['impact'] = classification_txt[self.IMPACT]
@staticmethod output.data['confidence'] = classification_txt[self.CONFIDENCE]
def add_variable_to_json(e, d, additional_fields=None):
json_utils.add_variable_to_json(e, d, additional_fields=additional_fields)
@staticmethod return output
def add_variables_to_json(e, d):
json_utils.add_variables_to_json(e, d)
@staticmethod
def add_contract_to_json(e, d, additional_fields=None):
json_utils.add_contract_to_json(e, d, additional_fields=additional_fields)
@staticmethod
def add_function_to_json(e, d, additional_fields=None):
json_utils.add_function_to_json(e, d, additional_fields=additional_fields)
@staticmethod
def add_functions_to_json(e, d, additional_fields=None):
json_utils.add_functions_to_json(e, d, additional_fields=additional_fields)
@staticmethod
def add_enum_to_json(e, d, additional_fields=None):
json_utils.add_enum_to_json(e, d, additional_fields=additional_fields)
@staticmethod
def add_struct_to_json(e, d, additional_fields=None):
json_utils.add_struct_to_json(e, d, additional_fields=additional_fields)
@staticmethod
def add_event_to_json(e, d, additional_fields=None):
json_utils.add_event_to_json(e, d, additional_fields=additional_fields)
@staticmethod
def add_pragma_to_json(e, d, additional_fields=None):
json_utils.add_pragma_to_json(e, d, additional_fields=additional_fields)
@staticmethod
def add_node_to_json(e, d, additional_fields=None):
json_utils.add_node_to_json(e, d, additional_fields=additional_fields)
@staticmethod
def add_nodes_to_json(e, d):
json_utils.add_nodes_to_json(e, d)
@staticmethod
def add_other_to_json(name, source_mapping, d, slither, additional_fields=None):
json_utils.add_other_to_json(name, source_mapping, d, slither, additional_fields=additional_fields)
@staticmethod @staticmethod
def _format(slither, result): def _format(slither, result):

@ -57,24 +57,24 @@ All the calls to `get` revert, breaking Bob's smart contract execution.'''
if f.view or f.pure: if f.view or f.pure:
if f.contains_assembly: if f.contains_assembly:
attr = 'view' if f.view else 'pure' attr = 'view' if f.view else 'pure'
info = '{} ({}) is declared {} but contains assembly code\n'
info = info.format(f.canonical_name, f.source_mapping_str, attr) info = [f, f' is declared {attr} but contains assembly code\n']
json = self.generate_json_result(info, {'contains_assembly': True}) res = self.generate_result(info, {'contains_assembly': True})
self.add_function_to_json(f, json)
results.append(json) results.append(res)
variables_written = f.all_state_variables_written() variables_written = f.all_state_variables_written()
if variables_written: if variables_written:
attr = 'view' if f.view else 'pure' attr = 'view' if f.view else 'pure'
info = '{} ({}) is declared {} but changes state variables:\n'
info = info.format(f.canonical_name, f.source_mapping_str, attr) info = [f, f' is declared {attr} but changes state variables:\n']
for variable_written in variables_written: for variable_written in variables_written:
info += '\t- {}\n'.format(variable_written.canonical_name) info += ['\t- ', variable_written, '\n']
res = self.generate_result(info, {'contains_assembly': False})
json = self.generate_json_result(info, {'contains_assembly': False}) results.append(res)
self.add_function_to_json(f, json)
self.add_variables_to_json(variables_written, json)
results.append(json)
return results return results

@ -30,17 +30,15 @@ class ConstantPragma(AbstractDetector):
versions = sorted(list(set(versions))) versions = sorted(list(set(versions)))
if len(versions) > 1: if len(versions) > 1:
info = "Different versions of Solidity is used in {}:\n".format(self.filename) info = [f"Different versions of Solidity is used in {self.filename}:\n"]
info += "\t- Version used: {}\n".format([str(v) for v in versions]) info += [f"\t- Version used: {[str(v) for v in versions]}\n"]
for p in pragma: for p in pragma:
info += "\t- {} declares {}\n".format(p.source_mapping_str, str(p)) info += ["\t- ", p, "\n"]
json = self.generate_json_result(info) res = self.generate_result(info)
# Add each pragma to our elements results.append(res)
for p in pragma:
self.add_pragma_to_json(p, json)
results.append(json)
return results return results

@ -100,10 +100,10 @@ Use Solidity 0.4.25 or 0.5.3. Consider using the latest version of Solidity for
# If we found any disallowed pragmas, we output our findings. # If we found any disallowed pragmas, we output our findings.
if disallowed_pragmas: if disallowed_pragmas:
for (reason, p) in disallowed_pragmas: for (reason, p) in disallowed_pragmas:
info = f"Pragma version \"{p.version}\" {reason} ({p.source_mapping_str})\n" info = ["Pragma version", p, f" {reason}\n"]
json = self.generate_result(info)
json = self.generate_json_result(info)
self.add_pragma_to_json(p, json)
results.append(json) results.append(json)
return results return results

@ -74,18 +74,14 @@ Every ether sent to `Locked` will be lost.'''
funcs_payable = [function for function in contract.functions if function.payable] funcs_payable = [function for function in contract.functions if function.payable]
if funcs_payable: if funcs_payable:
if self.do_no_send_ether(contract): if self.do_no_send_ether(contract):
txt = "Contract locking ether found in {}:\n".format(self.filename) info = [f"Contract locking ether found in {self.filename}:\n"]
txt += "\tContract {} has payable functions:\n".format(contract.name) info += ["\tContract ", contract, " has payable functions:\n"]
for function in funcs_payable: for function in funcs_payable:
txt += "\t - {} ({})\n".format(function.name, function.source_mapping_str) info += [f"\t - ", function, "\n"]
txt += "\tBut does not have a function to withdraw the ether\n" info += "\tBut does not have a function to withdraw the ether\n"
info = txt.format(self.filename,
contract.name, json = self.generate_result(info)
[f.name for f in funcs_payable])
json = self.generate_json_result(info)
self.add_contract_to_json(contract, json)
self.add_functions_to_json(funcs_payable, json)
results.append(json) results.append(json)
return results return results

@ -87,12 +87,9 @@ contract Token{
functions = IncorrectERC20InterfaceDetection.detect_incorrect_erc20_interface(c) functions = IncorrectERC20InterfaceDetection.detect_incorrect_erc20_interface(c)
if functions: if functions:
for function in functions: for function in functions:
info = "{} ({}) has incorrect ERC20 function interface: {} ({})\n".format(c.name, info = [c, " has incorrect ERC20 function interface:", function, "\n"]
c.source_mapping_str, json = self.generate_result(info)
function.full_name,
function.source_mapping_str)
json = self.generate_json_result(info)
self.add_function_to_json(function, json)
results.append(json) results.append(json)
return results return results

@ -86,12 +86,9 @@ contract Token{
functions = IncorrectERC721InterfaceDetection.detect_incorrect_erc721_interface(c) functions = IncorrectERC721InterfaceDetection.detect_incorrect_erc721_interface(c)
if functions: if functions:
for function in functions: for function in functions:
info = "{} ({}) has incorrect ERC721 function interface: {} ({})\n".format(c.name, info = [c, " has incorrect ERC721 function interface:", function, "\n"]
c.source_mapping_str, res = self.generate_result(info)
function.full_name,
function.source_mapping_str) results.append(res)
json = self.generate_json_result(info)
self.add_function_to_json(function, json)
results.append(json)
return results return results

@ -32,6 +32,8 @@ In this case, Transfer and Approval events should have the 'indexed' keyword on
WIKI_RECOMMENDATION = 'Add the `indexed` keyword to event parameters which should include it, according to the ERC20 specification.' WIKI_RECOMMENDATION = 'Add the `indexed` keyword to event parameters which should include it, according to the ERC20 specification.'
STANDARD_JSON = False
@staticmethod @staticmethod
def detect_erc20_unindexed_event_params(contract): def detect_erc20_unindexed_event_params(contract):
""" """
@ -71,14 +73,13 @@ In this case, Transfer and Approval events should have the 'indexed' keyword on
# Add each problematic event definition to our result list # Add each problematic event definition to our result list
for (event, parameter) in unindexed_params: for (event, parameter) in unindexed_params:
info = "ERC20 event {}.{} ({}) does not index parameter '{}'\n".format(c.name, event.name, event.source_mapping_str, parameter.name) info = ["ERC20 event ", event, f"does not index parameter {parameter}\n"]
# Add the events to the JSON (note: we do not add the params/vars as they have no source mapping). # Add the events to the JSON (note: we do not add the params/vars as they have no source mapping).
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_event_to_json(event, json, {
"parameter_name": parameter.name res.add(event, {"parameter_name": parameter.name})
}) results.append(res)
results.append(json)
return results return results

@ -26,11 +26,11 @@ class Backdoor(AbstractDetector):
for f in contract.functions: for f in contract.functions:
if 'backdoor' in f.name: if 'backdoor' in f.name:
# Info to be printed # Info to be printed
info = 'Backdoor function found in {}.{} ({})\n' info = ['Backdoor function found in ', f, '\n']
info = info.format(contract.name, f.name, f.source_mapping_str)
# Add the result in result # Add the result in result
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_function_to_json(f, json)
results.append(json) results.append(res)
return results return results

@ -109,16 +109,13 @@ Bob calls `setDestination` and `withdraw`. As a result he withdraws the contract
arbitrary_send = self.detect_arbitrary_send(c) arbitrary_send = self.detect_arbitrary_send(c)
for (func, nodes) in arbitrary_send: for (func, nodes) in arbitrary_send:
info = "{} ({}) sends eth to arbitrary user\n" info = [func, " sends eth to arbitrary user\n"]
info = info.format(func.canonical_name, info += ['\tDangerous calls:\n']
func.source_mapping_str)
info += '\tDangerous calls:\n'
for node in nodes: for node in nodes:
info += '\t- {} ({})\n'.format(node.expression, node.source_mapping_str) info += ['\t- ', node, '\n']
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_function_to_json(func, json)
self.add_nodes_to_json(nodes, json) results.append(res)
results.append(json)
return results return results

@ -31,6 +31,7 @@ class ComplexFunction(AbstractDetector):
CAUSE_EXTERNAL_CALL = "external_calls" CAUSE_EXTERNAL_CALL = "external_calls"
CAUSE_STATE_VARS = "state_vars" CAUSE_STATE_VARS = "state_vars"
STANDARD_JSON = True
@staticmethod @staticmethod
def detect_complex_func(func): def detect_complex_func(func):
@ -104,14 +105,14 @@ class ComplexFunction(AbstractDetector):
info = info + "\n" info = info + "\n"
self.log(info) self.log(info)
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_function_to_json(func, json, { res.add(func, {
'high_number_of_external_calls': cause == self.CAUSE_EXTERNAL_CALL, 'high_number_of_external_calls': cause == self.CAUSE_EXTERNAL_CALL,
'high_number_of_branches': cause == self.CAUSE_CYCLOMATIC, 'high_number_of_branches': cause == self.CAUSE_CYCLOMATIC,
'high_number_of_state_variables': cause == self.CAUSE_STATE_VARS 'high_number_of_state_variables': cause == self.CAUSE_STATE_VARS
}) })
results.append(json) results.append(res)
return results return results

@ -182,17 +182,14 @@ class ExternalFunction(AbstractDetector):
function_definition = all_function_definitions[0] function_definition = all_function_definitions[0]
all_function_definitions = all_function_definitions[1:] all_function_definitions = all_function_definitions[1:]
txt = f"{function_definition.full_name} should be declared external:\n" info = [f"{function_definition.full_name} should be declared external:\n"]
txt += f"\t- {function_definition.canonical_name} ({function_definition.source_mapping_str})\n" info += [f"\t- ", function_definition, "\n"]
for other_function_definition in all_function_definitions: for other_function_definition in all_function_definitions:
txt += f"\t- {other_function_definition.canonical_name}" info += [f"\t- ", other_function_definition, "\n"]
txt += f" ({other_function_definition.source_mapping_str})\n"
json = self.generate_json_result(txt) res = self.generate_result(info)
self.add_function_to_json(function_definition, json)
for other_function_definition in all_function_definitions: results.append(res)
self.add_function_to_json(other_function_definition, json)
results.append(json)
return results return results

@ -73,12 +73,10 @@ Bob calls `kill` and destructs the contract.'''
functions = self.detect_suicidal(c) functions = self.detect_suicidal(c)
for func in functions: for func in functions:
txt = "{} ({}) allows anyone to destruct the contract\n" info = [func, " allows anyone to destruct the contract\n"]
info = txt.format(func.canonical_name,
func.source_mapping_str)
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_function_to_json(func, json)
results.append(json) results.append(res)
return results return results

@ -30,6 +30,7 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
WIKI_RECOMMENDATION = 'Follow the Solidity [naming convention](https://solidity.readthedocs.io/en/v0.4.25/style-guide.html#naming-conventions).' WIKI_RECOMMENDATION = 'Follow the Solidity [naming convention](https://solidity.readthedocs.io/en/v0.4.25/style-guide.html#naming-conventions).'
STANDARD_JSON = False
@staticmethod @staticmethod
def is_cap_words(name): def is_cap_words(name):
@ -59,39 +60,36 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
for contract in self.contracts: for contract in self.contracts:
if not self.is_cap_words(contract.name): if not self.is_cap_words(contract.name):
info = "Contract '{}' ({}) is not in CapWords\n".format(contract.name, info = ["Contract ", contract, " is not in CapWords\n"]
contract.source_mapping_str)
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_contract_to_json(contract, json, { res.add(contract, {
"target": "contract", "target": "contract",
"convention": "CapWords" "convention": "CapWords"
}) })
results.append(json) results.append(res)
for struct in contract.structures_declared: for struct in contract.structures_declared:
if not self.is_cap_words(struct.name): if not self.is_cap_words(struct.name):
info = "Struct '{}' ({}) is not in CapWords\n" info = ["Struct ", struct, " is not in CapWords\n"]
info = info.format(struct.canonical_name, struct.source_mapping_str)
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_struct_to_json(struct, json, { res.add(struct, {
"target": "structure", "target": "structure",
"convention": "CapWords" "convention": "CapWords"
}) })
results.append(json) results.append(res)
for event in contract.events_declared: for event in contract.events_declared:
if not self.is_cap_words(event.name): if not self.is_cap_words(event.name):
info = "Event '{}' ({}) is not in CapWords\n" info = ["Event ", event, " is not in CapWords\n"]
info = info.format(event.canonical_name, event.source_mapping_str)
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_event_to_json(event, json, { res.add(event, {
"target": "event", "target": "event",
"convention": "CapWords" "convention": "CapWords"
}) })
results.append(json) results.append(res)
for func in contract.functions_declared: for func in contract.functions_declared:
if func.is_constructor: if func.is_constructor:
@ -101,15 +99,14 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
continue continue
if func.name.startswith("echidna_") or func.name.startswith("crytic_"): if func.name.startswith("echidna_") or func.name.startswith("crytic_"):
continue continue
info = "Function '{}' ({}) is not in mixedCase\n" info = ["Function ", func, " is not in mixedCase\n"]
info = info.format(func.canonical_name, func.source_mapping_str)
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_function_to_json(func, json, { res.add(func, {
"target": "function", "target": "function",
"convention": "mixedCase" "convention": "mixedCase"
}) })
results.append(json) results.append(res)
for argument in func.parameters: for argument in func.parameters:
# Ignore parameter names that are not specified i.e. empty strings # Ignore parameter names that are not specified i.e. empty strings
@ -120,30 +117,26 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
else: else:
correct_naming = self.is_mixed_case_with_underscore(argument.name) correct_naming = self.is_mixed_case_with_underscore(argument.name)
if not correct_naming: if not correct_naming:
info = "Parameter '{}' of {} ({}) is not in mixedCase\n" info = ["Parameter ", argument, " is not in mixedCase\n"]
info = info.format(argument.name,
argument.canonical_name,
argument.source_mapping_str)
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_variable_to_json(argument, json, { res.add(argument, {
"target": "parameter", "target": "parameter",
"convention": "mixedCase" "convention": "mixedCase"
}) })
results.append(json) results.append(res)
for var in contract.state_variables_declared: for var in contract.state_variables_declared:
if self.should_avoid_name(var.name): if self.should_avoid_name(var.name):
if not self.is_upper_case_with_underscores(var.name): if not self.is_upper_case_with_underscores(var.name):
info = "Variable '{}' ({}) used l, O, I, which should not be used\n" info = ["Variable ", var," used l, O, I, which should not be used\n"]
info = info.format(var.canonical_name, var.source_mapping_str)
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_variable_to_json(var, json, { res.add(var, {
"target": "variable", "target": "variable",
"convention": "l_O_I_should_not_be_used" "convention": "l_O_I_should_not_be_used"
}) })
results.append(json) results.append(res)
if var.is_constant is True: if var.is_constant is True:
# For ERC20 compatibility # For ERC20 compatibility
@ -151,15 +144,14 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
continue continue
if not self.is_upper_case_with_underscores(var.name): if not self.is_upper_case_with_underscores(var.name):
info = "Constant '{}' ({}) is not in UPPER_CASE_WITH_UNDERSCORES\n" info = ["Constant ", var," is not in UPPER_CASE_WITH_UNDERSCORES\n"]
info = info.format(var.canonical_name, var.source_mapping_str)
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_variable_to_json(var, json, { res.add(var, {
"target": "variable_constant", "target": "variable_constant",
"convention": "UPPER_CASE_WITH_UNDERSCORES" "convention": "UPPER_CASE_WITH_UNDERSCORES"
}) })
results.append(json) results.append(res)
else: else:
if var.visibility == 'private': if var.visibility == 'private':
@ -167,40 +159,36 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
else: else:
correct_naming = self.is_mixed_case(var.name) correct_naming = self.is_mixed_case(var.name)
if not correct_naming: if not correct_naming:
info = "Variable '{}' ({}) is not in mixedCase\n" info = ["Variable ", var, " is not in mixedCase\n"]
info = info.format(var.canonical_name, var.source_mapping_str)
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_variable_to_json(var, json, { res.add(var, {
"target": "variable", "target": "variable",
"convention": "mixedCase" "convention": "mixedCase"
}) })
results.append(json) results.append(res)
for enum in contract.enums_declared: for enum in contract.enums_declared:
if not self.is_cap_words(enum.name): if not self.is_cap_words(enum.name):
info = "Enum '{}' ({}) is not in CapWords\n" info = ["Enum ", enum, " is not in CapWords\n"]
info = info.format(enum.canonical_name, enum.source_mapping_str)
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_enum_to_json(enum, json, { res.add(enum, {
"target": "enum", "target": "enum",
"convention": "CapWords" "convention": "CapWords"
}) })
results.append(json) results.append(res)
for modifier in contract.modifiers_declared: for modifier in contract.modifiers_declared:
if not self.is_mixed_case(modifier.name): if not self.is_mixed_case(modifier.name):
info = "Modifier '{}' ({}) is not in mixedCase\n" info = ["Modifier ", modifier, " is not in mixedCase\n"]
info = info.format(modifier.canonical_name,
modifier.source_mapping_str)
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_function_to_json(modifier, json, { res.add(modifier, {
"target": "modifier", "target": "modifier",
"convention": "mixedCase" "convention": "mixedCase"
}) })
results.append(json) results.append(res)
return results return results

@ -69,16 +69,14 @@ class Timestamp(AbstractDetector):
dangerous_timestamp = self.detect_dangerous_timestamp(c) dangerous_timestamp = self.detect_dangerous_timestamp(c)
for (func, nodes) in dangerous_timestamp: for (func, nodes) in dangerous_timestamp:
info = "{} ({}) uses timestamp for comparisons\n" info = [func, " uses timestamp for comparisons\n"]
info = info.format(func.canonical_name,
func.source_mapping_str) info += ['\tDangerous comparisons:\n']
info += '\tDangerous comparisons:\n'
for node in nodes: for node in nodes:
info += '\t- {} ({})\n'.format(node.expression, node.source_mapping_str) info += ['\t- ', node, '\n']
res = self.generate_result(info)
json = self.generate_json_result(info) results.append(res)
self.add_function_to_json(func, json)
self.add_nodes_to_json(nodes, json)
results.append(json)
return results return results

@ -48,14 +48,13 @@ class LowLevelCalls(AbstractDetector):
for c in self.contracts: for c in self.contracts:
values = self.detect_low_level_calls(c) values = self.detect_low_level_calls(c)
for func, nodes in values: for func, nodes in values:
info = "Low level call in {} ({}):\n" info = ["Low level call in ", func,":\n"]
info = info.format(func.canonical_name, func.source_mapping_str)
for node in nodes: for node in nodes:
info += "\t-{} {}\n".format(str(node.expression), node.source_mapping_str) info += ['\t- ', node, '\n']
res = self.generate_result(info)
json = self.generate_json_result(info) results.append(res)
self.add_function_to_json(func, json)
self.add_nodes_to_json(nodes, json)
results.append(json)
return results return results

@ -74,17 +74,11 @@ contract MyConc{
if unused_return: if unused_return:
for node in unused_return: for node in unused_return:
info = "{} ({}) ignores return value by {} \"{}\" ({})\n" info = [f, f" ignores return value by ", node, "\n"]
info = info.format(f.canonical_name,
f.source_mapping_str, res = self.generate_result(info)
self._txt_description,
node.expression, results.append(res)
node.source_mapping_str)
json = self.generate_json_result(info)
self.add_node_to_json(node, json)
self.add_function_to_json(f, json)
results.append(json)
return results return results

@ -36,12 +36,10 @@ By reading B's constructor definition, the reader might assume that `A()` initia
for constructor_call in cst.explicit_base_constructor_calls_statements: for constructor_call in cst.explicit_base_constructor_calls_statements:
for node in constructor_call.nodes: for node in constructor_call.nodes:
if any(isinstance(ir, Nop) for ir in node.irs): if any(isinstance(ir, Nop) for ir in node.irs):
info = "Void constructor called in {} ({}):\n" info = ["Void constructor called in ", cst, ":\n"]
info = info.format(cst.canonical_name, cst.source_mapping_str) info += ["\t- ", node, "\n"]
info += "\t-{} {}\n".format(str(node.expression), node.source_mapping_str)
res = self.generate_result(info)
json = self.generate_json_result(info)
self.add_function_to_json(cst, json) results.append(res)
self.add_nodes_to_json([node], json)
results.append(json)
return results return results

@ -36,6 +36,8 @@ Only report reentrancy that acts as a double call (see `reentrancy-eth`, `reentr
WIKI_RECOMMENDATION = 'Apply the [check-effects-interactions pattern](http://solidity.readthedocs.io/en/v0.4.21/security-considerations.html#re-entrancy).' WIKI_RECOMMENDATION = 'Apply the [check-effects-interactions pattern](http://solidity.readthedocs.io/en/v0.4.21/security-considerations.html#re-entrancy).'
STANDARD_JSON = False
def find_reentrancies(self): def find_reentrancies(self):
result = {} result = {}
for contract in self.contracts: for contract in self.contracts:
@ -78,28 +80,29 @@ Only report reentrancy that acts as a double call (see `reentrancy-eth`, `reentr
for (func, calls, send_eth), varsWritten in result_sorted: for (func, calls, send_eth), varsWritten in result_sorted:
calls = sorted(list(set(calls)), key=lambda x: x.node_id) calls = sorted(list(set(calls)), key=lambda x: x.node_id)
send_eth = sorted(list(set(send_eth)), key=lambda x: x.node_id) send_eth = sorted(list(set(send_eth)), key=lambda x: x.node_id)
info = 'Reentrancy in {} ({}):\n' info = ['Reentrancy in ', func, ':\n']
info = info.format(func.canonical_name, func.source_mapping_str)
info += '\tExternal calls:\n' info += ['\tExternal calls:\n']
for call_info in calls: for call_info in calls:
info += '\t- {} ({})\n'.format(call_info.expression, call_info.source_mapping_str) info += ['\t- ' , call_info, '\n']
if calls != send_eth and send_eth: if calls != send_eth and send_eth:
info += '\tExternal calls sending eth:\n' info += ['\tExternal calls sending eth:\n']
for call_info in send_eth: for call_info in send_eth:
info += '\t- {} ({})\n'.format(call_info.expression, call_info.source_mapping_str) info += ['\t- ', call_info, '\n']
info += '\tState variables written after the call(s):\n' info += ['\tState variables written after the call(s):\n']
for (v, node) in sorted(varsWritten, key=lambda x: (x[0].name, x[1].node_id)): for (v, node) in sorted(varsWritten, key=lambda x: (x[0].name, x[1].node_id)):
info += '\t- {} ({})\n'.format(v, node.source_mapping_str) info += ['\t- ', v, ' in ', node, '\n']
# Create our JSON result # Create our JSON result
json = self.generate_json_result(info) res = self.generate_result(info)
# Add the function with the re-entrancy first # Add the function with the re-entrancy first
self.add_function_to_json(func, json) res.add(func)
# Add all underlying calls in the function which are potentially problematic. # Add all underlying calls in the function which are potentially problematic.
for call_info in calls: for call_info in calls:
self.add_node_to_json(call_info, json, { res.add(call_info, {
"underlying_type": "external_calls" "underlying_type": "external_calls"
}) })
@ -108,18 +111,18 @@ Only report reentrancy that acts as a double call (see `reentrancy-eth`, `reentr
# If the calls are not the same ones that send eth, add the eth sending nodes. # If the calls are not the same ones that send eth, add the eth sending nodes.
if calls != send_eth: if calls != send_eth:
for call_info in send_eth: for call_info in send_eth:
self.add_node_to_json(call_info, json, { res.add(call_info, {
"underlying_type": "external_calls_sending_eth" "underlying_type": "external_calls_sending_eth"
}) })
# Add all variables written via nodes which write them. # Add all variables written via nodes which write them.
for (v, node) in varsWritten: for (v, node) in varsWritten:
self.add_node_to_json(node, json, { res.add(node, {
"underlying_type": "variables_written", "underlying_type": "variables_written",
"variable_name": v.name "variable_name": v.name
}) })
# Append our result # Append our result
results.append(json) results.append(res)
return results return results

@ -37,6 +37,7 @@ Bob uses the re-entrancy bug to call `withdrawBalance` two times, and withdraw m
WIKI_RECOMMENDATION = 'Apply the [check-effects-interactions pattern](http://solidity.readthedocs.io/en/v0.4.21/security-considerations.html#re-entrancy).' WIKI_RECOMMENDATION = 'Apply the [check-effects-interactions pattern](http://solidity.readthedocs.io/en/v0.4.21/security-considerations.html#re-entrancy).'
STANDARD_JSON = False
def find_reentrancies(self): def find_reentrancies(self):
result = {} result = {}
@ -81,28 +82,27 @@ Bob uses the re-entrancy bug to call `withdrawBalance` two times, and withdraw m
calls = sorted(list(set(calls)), key=lambda x: x.node_id) calls = sorted(list(set(calls)), key=lambda x: x.node_id)
send_eth = sorted(list(set(send_eth)), key=lambda x: x.node_id) send_eth = sorted(list(set(send_eth)), key=lambda x: x.node_id)
info = 'Reentrancy in {} ({}):\n' info = ['Reentrancy in ', func, ':\n']
info = info.format(func.canonical_name, func.source_mapping_str) info += ['\tExternal calls:\n']
info += '\tExternal calls:\n'
for call_info in calls: for call_info in calls:
info += '\t- {} ({})\n'.format(call_info.expression, call_info.source_mapping_str) info += ['\t- ' , call_info, '\n']
if calls != send_eth: if calls != send_eth and send_eth:
info += '\tExternal calls sending eth:\n' info += ['\tExternal calls sending eth:\n']
for call_info in send_eth: for call_info in send_eth:
info += '\t- {} ({})\n'.format(call_info.expression, call_info.source_mapping_str) info += ['\t- ', call_info, '\n']
info += '\tState variables written after the call(s):\n' info += ['\tState variables written after the call(s):\n']
for (v, node) in sorted(varsWritten, key=lambda x: (x[0].name, x[1].node_id)): for (v, node) in sorted(varsWritten, key=lambda x: (x[0].name, x[1].node_id)):
info += '\t- {} ({})\n'.format(v, node.source_mapping_str) info += ['\t- ', v, ' in ', node, '\n']
# Create our JSON result # Create our JSON result
json = self.generate_json_result(info) res = self.generate_result(info)
# Add the function with the re-entrancy first # Add the function with the re-entrancy first
self.add_function_to_json(func, json) res.add(func)
# Add all underlying calls in the function which are potentially problematic. # Add all underlying calls in the function which are potentially problematic.
for call_info in calls: for call_info in calls:
self.add_node_to_json(call_info, json, { res.add(call_info, {
"underlying_type": "external_calls" "underlying_type": "external_calls"
}) })
@ -111,18 +111,18 @@ Bob uses the re-entrancy bug to call `withdrawBalance` two times, and withdraw m
# If the calls are not the same ones that send eth, add the eth sending nodes. # If the calls are not the same ones that send eth, add the eth sending nodes.
if calls != send_eth: if calls != send_eth:
for call_info in send_eth: for call_info in send_eth:
self.add_node_to_json(call_info, json, { res.add(call_info, {
"underlying_type": "external_calls_sending_eth" "underlying_type": "external_calls_sending_eth"
}) })
# Add all variables written via nodes which write them. # Add all variables written via nodes which write them.
for (v, node) in varsWritten: for (v, node) in varsWritten:
self.add_node_to_json(node, json, { res.add(node, {
"underlying_type": "variables_written", "underlying_type": "variables_written",
"variable_name": v.name "variable_name": v.name
}) })
# Append our result # Append our result
results.append(json) results.append(res)
return results return results

@ -36,6 +36,8 @@ Do not report reentrancies that involve ethers (see `reentrancy-eth`)'''
''' '''
WIKI_RECOMMENDATION = 'Apply the [check-effects-interactions pattern](http://solidity.readthedocs.io/en/v0.4.21/security-considerations.html#re-entrancy).' WIKI_RECOMMENDATION = 'Apply the [check-effects-interactions pattern](http://solidity.readthedocs.io/en/v0.4.21/security-considerations.html#re-entrancy).'
STANDARD_JSON = False
def find_reentrancies(self): def find_reentrancies(self):
result = {} result = {}
for contract in self.contracts: for contract in self.contracts:
@ -75,35 +77,36 @@ Do not report reentrancies that involve ethers (see `reentrancy-eth`)'''
result_sorted = sorted(list(reentrancies.items()), key=lambda x:x[0][0].name) result_sorted = sorted(list(reentrancies.items()), key=lambda x:x[0][0].name)
for (func, calls), varsWritten in result_sorted: for (func, calls), varsWritten in result_sorted:
calls = sorted(list(set(calls)), key=lambda x: x.node_id) calls = sorted(list(set(calls)), key=lambda x: x.node_id)
info = 'Reentrancy in {} ({}):\n'
info = info.format(func.canonical_name, func.source_mapping_str) info = ['Reentrancy in ', func, ':\n']
info += '\tExternal calls:\n'
info += ['\tExternal calls:\n']
for call_info in calls: for call_info in calls:
info += '\t- {} ({})\n'.format(call_info.expression, call_info.source_mapping_str) info += ['\t- ', call_info, '\n']
info += '\tState variables written after the call(s):\n' info += '\tState variables written after the call(s):\n'
for (v, node) in sorted(varsWritten, key=lambda x: (x[0].name, x[1].node_id)): for (v, node) in sorted(varsWritten, key=lambda x: (x[0].name, x[1].node_id)):
info += '\t- {} ({})\n'.format(v, node.source_mapping_str) info += ['\t- ', v, ' in ', node, '\n']
# Create our JSON result # Create our JSON result
json = self.generate_json_result(info) res = self.generate_result(info)
# Add the function with the re-entrancy first # Add the function with the re-entrancy first
self.add_function_to_json(func, json) res.add(func)
# Add all underlying calls in the function which are potentially problematic. # Add all underlying calls in the function which are potentially problematic.
for call_info in calls: for call_info in calls:
self.add_node_to_json(call_info, json, { res.add(call_info, {
"underlying_type": "external_calls" "underlying_type": "external_calls"
}) })
# Add all variables written via nodes which write them. # Add all variables written via nodes which write them.
for (v, node) in varsWritten: for (v, node) in varsWritten:
self.add_node_to_json(node, json, { res.add(node, {
"underlying_type": "variables_written", "underlying_type": "variables_written",
"variable_name": v.name "variable_name": v.name
}) })
# Append our result # Append our result
results.append(json) results.append(res)
return results return results

@ -65,14 +65,12 @@ contract DerivedContract is BaseContract{
for all_variables in shadowing: for all_variables in shadowing:
shadow = all_variables[0] shadow = all_variables[0]
variables = all_variables[1:] variables = all_variables[1:]
info = '{} ({}) shadows:\n'.format(shadow.canonical_name, info = [shadow, ' shadows:\n']
shadow.source_mapping_str)
for var in variables: for var in variables:
info += "\t- {} ({})\n".format(var.canonical_name, info += ["\t- ", var, "\n"]
var.source_mapping_str)
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_variables_to_json(all_variables, json)
results.append(json) results.append(res)
return results return results

@ -77,7 +77,7 @@ contract Bug {
results = [] results = []
for local in function_or_modifier.variables: for local in function_or_modifier.variables:
if self.is_builtin_symbol(local.name): if self.is_builtin_symbol(local.name):
results.append((self.SHADOWING_LOCAL_VARIABLE, local, function_or_modifier)) results.append((self.SHADOWING_LOCAL_VARIABLE, local))
return results return results
def detect_builtin_shadowing_definitions(self, contract): def detect_builtin_shadowing_definitions(self, contract):
@ -92,18 +92,18 @@ contract Bug {
# Loop through all functions, modifiers, variables (state and local) to detect any built-in symbol keywords. # Loop through all functions, modifiers, variables (state and local) to detect any built-in symbol keywords.
for function in contract.functions_declared: for function in contract.functions_declared:
if self.is_builtin_symbol(function.name): if self.is_builtin_symbol(function.name):
result.append((self.SHADOWING_FUNCTION, function, None)) result.append((self.SHADOWING_FUNCTION, function))
result += self.detect_builtin_shadowing_locals(function) result += self.detect_builtin_shadowing_locals(function)
for modifier in contract.modifiers_declared: for modifier in contract.modifiers_declared:
if self.is_builtin_symbol(modifier.name): if self.is_builtin_symbol(modifier.name):
result.append((self.SHADOWING_MODIFIER, modifier, None)) result.append((self.SHADOWING_MODIFIER, modifier))
result += self.detect_builtin_shadowing_locals(modifier) result += self.detect_builtin_shadowing_locals(modifier)
for variable in contract.state_variables_declared: for variable in contract.state_variables_declared:
if self.is_builtin_symbol(variable.name): if self.is_builtin_symbol(variable.name):
result.append((self.SHADOWING_STATE_VARIABLE, variable, None)) result.append((self.SHADOWING_STATE_VARIABLE, variable))
for event in contract.events_declared: for event in contract.events_declared:
if self.is_builtin_symbol(event.name): if self.is_builtin_symbol(event.name):
result.append((self.SHADOWING_EVENT, event, None)) result.append((self.SHADOWING_EVENT, event))
return result return result
@ -124,27 +124,10 @@ contract Bug {
# Obtain components # Obtain components
shadow_type = shadow[0] shadow_type = shadow[0]
shadow_object = shadow[1] shadow_object = shadow[1]
local_variable_parent = shadow[2]
info = [shadow_object, f' ({shadow_type}) shadows built-in symbol"\n']
# Build the path for our info string
local_variable_path = contract.name + "." res = self.generate_result(info)
if local_variable_parent is not None: results.append(res)
local_variable_path += local_variable_parent.name + "."
local_variable_path += shadow_object.name
info = '{} ({} @ {}) shadows built-in symbol \"{}"\n'.format(local_variable_path,
shadow_type,
shadow_object.source_mapping_str,
shadow_object.name)
# Generate relevant JSON data for this shadowing definition.
json = self.generate_json_result(info)
if shadow_type in [self.SHADOWING_FUNCTION, self.SHADOWING_MODIFIER]:
self.add_function_to_json(shadow_object, json)
elif shadow_type == self.SHADOWING_EVENT:
self.add_event_to_json(shadow_object, json)
elif shadow_type in [self.SHADOWING_STATE_VARIABLE, self.SHADOWING_LOCAL_VARIABLE]:
self.add_variable_to_json(shadow_object, json)
results.append(json)
return results return results

@ -68,23 +68,23 @@ contract Bug {
# Check functions # Check functions
for scope_function in scope_contract.functions_declared: for scope_function in scope_contract.functions_declared:
if variable.name == scope_function.name: if variable.name == scope_function.name:
overshadowed.append((self.OVERSHADOWED_FUNCTION, scope_contract.name, scope_function)) overshadowed.append((self.OVERSHADOWED_FUNCTION, scope_function))
# Check modifiers # Check modifiers
for scope_modifier in scope_contract.modifiers_declared: for scope_modifier in scope_contract.modifiers_declared:
if variable.name == scope_modifier.name: if variable.name == scope_modifier.name:
overshadowed.append((self.OVERSHADOWED_MODIFIER, scope_contract.name, scope_modifier)) overshadowed.append((self.OVERSHADOWED_MODIFIER, scope_modifier))
# Check events # Check events
for scope_event in scope_contract.events_declared: for scope_event in scope_contract.events_declared:
if variable.name == scope_event.name: if variable.name == scope_event.name:
overshadowed.append((self.OVERSHADOWED_EVENT, scope_contract.name, scope_event)) overshadowed.append((self.OVERSHADOWED_EVENT, scope_event))
# Check state variables # Check state variables
for scope_state_variable in scope_contract.state_variables_declared: for scope_state_variable in scope_contract.state_variables_declared:
if variable.name == scope_state_variable.name: if variable.name == scope_state_variable.name:
overshadowed.append((self.OVERSHADOWED_STATE_VARIABLE, scope_contract.name, scope_state_variable)) overshadowed.append((self.OVERSHADOWED_STATE_VARIABLE, scope_state_variable))
# If we have found any overshadowed objects, we'll want to add it to our result list. # If we have found any overshadowed objects, we'll want to add it to our result list.
if overshadowed: if overshadowed:
result.append((contract.name, function.name, variable, overshadowed)) result.append((variable, overshadowed))
return result return result
@ -102,29 +102,15 @@ contract Bug {
shadows = self.detect_shadowing_definitions(contract) shadows = self.detect_shadowing_definitions(contract)
if shadows: if shadows:
for shadow in shadows: for shadow in shadows:
local_parent_name = shadow[1] local_variable = shadow[0]
local_variable = shadow[2] overshadowed = shadow[1]
overshadowed = shadow[3] info = [local_variable, ' shadows:\n']
info = '{}.{}.{} (local variable @ {}) shadows:\n'.format(contract.name,
local_parent_name,
local_variable.name,
local_variable.source_mapping_str)
for overshadowed_entry in overshadowed: for overshadowed_entry in overshadowed:
info += "\t- {}.{} ({} @ {})\n".format(overshadowed_entry[1], info += ["\t- ", overshadowed_entry[1], f" ({overshadowed_entry[0]})\n"]
overshadowed_entry[2],
overshadowed_entry[0],
overshadowed_entry[2].source_mapping_str)
# Generate relevant JSON data for this shadowing definition. # Generate relevant JSON data for this shadowing definition.
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_variable_to_json(local_variable, json)
for overshadowed_entry in overshadowed: results.append(res)
if overshadowed_entry[0] in [self.OVERSHADOWED_FUNCTION, self.OVERSHADOWED_MODIFIER]:
self.add_function_to_json(overshadowed_entry[2], json)
elif overshadowed_entry[0] == self.OVERSHADOWED_EVENT:
self.add_event_to_json(overshadowed_entry[2], json)
elif overshadowed_entry[0] == self.OVERSHADOWED_STATE_VARIABLE:
self.add_variable_to_json(overshadowed_entry[2], json)
results.append(json)
return results return results

@ -76,15 +76,12 @@ contract DerivedContract is BaseContract{
for all_variables in shadowing: for all_variables in shadowing:
shadow = all_variables[0] shadow = all_variables[0]
variables = all_variables[1:] variables = all_variables[1:]
info = '{} ({}) shadows:\n'.format(shadow.canonical_name, info = [shadow, ' shadows:\n']
shadow.source_mapping_str)
for var in variables: for var in variables:
info += "\t- {} ({})\n".format(var.canonical_name, info += ["\t- ", var, "\n"]
var.source_mapping_str)
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_variables_to_json(all_variables, json) results.append(res)
results.append(json)
return results return results

@ -46,6 +46,7 @@ contract Token
WIKI_RECOMMENDATION = 'Special control characters must not be allowed.' WIKI_RECOMMENDATION = 'Special control characters must not be allowed.'
RTLO_CHARACTER_ENCODED = "\u202e".encode('utf-8') RTLO_CHARACTER_ENCODED = "\u202e".encode('utf-8')
STANDARD_JSON = False
def _detect(self): def _detect(self):
results = [] results = []
@ -72,12 +73,11 @@ contract Token
# We have a patch, so pattern.find will return at least one result # We have a patch, so pattern.find will return at least one result
info += f"\t- {pattern.findall(source_encoded)[0]}\n" info += f"\t- {pattern.findall(source_encoded)[0]}\n"
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_other_to_json("rtlo-character", res.add_other("rtlo-character",
(filename, idx, len(self.RTLO_CHARACTER_ENCODED)), (filename, idx, len(self.RTLO_CHARACTER_ENCODED)),
json, self.slither)
self.slither) results.append(res)
results.append(json)
# Advance the start index for the next iteration # Advance the start index for the next iteration
start_index = result_index + 1 start_index = result_index + 1

@ -51,15 +51,12 @@ class Assembly(AbstractDetector):
for c in self.contracts: for c in self.contracts:
values = self.detect_assembly(c) values = self.detect_assembly(c)
for func, nodes in values: for func, nodes in values:
info = "{} uses assembly ({})\n" info = [func, " uses assembly\n"]
info = info.format(func.canonical_name, func.source_mapping_str)
for node in nodes: for node in nodes:
info += "\t- {}\n".format(node.source_mapping_str) info += ["\t- ", node, "\n"]
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_function_to_json(func, json) results.append(res)
self.add_nodes_to_json(nodes, json)
results.append(json)
return results return results

@ -87,11 +87,8 @@ If one of the destinations has a fallback function which reverts, `bad` will alw
for node in values: for node in values:
func = node.function func = node.function
info = "{} has external calls inside a loop: \"{}\" ({})\n" info = [func, " has external calls inside a loop: ", node, "\n"]
info = info.format(func.canonical_name, node.expression, node.source_mapping_str) res = self.generate_result(info)
results.append(res)
json = self.generate_json_result(info)
self.add_node_to_json(node, json)
results.append(json)
return results return results

@ -47,14 +47,11 @@ Bob calls `delegate` and delegates the execution to its malicious contract. As a
continue continue
nodes = self.controlled_delegatecall(f) nodes = self.controlled_delegatecall(f)
if nodes: if nodes:
func_info = '{}.{} ({}) uses delegatecall to a input-controlled function id\n' func_info = [f, ' uses delegatecall to a input-controlled function id\n']
func_info = func_info.format(contract.name, f.name, f.source_mapping_str)
for node in nodes:
node_info = func_info + '\t- {} ({})\n'.format(node.expression, node.source_mapping_str)
json = self.generate_json_result(node_info) for node in nodes:
self.add_node_to_json(node, json) node_info = func_info + ['\t- ', node,'\n']
self.add_function_to_json(f, json) res = self.generate_result(node_info)
results.append(json) results.append(res)
return results return results

@ -152,20 +152,12 @@ contract ContractWithDeprecatedReferences {
for deprecated_reference in deprecated_references: for deprecated_reference in deprecated_references:
source_object = deprecated_reference[0] source_object = deprecated_reference[0]
deprecated_entries = deprecated_reference[1] deprecated_entries = deprecated_reference[1]
info = 'Deprecated standard detected @ {}:\n'.format(source_object.source_mapping_str) info = ['Deprecated standard detected ', source_object, ':\n']
for (dep_id, original_desc, recommended_disc) in deprecated_entries: for (dep_id, original_desc, recommended_disc) in deprecated_entries:
info += "\t- Usage of \"{}\" should be replaced with \"{}\"\n".format(original_desc, info += [f"\t- Usage of \"{original_desc}\" should be replaced with \"{recommended_disc}\"\n"]
recommended_disc)
res = self.generate_result(info)
# Generate relevant JSON data for this deprecated standard. results.append(res)
json = self.generate_json_result(info)
if isinstance(source_object, StateVariableSolc) or isinstance(source_object, StateVariable):
self.add_variable_to_json(source_object, json)
else:
self.add_nodes_to_json([source_object], json)
results.append(json)
return results return results

@ -111,19 +111,16 @@ contract Crowdsale{
ret = sorted(list(ret.items()), key=lambda x:x[0].name) ret = sorted(list(ret.items()), key=lambda x:x[0].name)
for f, nodes in ret: for f, nodes in ret:
func_info = "{} ({}) uses a dangerous strict equality:\n".format(f.canonical_name, func_info = [f, " uses a dangerous strict equality:\n"]
f.source_mapping_str)
# sort the nodes to get deterministic results # sort the nodes to get deterministic results
nodes.sort(key=lambda x: x.node_id) nodes.sort(key=lambda x: x.node_id)
# Output each node with the function info header as a separate result. # Output each node with the function info header as a separate result.
for node in nodes: for node in nodes:
node_info = func_info + f"\t- {str(node.expression)}\n" node_info = func_info + [f"\t- ", node, "\n"]
json = self.generate_json_result(node_info) res = self.generate_result(node_info)
self.add_node_to_json(node, json) results.append(res)
self.add_function_to_json(f, json)
results.append(json)
return results return results

@ -64,15 +64,12 @@ Use:
# iterate over all the nodes # iterate over all the nodes
ret = self._detect_too_many_digits(f) ret = self._detect_too_many_digits(f)
if ret: if ret:
func_info = '{}.{} ({}) uses literals with too many digits:'.format(f.contract.name, func_info = [f, ' uses literals with too many digits:']
f.name,
f.source_mapping_str)
for node in ret: for node in ret:
node_info = func_info + '\n\t- {}\n'.format(node.expression) node_info = func_info + ['\n\t- ', node,'\n']
# Add the result in result # Add the result in result
json = self.generate_json_result(node_info) res = self.generate_result(node_info)
self.add_node_to_json(node, json) results.append(res)
results.append(json)
return results return results

@ -68,12 +68,8 @@ Bob is the owner of `TxOrigin`. Bob calls Eve's contract. Eve's contract calls `
for func, nodes in values: for func, nodes in values:
for node in nodes: for node in nodes:
info = "{} uses tx.origin for authorization: \"{}\" ({})\n".format(func.canonical_name, info = [func, " uses tx.origin for authorization: ", node, "\n"]
node.expression, res = self.generate_result(info)
node.source_mapping_str) results.append(res)
json = self.generate_json_result(info)
self.add_node_to_json(node, json)
results.append(json)
return results return results

@ -88,11 +88,8 @@ class ConstCandidateStateVars(AbstractDetector):
# Create a result for each finding # Create a result for each finding
for v in constable_variables: for v in constable_variables:
info = "{} should be constant ({})\n".format(v.canonical_name, info = [v, " should be constant\n"]
v.source_mapping_str) json = self.generate_result(info)
json = self.generate_json_result(info)
self.add_variable_to_json(v, json)
results.append(json) results.append(json)
return results return results

@ -97,17 +97,9 @@ Bob calls `transfer`. As a result, the ethers are sent to the address 0x0 and ar
self._detect_uninitialized(function, function.entry_point, []) self._detect_uninitialized(function, function.entry_point, [])
all_results = list(set(self.results)) all_results = list(set(self.results))
for(function, uninitialized_local_variable) in all_results: for(function, uninitialized_local_variable) in all_results:
var_name = uninitialized_local_variable.name
info = "{} in {} ({}) is a local variable never initialiazed\n" info = [uninitialized_local_variable, " is a local variable never initialiazed\n"]
info = info.format(var_name, json = self.generate_result(info)
function.canonical_name,
uninitialized_local_variable.source_mapping_str)
json = self.generate_json_result(info)
self.add_variable_to_json(uninitialized_local_variable, json)
self.add_function_to_json(function, json)
results.append(json) results.append(json)
return results return results

@ -87,18 +87,13 @@ Initialize all the variables. If a variable is meant to be initialized to zero,
for c in self.slither.contracts_derived: for c in self.slither.contracts_derived:
ret = self.detect_uninitialized(c) ret = self.detect_uninitialized(c)
for variable, functions in ret: for variable, functions in ret:
info = "{} ({}) is never initialized. It is used in:\n"
info = info.format(variable.canonical_name,
variable.source_mapping_str)
for f in functions:
info += "\t- {} ({})\n".format(f.name, f.source_mapping_str)
source = [variable.source_mapping] info = [variable, " is never initialized. It is used in:\n"]
source += [f.source_mapping for f in functions]
for f in functions:
info += ["\t- ", f, "\n"]
json = self.generate_json_result(info) json = self.generate_result(info)
self.add_variable_to_json(variable, json)
self.add_functions_to_json(functions, json)
results.append(json) results.append(json)
return results return results

@ -101,14 +101,8 @@ Bob calls `func`. As a result, `owner` is override to 0.
self._detect_uninitialized(function, function.entry_point, []) self._detect_uninitialized(function, function.entry_point, [])
for(function, uninitialized_storage_variable) in self.results: for(function, uninitialized_storage_variable) in self.results:
var_name = uninitialized_storage_variable.name info = [uninitialized_storage_variable, " is a storage variable never initialiazed\n"]
json = self.generate_result(info)
info = "{} in {} ({}) is a storage variable never initialiazed\n"
info = info.format(var_name, function.canonical_name, uninitialized_storage_variable.source_mapping_str)
json = self.generate_json_result(info)
self.add_variable_to_json(uninitialized_storage_variable, json)
self.add_function_to_json(function, json)
results.append(json) results.append(json)
return results return results

@ -60,13 +60,8 @@ class UnusedStateVars(AbstractDetector):
unusedVars = self.detect_unused(c) unusedVars = self.detect_unused(c)
if unusedVars: if unusedVars:
for var in unusedVars: for var in unusedVars:
info = "{} ({}) is never used in {}\n".format(var.canonical_name, info = [var, " is never used in ", c, "\n"]
var.source_mapping_str, json = self.generate_result(info)
c.name)
json = self.generate_json_result(info)
self.add_variable_to_json(var, json)
self.add_contract_to_json(c, json)
results.append(json) results.append(json)
return results return results

@ -1,6 +1,6 @@
import abc import abc
from slither.utils import json_utils from slither.utils import output
class IncorrectPrinterInitialization(Exception): class IncorrectPrinterInitialization(Exception):
@ -33,38 +33,14 @@ class AbstractPrinter(metaclass=abc.ABCMeta):
self.logger.info(info) self.logger.info(info)
def generate_json_result(self, info, additional_fields=None): def generate_output(self, info, additional_fields=None):
if additional_fields is None: if additional_fields is None:
additional_fields = {} additional_fields = {}
d = json_utils.generate_json_result(info, additional_fields) d = output.Output(info, additional_fields)
d['printer'] = self.ARGUMENT d.data['printer'] = self.ARGUMENT
return d return d
@staticmethod
def add_contract_to_json(e, d, additional_fields=None):
json_utils.add_contract_to_json(e, d, additional_fields=additional_fields)
@staticmethod
def add_function_to_json(e, d, additional_fields=None):
json_utils.add_function_to_json(e, d, additional_fields=additional_fields)
@staticmethod
def add_functions_to_json(e, d, additional_fields=None):
json_utils.add_functions_to_json(e, d, additional_fields=additional_fields)
@staticmethod
def add_file_to_json(e, content, d, additional_fields=None):
json_utils.add_file_to_json(e, content, d, additional_fields)
@staticmethod
def add_pretty_table_to_json(e, content, d, additional_fields=None):
json_utils.add_pretty_table_to_json(e, content, d, additional_fields)
@staticmethod
def add_other_to_json(name, source_mapping, d, slither, additional_fields=None):
json_utils.add_other_to_json(name, source_mapping, d, slither, additional_fields)
@abc.abstractmethod @abc.abstractmethod
def output(self, filename): def output(self, filename):
"""TODO Documentation""" """TODO Documentation"""

@ -176,9 +176,9 @@ class PrinterCallGraph(AbstractPrinter):
results.append((filename, content)) results.append((filename, content))
self.info(info) self.info(info)
json = self.generate_json_result(info) res = self.generate_output(info)
for filename, content in results: for filename, content in results:
self.add_file_to_json(filename, content, json) res.add_file(filename, content)
return json return res

@ -48,8 +48,8 @@ class PrinterWrittenVariablesAndAuthorization(AbstractPrinter):
txt += str(table) + '\n' txt += str(table) + '\n'
self.info(txt) self.info(txt)
json = self.generate_json_result(txt) res = self.generate_output(txt)
for name, table in all_tables: for name, table in all_tables:
self.add_pretty_table_to_json(table, name, json) res.add_pretty_table(table, name)
return json return res

@ -31,7 +31,7 @@ class CFG(AbstractPrinter):
self.info(info) self.info(info)
json = self.generate_json_result(info) res = self.generate_output(info)
for filename, content in all_files: for filename, content in all_files:
self.add_file_to_json(filename, content, json) res.add_file(filename, content)
return json return res

@ -140,4 +140,6 @@ class Echidna(AbstractPrinter):
self.info(json.dumps(d, indent=4)) self.info(json.dumps(d, indent=4))
return d res = self.generate_output(json.dumps(d, indent=4))
return res

@ -75,6 +75,6 @@ class PrinterInheritance(AbstractPrinter):
result['base_to_child'][base.name]['not_immediate'] = list(map(str, immediate)) result['base_to_child'][base.name]['not_immediate'] = list(map(str, immediate))
self.info(info) self.info(info)
json = self.generate_json_result(info, additional_fields=result) res = self.generate_output(info, additional_fields=result)
return json return res

@ -172,7 +172,7 @@ class PrinterInheritanceGraph(AbstractPrinter):
with open(filename, 'w', encoding='utf8') as f: with open(filename, 'w', encoding='utf8') as f:
f.write(content) f.write(content)
json = self.generate_json_result(info) res = self.generate_output(info)
self.add_file_to_json(filename, content, json) res.add_file(filename, content)
return json return res

@ -2,46 +2,51 @@
Module printing summary of the contract Module printing summary of the contract
""" """
from slither.printers.abstract_printer import AbstractPrinter from slither.printers.abstract_printer import AbstractPrinter
from slither.utils import output
class ConstructorPrinter(AbstractPrinter): class ConstructorPrinter(AbstractPrinter):
WIKI = 'https://github.com/crytic/slither/wiki/Printer-documentation#constructor-calls' WIKI = 'https://github.com/crytic/slither/wiki/Printer-documentation#constructor-calls'
ARGUMENT = 'constructor-calls' ARGUMENT = 'constructor-calls'
HELP = 'Print the constructors executed' HELP = 'Print the constructors executed'
def _get_soruce_code(self,cst): def _get_soruce_code(self, cst):
src_mapping = cst.source_mapping src_mapping = cst.source_mapping
content= self.slither.source_code[src_mapping['filename_absolute']] content = self.slither.source_code[src_mapping['filename_absolute']]
start = src_mapping['start'] start = src_mapping['start']
end = src_mapping['start'] + src_mapping['length'] end = src_mapping['start'] + src_mapping['length']
initial_space = src_mapping['starting_column'] initial_space = src_mapping['starting_column']
return ' ' * initial_space + content[start:end] return ' ' * initial_space + content[start:end]
def output(self, _filename):
info = ''
for contract in self.contracts:
stack_name = []
stack_definition = []
info += "\n\nContact Name: " + contract.name
info += " Constructor Call Sequence: "
cst = contract.constructors_declared
if cst:
stack_name.append(contract.name)
stack_definition.append(self._get_soruce_code(cst))
for inherited_contract in contract.inheritance:
cst = inherited_contract.constructors_declared
if cst:
stack_name.append(inherited_contract.name)
stack_definition.append(self._get_soruce_code(cst))
if len(stack_name) > 0:
info += " " + ' '.join(stack_name[len(stack_name) - 1])
count = len(stack_name) - 2
while count >= 0:
info += "-->" + ' '.join(stack_name[count])
count = count - 1
info += "\n Constructor Definitions:"
count = len(stack_definition) - 1
while count >= 0:
info += "\n Contract name:" + str(stack_name[count])
info += "\n" + str(stack_definition[count])
count = count - 1
def output(self,_filename): self.info(info)
for contract in self.contracts: res = output.Output(info)
stack_name = [] return res
stack_definition = []
print("\n\nContact Name:",contract.name)
print(" Constructor Call Sequence: ", sep=' ', end='', flush=True)
cst = contract.constructors_declared
if cst:
stack_name.append(contract.name)
stack_definition.append(self._get_soruce_code(cst))
for inherited_contract in contract.inheritance:
cst = inherited_contract.constructors_declared
if cst:
stack_name.append(inherited_contract.name)
stack_definition.append(self._get_soruce_code(cst))
if len(stack_name)>0:
print(" ",stack_name[len(stack_name)-1], sep=' ', end='', flush=True)
count = len(stack_name)-2
while count>=0:
print("-->",stack_name[count], sep=' ', end='', flush=True)
count= count-1
print("\n Constructor Definitions:")
count = len(stack_definition)-1
while count>=0:
print("\n Contract name:", stack_name[count])
print ("\n", stack_definition[count])
count = count-1

@ -3,6 +3,7 @@
""" """
import collections import collections
from slither.printers.abstract_printer import AbstractPrinter from slither.printers.abstract_printer import AbstractPrinter
from slither.utils import output
from slither.utils.colors import blue, green, magenta from slither.utils.colors import blue, green, magenta
@ -24,7 +25,7 @@ class ContractSummary(AbstractPrinter):
all_contracts = [] all_contracts = []
for c in self.contracts: for c in self.contracts:
txt += blue("\n+ Contract %s\n" % c.name) txt += blue("\n+ Contract %s\n" % c.name)
additional_fields = {"elements": []} additional_fields = output.Output('')
# Order the function with # Order the function with
# contract_declarer -> list_functions # contract_declarer -> list_functions
@ -47,15 +48,15 @@ class ContractSummary(AbstractPrinter):
if function.visibility not in ['external', 'public', 'internal', 'private']: if function.visibility not in ['external', 'public', 'internal', 'private']:
txt += " - {}  ({})\n".format(function, function.visibility) txt += " - {}  ({})\n".format(function, function.visibility)
self.add_function_to_json(function, additional_fields, additional_fields={"visibility": additional_fields.add(function, additional_fields={"visibility":
function.visibility}) function.visibility})
all_contracts.append((c, additional_fields)) all_contracts.append((c, additional_fields.data))
self.info(txt) self.info(txt)
json = self.generate_json_result(txt) res = self.generate_output(txt)
for contract, additional_fields in all_contracts: for contract, additional_fields in all_contracts:
self.add_contract_to_json(contract, json, additional_fields=additional_fields) res.add(contract, additional_fields=additional_fields)
return json return res

@ -51,8 +51,8 @@ class DataDependency(AbstractPrinter):
all_txt += txt all_txt += txt
all_tables.append((c.name, table)) all_tables.append((c.name, table))
json = self.generate_json_result(all_txt) res = self.generate_output(all_txt)
for name, table in all_tables: for name, table in all_tables:
self.add_pretty_table_to_json(table, name, json) res.add_pretty_table(table, name)
return json return res

@ -70,8 +70,8 @@ class FunctionSummary(AbstractPrinter):
all_tables.append((name, table)) all_tables.append((name, table))
all_txt += txt all_txt += txt
json = self.generate_json_result(all_txt) res = self.generate_output(all_txt)
for name, table in all_tables: for name, table in all_tables:
self.add_pretty_table_to_json(table, name, json) res.add_pretty_table(table, name)
return json return res

@ -37,8 +37,8 @@ class FunctionIds(AbstractPrinter):
self.info(txt) self.info(txt)
json = self.generate_json_result(txt) res = self.generate_output(txt)
for name, table in all_tables: for name, table in all_tables:
self.add_pretty_table_to_json(table, name, json) res.add_pretty_table(table, name)
return json return res

@ -4,6 +4,7 @@ Module printing summary of the contract
import logging import logging
from slither.printers.abstract_printer import AbstractPrinter from slither.printers.abstract_printer import AbstractPrinter
from slither.utils import output
from slither.utils.code_complexity import compute_cyclomatic_complexity from slither.utils.code_complexity import compute_cyclomatic_complexity
from slither.utils.colors import green, red, yellow from slither.utils.colors import green, red, yellow
from slither.utils.standard_libraries import is_standard_library from slither.utils.standard_libraries import is_standard_library
@ -205,6 +206,7 @@ class PrinterHumanSummary(AbstractPrinter):
'ercs': [], 'ercs': [],
} }
lines_number = self._lines_number() lines_number = self._lines_number()
if lines_number: if lines_number:
total_lines, total_dep_lines = lines_number total_lines, total_dep_lines = lines_number
@ -241,6 +243,7 @@ class PrinterHumanSummary(AbstractPrinter):
self.info(txt) self.info(txt)
results_contract = output.Output('')
for contract in self.slither.contracts_derived: for contract in self.slither.contracts_derived:
optimization, info, low, medium, high = self._get_detectors_result() optimization, info, low, medium, high = self._get_detectors_result()
contract_d = {'contract_name': contract.name, contract_d = {'contract_name': contract.name,
@ -262,9 +265,11 @@ class PrinterHumanSummary(AbstractPrinter):
contract_d['erc20_can_mint'] = False contract_d['erc20_can_mint'] = False
contract_d['erc20_race_condition_mitigated'] = race_condition_mitigated contract_d['erc20_race_condition_mitigated'] = race_condition_mitigated
self.add_contract_to_json(contract, results['contracts'], additional_fields=contract_d) results_contract.add_contract(contract, additional_fields=contract_d)
results['contracts']['elements'] = results_contract.elements
json = self.generate_json_result(txt, additional_fields=results) json = self.generate_output(txt, additional_fields=results)
return json return json

@ -40,8 +40,8 @@ class Modifiers(AbstractPrinter):
txt += "\n"+str(table) txt += "\n"+str(table)
self.info(txt) self.info(txt)
json = self.generate_json_result(all_txt) res = self.generate_output(all_txt)
for name, table in all_tables: for name, table in all_tables:
self.add_pretty_table_to_json(table, name, json) res.add_pretty_table(table, name)
return json return res

@ -45,8 +45,8 @@ class RequireOrAssert(AbstractPrinter):
all_tables.append((contract.name, table)) all_tables.append((contract.name, table))
all_txt += txt all_txt += txt
json = self.generate_json_result(all_txt) res = self.generate_output(all_txt)
for name, table in all_tables: for name, table in all_tables:
self.add_pretty_table_to_json(table, name, json) res.add_pretty_table(table, name)
return json return res

@ -4,8 +4,8 @@
from slither.printers.abstract_printer import AbstractPrinter from slither.printers.abstract_printer import AbstractPrinter
class PrinterSlithIR(AbstractPrinter):
class PrinterSlithIR(AbstractPrinter):
ARGUMENT = 'slithir' ARGUMENT = 'slithir'
HELP = 'Print the slithIR representation of the functions' HELP = 'Print the slithIR representation of the functions'
@ -20,30 +20,32 @@ class PrinterSlithIR(AbstractPrinter):
txt = "" txt = ""
for contract in self.contracts: for contract in self.contracts:
print('Contract {}'.format(contract.name)) txt += 'Contract {}'.format(contract.name)
for function in contract.functions: for function in contract.functions:
print(f'\tFunction {function.canonical_name} {"" if function.is_shadowed else "(*)"}') txt += f'\tFunction {function.canonical_name} {"" if function.is_shadowed else "(*)"}'
for node in function.nodes: for node in function.nodes:
if node.expression: if node.expression:
print('\t\tExpression: {}'.format(node.expression)) txt += '\t\tExpression: {}'.format(node.expression)
print('\t\tIRs:') txt += '\t\tIRs:'
for ir in node.irs: for ir in node.irs:
print('\t\t\t{}'.format(ir)) txt += '\t\t\t{}'.format(ir)
elif node.irs: elif node.irs:
print('\t\tIRs:') txt += '\t\tIRs:'
for ir in node.irs: for ir in node.irs:
print('\t\t\t{}'.format(ir)) txt += '\t\t\t{}'.format(ir)
for modifier_statement in function.modifiers_statements: for modifier_statement in function.modifiers_statements:
print(f'\t\tModifier Call {modifier_statement.entry_point.expression}') txt += f'\t\tModifier Call {modifier_statement.entry_point.expression}'
for modifier_statement in function.explicit_base_constructor_calls_statements: for modifier_statement in function.explicit_base_constructor_calls_statements:
print(f'\t\tConstructor Call {modifier_statement.entry_point.expression}') txt += f'\t\tConstructor Call {modifier_statement.entry_point.expression}'
for modifier in contract.modifiers: for modifier in contract.modifiers:
print('\tModifier {}'.format(modifier.canonical_name)) txt += '\tModifier {}'.format(modifier.canonical_name)
for node in modifier.nodes: for node in modifier.nodes:
print(node) txt += str(node)
if node.expression: if node.expression:
print('\t\tExpression: {}'.format(node.expression)) txt += '\t\tExpression: {}'.format(node.expression)
print('\t\tIRs:') txt += '\t\tIRs:'
for ir in node.irs: for ir in node.irs:
print('\t\t\t{}'.format(ir)) txt += '\t\t\t{}'.format(ir)
self.info(txt) self.info(txt)
res = self.generate_output(txt)
return res

@ -21,24 +21,26 @@ class PrinterSlithIRSSA(AbstractPrinter):
txt = "" txt = ""
for contract in self.contracts: for contract in self.contracts:
print('Contract {}'.format(contract.name)) txt += 'Contract {}'.format(contract.name)
for function in contract.functions: for function in contract.functions:
print('\tFunction {}'.format(function.canonical_name)) txt += '\tFunction {}'.format(function.canonical_name)
for node in function.nodes: for node in function.nodes:
if node.expression: if node.expression:
print('\t\tExpression: {}'.format(node.expression)) txt += '\t\tExpression: {}'.format(node.expression)
if node.irs_ssa: if node.irs_ssa:
print('\t\tIRs:') txt += '\t\tIRs:'
for ir in node.irs_ssa: for ir in node.irs_ssa:
print('\t\t\t{}'.format(ir)) txt += '\t\t\t{}'.format(ir)
for modifier in contract.modifiers: for modifier in contract.modifiers:
print('\tModifier {}'.format(modifier.canonical_name)) txt += '\tModifier {}'.format(modifier.canonical_name)
for node in modifier.nodes: for node in modifier.nodes:
print(node) txt += str(node)
if node.expression: if node.expression:
print('\t\tExpression: {}'.format(node.expression)) txt += '\t\tExpression: {}'.format(node.expression)
if node.irs_ssa: if node.irs_ssa:
print('\t\tIRs:') txt += '\t\tIRs:'
for ir in node.irs_ssa: for ir in node.irs_ssa:
print('\t\t\t{}'.format(ir)) txt += '\t\t\t{}'.format(ir)
self.info(txt) self.info(txt)
res = self.generate_output(txt)
return res

@ -36,8 +36,7 @@ class VariableOrder(AbstractPrinter):
self.info(txt) self.info(txt)
json = self.generate_json_result(txt) res = self.generate_output(txt)
for name, table in all_tables: for name, table in all_tables:
self.add_pretty_table_to_json(table, name, json) res.add_pretty_table(table, name)
return res
return json

@ -68,6 +68,8 @@ class Slither(SlitherSolc):
if kwargs.get('generate_patches', False): if kwargs.get('generate_patches', False):
self.generate_patches = True self.generate_patches = True
self._markdown_root = kwargs.get('markdown_root', "")
self._detectors = [] self._detectors = []
self._printers = [] self._printers = []
@ -163,7 +165,7 @@ class Slither(SlitherSolc):
:return: List of registered printers outputs. :return: List of registered printers outputs.
""" """
return [p.output(self.filename) for p in self._printers] return [p.output(self.filename).data for p in self._printers]
def _check_common_things(self, thing_name, cls, base_cls, instances_list): def _check_common_things(self, thing_name, cls, base_cls, instances_list):

@ -5,7 +5,7 @@ from collections import defaultdict
from slither import Slither from slither import Slither
from crytic_compile import cryticparser from crytic_compile import cryticparser
from slither.utils.erc import ERCS from slither.utils.erc import ERCS
from slither.utils.json_utils import output_json from slither.utils.output import output_to_json
from .erc.ercs import generic_erc_checks from .erc.ercs import generic_erc_checks
from .erc.erc20 import check_erc20 from .erc.erc20 import check_erc20
@ -59,7 +59,7 @@ def parse_args():
def _log_error(err, args): def _log_error(err, args):
if args.json: if args.json:
output_json(args.json, str(err), {"upgradeability-check": []}) output_to_json(args.json, str(err), {"upgradeability-check": []})
logger.error(err) logger.error(err)
@ -92,7 +92,7 @@ def main():
return return
if args.json: if args.json:
output_json(args.json, None, {"upgradeability-check": ret}) output_to_json(args.json, None, {"upgradeability-check": ret})
if __name__ == '__main__': if __name__ == '__main__':

@ -1,6 +1,6 @@
import logging import logging
from slither.utils import json_utils from slither.utils import output
logger = logging.getLogger("Slither-conformance") logger = logging.getLogger("Slither-conformance")
@ -18,9 +18,9 @@ def approval_race_condition(contract, ret):
txt = f'\t[ ] {contract.name} is not protected for the ERC20 approval race condition' txt = f'\t[ ] {contract.name} is not protected for the ERC20 approval race condition'
logger.info(txt) logger.info(txt)
lack_of_erc20_race_condition_protection = json_utils.generate_json_result(txt) lack_of_erc20_race_condition_protection = output.Output(txt)
json_utils.add_contract_to_json(contract, lack_of_erc20_race_condition_protection) lack_of_erc20_race_condition_protection.add(contract)
ret["lack_of_erc20_race_condition_protection"].append(lack_of_erc20_race_condition_protection) ret["lack_of_erc20_race_condition_protection"].append(lack_of_erc20_race_condition_protection.data)
def check_erc20(contract, ret, explored=None): def check_erc20(contract, ret, explored=None):

@ -1,7 +1,7 @@
import logging import logging
from slither.slithir.operations import EventCall from slither.slithir.operations import EventCall
from slither.utils import json_utils from slither.utils import output
from slither.utils.type import export_nested_types_from_variable, export_return_type_from_variable from slither.utils.type import export_nested_types_from_variable, export_return_type_from_variable
logger = logging.getLogger("Slither-conformance") logger = logging.getLogger("Slither-conformance")
@ -25,12 +25,12 @@ def _check_signature(erc_function, contract, ret):
if not state_variable_as_function or not state_variable_as_function.visibility in ['public', 'external']: if not state_variable_as_function or not state_variable_as_function.visibility in ['public', 'external']:
txt = f'[ ] {sig} is missing {"" if required else "(optional)"}' txt = f'[ ] {sig} is missing {"" if required else "(optional)"}'
logger.info(txt) logger.info(txt)
missing_func = json_utils.generate_json_result(txt, additional_fields={ missing_func = output.Output(txt, additional_fields={
"function": sig, "function": sig,
"required": required "required": required
}) })
json_utils.add_contract_to_json(contract, missing_func) missing_func.add(contract)
ret["missing_function"].append(missing_func) ret["missing_function"].append(missing_func.data)
return return
types = [str(x) for x in export_nested_types_from_variable(state_variable_as_function)] types = [str(x) for x in export_nested_types_from_variable(state_variable_as_function)]
@ -38,12 +38,12 @@ def _check_signature(erc_function, contract, ret):
if types != parameters: if types != parameters:
txt = f'[ ] {sig} is missing {"" if required else "(optional)"}' txt = f'[ ] {sig} is missing {"" if required else "(optional)"}'
logger.info(txt) logger.info(txt)
missing_func = json_utils.generate_json_result(txt, additional_fields={ missing_func = output.Output(txt, additional_fields={
"function": sig, "function": sig,
"required": required "required": required
}) })
json_utils.add_contract_to_json(contract, missing_func) missing_func.add(contract)
ret["missing_function"].append(missing_func) ret["missing_function"].append(missing_func.data)
return return
function_return_type = [export_return_type_from_variable(state_variable_as_function)] function_return_type = [export_return_type_from_variable(state_variable_as_function)]
@ -65,12 +65,12 @@ def _check_signature(erc_function, contract, ret):
txt = f'\t[ ] {sig} -> () should return {return_type}' txt = f'\t[ ] {sig} -> () should return {return_type}'
logger.info(txt) logger.info(txt)
incorrect_return = json_utils.generate_json_result(txt, additional_fields={ incorrect_return = output.Output(txt, additional_fields={
"expected_return_type": return_type, "expected_return_type": return_type,
"actual_return_type": function_return_type "actual_return_type": function_return_type
}) })
json_utils.add_function_to_json(function, incorrect_return) incorrect_return.add(function)
ret["incorrect_return_type"].append(incorrect_return) ret["incorrect_return_type"].append(incorrect_return.data)
elif not return_type: elif not return_type:
txt = f'\t[✓] {sig} -> () (correct return type)' txt = f'\t[✓] {sig} -> () (correct return type)'
@ -79,12 +79,12 @@ def _check_signature(erc_function, contract, ret):
txt = f'\t[ ] {sig} -> () should return {return_type}' txt = f'\t[ ] {sig} -> () should return {return_type}'
logger.info(txt) logger.info(txt)
incorrect_return = json_utils.generate_json_result(txt, additional_fields={ incorrect_return = output.Output(txt, additional_fields={
"expected_return_type": return_type, "expected_return_type": return_type,
"actual_return_type": function_return_type "actual_return_type": function_return_type
}) })
json_utils.add_function_to_json(function, incorrect_return) incorrect_return.add(function)
ret["incorrect_return_type"].append(incorrect_return) ret["incorrect_return_type"].append(incorrect_return.data)
if view: if view:
if function_view: if function_view:
@ -94,9 +94,9 @@ def _check_signature(erc_function, contract, ret):
txt = f'\t[ ] {sig} should be view' txt = f'\t[ ] {sig} should be view'
logger.info(txt) logger.info(txt)
should_be_view = json_utils.generate_json_result(txt) should_be_view = output.Output(txt)
json_utils.add_function_to_json(function, should_be_view) should_be_view.add(function)
ret["should_be_view"].append(should_be_view) ret["should_be_view"].append(should_be_view.data)
if events: if events:
for event in events: for event in events:
@ -106,11 +106,11 @@ def _check_signature(erc_function, contract, ret):
txt = f'\t[ ] Must emit be view {event_sig}' txt = f'\t[ ] Must emit be view {event_sig}'
logger.info(txt) logger.info(txt)
missing_event_emmited = json_utils.generate_json_result(txt, additional_fields={ missing_event_emmited = output.Output(txt, additional_fields={
"missing_event": event_sig "missing_event": event_sig
}) })
json_utils.add_function_to_json(function, missing_event_emmited) missing_event_emmited.add(function)
ret["missing_event_emmited"].append(missing_event_emmited) ret["missing_event_emmited"].append(missing_event_emmited.data)
else: else:
event_found = False event_found = False
@ -127,11 +127,11 @@ def _check_signature(erc_function, contract, ret):
txt = f'\t[ ] Must emit be view {event_sig}' txt = f'\t[ ] Must emit be view {event_sig}'
logger.info(txt) logger.info(txt)
missing_event_emmited = json_utils.generate_json_result(txt, additional_fields={ missing_event_emmited = output.Output(txt, additional_fields={
"missing_event": event_sig "missing_event": event_sig
}) })
json_utils.add_function_to_json(function, missing_event_emmited) missing_event_emmited.add(function)
ret["missing_event_emmited"].append(missing_event_emmited) ret["missing_event_emmited"].append(missing_event_emmited.data)
def _check_events(erc_event, contract, ret): def _check_events(erc_event, contract, ret):
@ -146,12 +146,11 @@ def _check_events(erc_event, contract, ret):
txt = f'[ ] {sig} is missing' txt = f'[ ] {sig} is missing'
logger.info(txt) logger.info(txt)
missing_event = json_utils.generate_json_result(txt, additional_fields={ missing_event = output.Output(txt, additional_fields={
"event": sig "event": sig
}) })
json_utils.add_contract_to_json(contract, missing_event) missing_event.add(contract)
ret["missing_event"].append(missing_event) ret["missing_event"].append(missing_event.data)
return return
txt = f'[✓] {sig} is present' txt = f'[✓] {sig} is present'
@ -166,11 +165,11 @@ def _check_events(erc_event, contract, ret):
txt = f'\t[ ] parameter {i} should be indexed' txt = f'\t[ ] parameter {i} should be indexed'
logger.info(txt) logger.info(txt)
missing_event_index = json_utils.generate_json_result(txt, additional_fields={ missing_event_index = output.Output(txt, additional_fields={
"missing_index": i "missing_index": i
}) })
json_utils.add_event_to_json(event, missing_event_index) missing_event_index.add_event(event)
ret["missing_event_index"].append(missing_event_index) ret["missing_event_index"].append(missing_event_index.data)
def generic_erc_checks(contract, erc_functions, erc_events, ret, explored=None): def generic_erc_checks(contract, erc_functions, erc_events, ret, explored=None):

@ -4,17 +4,19 @@ import logging
from slither.core.declarations import Function from slither.core.declarations import Function
from slither.core.variables.variable import Variable from slither.core.variables.variable import Variable
from slither.utils.colors import yellow, green, red from slither.utils.colors import yellow, green, red
from slither.utils import json_utils from slither.utils import output
logging.basicConfig(level=logging.WARNING) logging.basicConfig(level=logging.WARNING)
logger = logging.getLogger('Slither.kspec') logger = logging.getLogger('Slither.kspec')
def _refactor_type(type): def _refactor_type(type):
return { return {
'uint': 'uint256', 'uint': 'uint256',
'int': 'int256' 'int': 'int256'
}.get(type, type) }.get(type, type)
def _get_all_covered_kspec_functions(target): def _get_all_covered_kspec_functions(target):
# Create a set of our discovered functions which are covered # Create a set of our discovered functions which are covered
covered_functions = set() covered_functions = set()
@ -56,9 +58,10 @@ def _get_slither_functions(slither):
# TODO: integrate state variables # TODO: integrate state variables
all_functions_declared += list(set([s for s in slither.state_variables if s.visibility in ['public', 'external']])) all_functions_declared += list(set([s for s in slither.state_variables if s.visibility in ['public', 'external']]))
slither_functions = {(function.contract.name, function.full_name): function for function in all_functions_declared} slither_functions = {(function.contract.name, function.full_name): function for function in all_functions_declared}
return slither_functions return slither_functions
def _generate_output(kspec, message, color, generate_json): def _generate_output(kspec, message, color, generate_json):
info = "" info = ""
for function in kspec: for function in kspec:
@ -67,12 +70,13 @@ def _generate_output(kspec, message, color, generate_json):
logger.info(color(info)) logger.info(color(info))
if generate_json: if generate_json:
json_kspec_present = json_utils.generate_json_result(info) json_kspec_present = output.Output(info)
for function in kspec: for function in kspec:
json_utils.add_function_to_json(function, json_kspec_present) json_kspec_present.add(function)
return json_kspec_present return json_kspec_present.data
return None return None
def _generate_output_unresolved(kspec, message, color, generate_json): def _generate_output_unresolved(kspec, message, color, generate_json):
info = "" info = ""
for contract, function in kspec: for contract, function in kspec:
@ -81,8 +85,8 @@ def _generate_output_unresolved(kspec, message, color, generate_json):
logger.info(color(info)) logger.info(color(info))
if generate_json: if generate_json:
json_kspec_present = json_utils.generate_json_result(info, additional_fields={"signatures": kspec}) json_kspec_present = output.Output(info, additional_fields={"signatures": kspec})
return json_kspec_present return json_kspec_present.data
return None return None
@ -95,7 +99,6 @@ def _run_coverage_analysis(args, slither, kspec_functions):
kspec_functions_resolved = kspec_functions & slither_functions_set kspec_functions_resolved = kspec_functions & slither_functions_set
kspec_functions_unresolved = kspec_functions - kspec_functions_resolved kspec_functions_unresolved = kspec_functions - kspec_functions_resolved
kspec_missing = [] kspec_missing = []
kspec_present = [] kspec_present = []
@ -114,23 +117,24 @@ def _run_coverage_analysis(args, slither, kspec_functions):
red, red,
args.json) args.json)
json_kspec_missing_variables = _generate_output([f for f in kspec_missing if isinstance(f, Variable)], json_kspec_missing_variables = _generate_output([f for f in kspec_missing if isinstance(f, Variable)],
"[ ] (Missing variable)", "[ ] (Missing variable)",
yellow, yellow,
args.json) args.json)
json_kspec_unresolved = _generate_output_unresolved(kspec_functions_unresolved, json_kspec_unresolved = _generate_output_unresolved(kspec_functions_unresolved,
"[ ] (Unresolved)", "[ ] (Unresolved)",
yellow, yellow,
args.json) args.json)
# Handle unresolved kspecs # Handle unresolved kspecs
if args.json: if args.json:
json_utils.output_json(args.json, None, { output.output_to_json(args.json, None, {
"functions_present": json_kspec_present, "functions_present": json_kspec_present,
"functions_missing": json_kspec_missing_functions, "functions_missing": json_kspec_missing_functions,
"variables_missing": json_kspec_missing_variables, "variables_missing": json_kspec_missing_variables,
"functions_unresolved": json_kspec_unresolved "functions_unresolved": json_kspec_unresolved
}) })
def run_analysis(args, slither, kspec): def run_analysis(args, slither, kspec):
# Get all of our kspec'd functions (tuple(contract_name, function_name)). # Get all of our kspec'd functions (tuple(contract_name, function_name)).
if ',' in kspec: if ',' in kspec:
@ -143,4 +147,3 @@ def run_analysis(args, slither, kspec):
# Run coverage analysis # Run coverage analysis
_run_coverage_analysis(args, slither, kspec_functions) _run_coverage_analysis(args, slither, kspec_functions)

@ -7,7 +7,7 @@ from slither import Slither
from crytic_compile import cryticparser from crytic_compile import cryticparser
from slither.exceptions import SlitherException from slither.exceptions import SlitherException
from slither.utils.colors import red from slither.utils.colors import red
from slither.utils.json_utils import output_json from slither.utils.output import output_to_json
from .compare_variables_order import compare_variables_order from .compare_variables_order import compare_variables_order
from .compare_function_ids import compare_function_ids from .compare_function_ids import compare_function_ids
@ -135,7 +135,7 @@ def main():
info = 'Contract {} not found in {}'.format(v1_name, v1.filename) info = 'Contract {} not found in {}'.format(v1_name, v1.filename)
logger.error(red(info)) logger.error(red(info))
if args.json: if args.json:
output_json(args.json, str(info), {"upgradeability-check": json_results}) output_to_json(args.json, str(info), {"upgradeability-check": json_results})
return return
_checks_on_contract(v1_contract, json_results) _checks_on_contract(v1_contract, json_results)
@ -153,7 +153,7 @@ def main():
info = 'Proxy {} not found in {}'.format(args.proxy_name, proxy.filename) info = 'Proxy {} not found in {}'.format(args.proxy_name, proxy.filename)
logger.error(red(info)) logger.error(red(info))
if args.json: if args.json:
output_json(args.json, str(info), {"upgradeability-check": json_results}) output_to_json(args.json, str(info), {"upgradeability-check": json_results})
return return
json_results['proxy-present'] = True json_results['proxy-present'] = True
_checks_on_contract_and_proxy(v1_contract, proxy_contract, json_results) _checks_on_contract_and_proxy(v1_contract, proxy_contract, json_results)
@ -170,7 +170,7 @@ def main():
info = 'New logic contract {} not found in {}'.format(args.new_contract_name, v2.filename) info = 'New logic contract {} not found in {}'.format(args.new_contract_name, v2.filename)
logger.error(red(info)) logger.error(red(info))
if args.json: if args.json:
output_json(args.json, str(info), {"upgradeability-check": json_results}) output_to_json(args.json, str(info), {"upgradeability-check": json_results})
return return
json_results['contract_v2-present'] = True json_results['contract_v2-present'] = True
@ -183,12 +183,12 @@ def main():
_checks_on_contract_update(v1_contract, v2_contract, json_results) _checks_on_contract_update(v1_contract, v2_contract, json_results)
if args.json: if args.json:
output_json(args.json, None, {"upgradeability-check": json_results}) output_to_json(args.json, None, {"upgradeability-check": json_results})
except SlitherException as e: except SlitherException as e:
logger.error(str(e)) logger.error(str(e))
if args.json: if args.json:
output_json(args.json, str(e), {"upgradeability-check": json_results}) output_to_json(args.json, str(e), {"upgradeability-check": json_results})
return return
# endregion # endregion

@ -1,7 +1,7 @@
import logging import logging
from slither.slithir.operations import InternalCall from slither.slithir.operations import InternalCall
from slither.utils import json_utils from slither.utils.output import Output
from slither.utils.colors import red, yellow, green from slither.utils.colors import red, yellow, green
logger = logging.getLogger("Slither-check-upgradeability") logger = logging.getLogger("Slither-check-upgradeability")
@ -74,9 +74,9 @@ def check_initialization(contract):
initializer_modifier_missing = True initializer_modifier_missing = True
info = f'{f.canonical_name} does not call the initializer modifier' info = f'{f.canonical_name} does not call the initializer modifier'
logger.info(red(info)) logger.info(red(info))
json_elem = json_utils.generate_json_result(info) res = Output(info)
json_utils.add_function_to_json(f, json_elem) res.add(f)
results['missing-initializer-modifier'].append(json_elem) results['missing-initializer-modifier'].append(res.data)
if not initializer_modifier_missing: if not initializer_modifier_missing:
logger.info(green('All the init functions have the initializer modifier')) logger.info(green('All the init functions have the initializer modifier'))
@ -103,10 +103,10 @@ def check_initialization(contract):
for f in missing_calls: for f in missing_calls:
info = f'Missing call to {f.canonical_name} in {most_derived_init.canonical_name}' info = f'Missing call to {f.canonical_name} in {most_derived_init.canonical_name}'
logger.info(red(info)) logger.info(red(info))
json_elem = json_utils.generate_json_result(info) res = Output(info)
json_utils.add_function_to_json(f, json_elem, {"is_most_derived_init_function": False}) res.add(f, {"is_most_derived_init_function": False})
json_utils.add_function_to_json(most_derived_init, json_elem, {"is_most_derived_init_function": True}) res.add(most_derived_init, {"is_most_derived_init_function": True})
results['missing-calls'].append(json_elem) results['missing-calls'].append(res.data)
missing_call = True missing_call = True
if not missing_call: if not missing_call:
logger.info(green('No missing call to an init function found')) logger.info(green('No missing call to an init function found'))
@ -117,9 +117,9 @@ def check_initialization(contract):
for f in double_calls: for f in double_calls:
info = f'{f.canonical_name} is called multiple times in {most_derived_init.full_name}' info = f'{f.canonical_name} is called multiple times in {most_derived_init.full_name}'
logger.info(red(info)) logger.info(red(info))
json_elem = json_utils.generate_json_result(info) res = Output(info)
json_utils.add_function_to_json(f, json_elem) res.add(f)
results['multiple-calls'].append(json_elem) results['multiple-calls'].append(res.data)
double_calls_found = True double_calls_found = True
if not double_calls_found: if not double_calls_found:
logger.info(green('No double call to init functions found')) logger.info(green('No double call to init functions found'))
@ -128,9 +128,9 @@ def check_initialization(contract):
init_info = f'{contract.name} needs to be initialized by {most_derived_init.full_name}\n' init_info = f'{contract.name} needs to be initialized by {most_derived_init.full_name}\n'
logger.info(green('Check the deployement script to ensure that these functions are called:\n' + init_info)) logger.info(green('Check the deployement script to ensure that these functions are called:\n' + init_info))
json_elem = json_utils.generate_json_result(init_info) res = Output(init_info)
json_utils.add_function_to_json(most_derived_init, json_elem) res.add(most_derived_init)
results['initialize_target'] = json_elem results['initialize_target'] = res.data
if not error_found: if not error_found:
logger.info(green('No error found')) logger.info(green('No error found'))

@ -1,6 +1,6 @@
import logging import logging
from slither.utils import json_utils from slither.utils import output
from slither.utils.colors import red, green from slither.utils.colors import red, green
logger = logging.getLogger("Slither-check-upgradeability") logger = logging.getLogger("Slither-check-upgradeability")
@ -20,9 +20,9 @@ def check_variable_initialization(contract):
if s.initialized and not s.is_constant: if s.initialized and not s.is_constant:
info = f'{s.canonical_name} has an initial value ({s.source_mapping_str})' info = f'{s.canonical_name} has an initial value ({s.source_mapping_str})'
logger.info(red(info)) logger.info(red(info))
json_elem = json_utils.generate_json_result(info) res = output.Output(info)
json_utils.add_variable_to_json(s, json_elem) res.add(s)
results['variables-initialized'].append(json_elem) results['variables-initialized'].append(res.data)
error_found = True error_found = True
if not error_found: if not error_found:

@ -4,10 +4,9 @@
''' '''
import logging import logging
from slither import Slither
from slither.core.declarations import Function from slither.core.declarations import Function
from slither.exceptions import SlitherError from slither.exceptions import SlitherError
from slither.utils import json_utils from slither.utils.output import Output
from slither.utils.function import get_function_id from slither.utils.function import get_function_id
from slither.utils.colors import red, green from slither.utils.colors import red, green
@ -63,16 +62,10 @@ def compare_function_ids(implem, proxy):
info = f'Function id collision found: {implem_function.canonical_name} ({implem_function.source_mapping_str}) {proxy_function.canonical_name} ({proxy_function.source_mapping_str})' info = f'Function id collision found: {implem_function.canonical_name} ({implem_function.source_mapping_str}) {proxy_function.canonical_name} ({proxy_function.source_mapping_str})'
logger.info(red(info)) logger.info(red(info))
json_elem = json_utils.generate_json_result(info) res = Output(info)
if isinstance(implem_function, Function): res.add(implem_function)
json_utils.add_function_to_json(implem_function, json_elem) res.add(proxy_function)
else: results['function-id-collision'].append(res.data)
json_utils.add_variable_to_json(implem_function, json_elem)
if isinstance(proxy_function, Function):
json_utils.add_function_to_json(proxy_function, json_elem)
else:
json_utils.add_variable_to_json(proxy_function, json_elem)
results['function-id-collision'].append(json_elem)
else: else:
@ -82,17 +75,10 @@ def compare_function_ids(implem, proxy):
info = f'Shadowing between {implem_function.canonical_name} ({implem_function.source_mapping_str}) and {proxy_function.canonical_name} ({proxy_function.source_mapping_str})' info = f'Shadowing between {implem_function.canonical_name} ({implem_function.source_mapping_str}) and {proxy_function.canonical_name} ({proxy_function.source_mapping_str})'
logger.info(red(info)) logger.info(red(info))
json_elem = json_utils.generate_json_result(info) res = Output(info)
json_elem = json_utils.generate_json_result(info) res.add(implem_function)
if isinstance(implem_function, Function): res.add(proxy_function)
json_utils.add_function_to_json(implem_function, json_elem) results['shadowing'].append(res.data)
else:
json_utils.add_variable_to_json(implem_function, json_elem)
if isinstance(proxy_function, Function):
json_utils.add_function_to_json(proxy_function, json_elem)
else:
json_utils.add_variable_to_json(proxy_function, json_elem)
results['shadowing'].append(json_elem)
if not error_found: if not error_found:
logger.info(green('No error found')) logger.info(green('No error found'))

@ -3,7 +3,7 @@
''' '''
import logging import logging
from slither.utils import json_utils from slither.utils.output import Output
from slither.utils.colors import red, green, yellow from slither.utils.colors import red, green, yellow
logger = logging.getLogger("Slither-check-upgradeability") logger = logging.getLogger("Slither-check-upgradeability")
@ -32,9 +32,9 @@ def compare_variables_order(contract1, contract2, missing_variable_check=True):
info = f'Variable only in {contract1.name}: {variable1.name} ({variable1.source_mapping_str})' info = f'Variable only in {contract1.name}: {variable1.name} ({variable1.source_mapping_str})'
logger.info(yellow(info)) logger.info(yellow(info))
json_elem = json_utils.generate_json_result(info) res = Output(info)
json_utils.add_variable_to_json(variable1, json_elem) res.add(variable1)
results['missing_variables'].append(json_elem) results['missing_variables'].append(res.data)
error_found = True error_found = True
continue continue
@ -47,10 +47,10 @@ def compare_variables_order(contract1, contract2, missing_variable_check=True):
info += f'\t Variable {idx} in {contract2.name}: {variable2.name} {variable2.type} ({variable2.source_mapping_str})\n' info += f'\t Variable {idx} in {contract2.name}: {variable2.name} {variable2.type} ({variable2.source_mapping_str})\n'
logger.info(red(info)) logger.info(red(info))
json_elem = json_utils.generate_json_result(info, additional_fields={'index': idx}) res = Output(info, additional_fields={'index': idx})
json_utils.add_variable_to_json(variable1, json_elem) res.add(variable1)
json_utils.add_variable_to_json(variable2, json_elem) res.add(variable2)
results['different-variables'].append(json_elem) results['different-variables'].append(res.data)
error_found = True error_found = True
@ -61,9 +61,9 @@ def compare_variables_order(contract1, contract2, missing_variable_check=True):
info = f'Extra variables in {contract2.name}: {variable2.name} ({variable2.source_mapping_str})\n' info = f'Extra variables in {contract2.name}: {variable2.name} ({variable2.source_mapping_str})\n'
logger.info(yellow(info)) logger.info(yellow(info))
json_elem = json_utils.generate_json_result(info, additional_fields={'index': idx}) res = Output(info, additional_fields={'index': idx})
json_utils.add_variable_to_json(variable2, json_elem) res.add(variable2)
results['extra-variables'].append(json_elem) results['extra-variables'].append(res.data)
idx = idx + 1 idx = idx + 1
if not error_found: if not error_found:

@ -1,6 +1,6 @@
import logging import logging
from slither.utils import json_utils from slither.utils.output import Output
from slither.utils.colors import red, yellow, green from slither.utils.colors import red, yellow, green
logger = logging.getLogger("Slither-check-upgradeability") logger = logging.getLogger("Slither-check-upgradeability")
@ -49,30 +49,30 @@ def constant_conformance_check(contract_v1, contract_v2):
info = f'{state_v1.canonical_name} ({state_v1.source_mapping_str}) was constant and {state_v2.canonical_name} is not ({state_v2.source_mapping_str})' info = f'{state_v1.canonical_name} ({state_v1.source_mapping_str}) was constant and {state_v2.canonical_name} is not ({state_v2.source_mapping_str})'
logger.info(red(info)) logger.info(red(info))
json_elem = json_utils.generate_json_result(info) res = Output(info)
json_utils.add_variable_to_json(state_v1, json_elem) res.add(state_v1)
json_utils.add_variable_to_json(state_v2, json_elem) res.add(state_v2)
results['were_constants'].append(json_elem) results['were_constants'].append(res.data)
error_found = True error_found = True
elif state_v2.is_constant: elif state_v2.is_constant:
info = f'{state_v1.canonical_name} ({state_v1.source_mapping_str}) was not constant but {state_v2.canonical_name} is ({state_v2.source_mapping_str})' info = f'{state_v1.canonical_name} ({state_v1.source_mapping_str}) was not constant but {state_v2.canonical_name} is ({state_v2.source_mapping_str})'
logger.info(red(info)) logger.info(red(info))
json_elem = json_utils.generate_json_result(info) res = Output(info)
json_utils.add_variable_to_json(state_v1, json_elem) res.add(state_v1)
json_utils.add_variable_to_json(state_v2, json_elem) res.add(state_v2)
results['became_constants'].append(json_elem) results['became_constants'].append(res.data)
error_found = True error_found = True
else: else:
info = f'{state_v1.canonical_name} not found in {contract_v2.name}, not check was done' info = f'{state_v1.canonical_name} not found in {contract_v2.name}, not check was done'
logger.info(yellow(info)) logger.info(yellow(info))
json_elem = json_utils.generate_json_result(info) res = Output(info)
json_utils.add_variable_to_json(state_v1, json_elem) res.add(state_v1)
json_utils.add_contract_to_json(contract_v2, json_elem) res.add(contract_v2)
results['not_found_in_v2'].append(json_elem) results['not_found_in_v2'].append(res.data)
error_found = True error_found = True

@ -1,381 +0,0 @@
import os
import json
import logging
from collections import OrderedDict
from slither.core.source_mapping.source_mapping import SourceMapping
from slither.utils.colors import yellow
logger = logging.getLogger("Slither")
###################################################################################
###################################################################################
# region Output
###################################################################################
###################################################################################
def output_json(filename, error, results):
"""
:param filename: Filename where the json will be written. If None or "-", write to stdout
:param error: Error to report
:param results: Results to report
:param logger: Logger where to log potential info
:return:
"""
# Create our encapsulated JSON result.
json_result = {
"success": error is None,
"error": error,
"results": results
}
if filename == "-":
filename = None
# Determine if we should output to stdout
if filename is None:
# Write json to console
print(json.dumps(json_result))
else:
# Write json to file
if os.path.isfile(filename):
logger.info(yellow(f'{filename} exists already, the overwrite is prevented'))
else:
with open(filename, 'w', encoding='utf8') as f:
json.dump(json_result, f, indent=2)
# endregion
###################################################################################
###################################################################################
# region Json generation
###################################################################################
###################################################################################
def generate_json_result(info, additional_fields=None):
if additional_fields is None:
additional_fields = {}
d = OrderedDict()
d['elements'] = []
d['description'] = info
if additional_fields:
d['additional_fields'] = additional_fields
return d
# endregion
###################################################################################
###################################################################################
# region Internal functions
###################################################################################
###################################################################################
def _create_base_element(type, name, source_mapping, type_specific_fields=None, additional_fields=None):
if additional_fields is None:
additional_fields = {}
if type_specific_fields is None:
type_specific_fields = {}
element = {'type': type,
'name': name,
'source_mapping': source_mapping}
if type_specific_fields:
element['type_specific_fields'] = type_specific_fields
if additional_fields:
element['additional_fields'] = additional_fields
return element
def _create_parent_element(element):
from slither.core.children.child_contract import ChildContract
from slither.core.children.child_function import ChildFunction
from slither.core.children.child_inheritance import ChildInheritance
if isinstance(element, ChildInheritance):
if element.contract_declarer:
contract = {'elements': []}
add_contract_to_json(element.contract_declarer, contract)
return contract['elements'][0]
elif isinstance(element, ChildContract):
if element.contract:
contract = {'elements': []}
add_contract_to_json(element.contract, contract)
return contract['elements'][0]
elif isinstance(element, ChildFunction):
if element.function:
function = {'elements': []}
add_function_to_json(element.function, function)
return function['elements'][0]
return None
# endregion
###################################################################################
###################################################################################
# region Variables
###################################################################################
###################################################################################
def add_variable_to_json(variable, d, additional_fields=None):
if additional_fields is None:
additional_fields = {}
type_specific_fields = {
'parent': _create_parent_element(variable)
}
element = _create_base_element('variable',
variable.name,
variable.source_mapping,
type_specific_fields,
additional_fields)
d['elements'].append(element)
def add_variables_to_json(variables, d):
for variable in sorted(variables, key=lambda x: x.name):
add_variable_to_json(variable, d)
# endregion
###################################################################################
###################################################################################
# region Contract
###################################################################################
###################################################################################
def add_contract_to_json(contract, d, additional_fields=None):
if additional_fields is None:
additional_fields = {}
element = _create_base_element('contract',
contract.name,
contract.source_mapping,
{},
additional_fields)
d['elements'].append(element)
# endregion
###################################################################################
###################################################################################
# region Functions
###################################################################################
###################################################################################
def add_function_to_json(function, d, additional_fields=None):
if additional_fields is None:
additional_fields = {}
type_specific_fields = {
'parent': _create_parent_element(function),
'signature': function.full_name
}
element = _create_base_element('function',
function.name,
function.source_mapping,
type_specific_fields,
additional_fields)
d['elements'].append(element)
def add_functions_to_json(functions, d, additional_fields=None):
if additional_fields is None:
additional_fields = {}
for function in sorted(functions, key=lambda x: x.name):
add_function_to_json(function, d, additional_fields)
# endregion
###################################################################################
###################################################################################
# region Enum
###################################################################################
###################################################################################
def add_enum_to_json(enum, d, additional_fields=None):
if additional_fields is None:
additional_fields = {}
type_specific_fields = {
'parent': _create_parent_element(enum)
}
element = _create_base_element('enum',
enum.name,
enum.source_mapping,
type_specific_fields,
additional_fields)
d['elements'].append(element)
# endregion
###################################################################################
###################################################################################
# region Structures
###################################################################################
###################################################################################
def add_struct_to_json(struct, d, additional_fields=None):
if additional_fields is None:
additional_fields = {}
type_specific_fields = {
'parent': _create_parent_element(struct)
}
element = _create_base_element('struct',
struct.name,
struct.source_mapping,
type_specific_fields,
additional_fields)
d['elements'].append(element)
# endregion
###################################################################################
###################################################################################
# region Events
###################################################################################
###################################################################################
def add_event_to_json(event, d, additional_fields=None):
if additional_fields is None:
additional_fields = {}
type_specific_fields = {
'parent': _create_parent_element(event),
'signature': event.full_name
}
element = _create_base_element('event',
event.name,
event.source_mapping,
type_specific_fields,
additional_fields)
d['elements'].append(element)
# endregion
###################################################################################
###################################################################################
# region Nodes
###################################################################################
###################################################################################
def add_node_to_json(node, d, additional_fields=None):
if additional_fields is None:
additional_fields = {}
type_specific_fields = {
'parent': _create_parent_element(node),
}
node_name = str(node.expression) if node.expression else ""
element = _create_base_element('node',
node_name,
node.source_mapping,
type_specific_fields,
additional_fields)
d['elements'].append(element)
def add_nodes_to_json(nodes, d):
for node in sorted(nodes, key=lambda x: x.node_id):
add_node_to_json(node, d)
# endregion
###################################################################################
###################################################################################
# region Pragma
###################################################################################
###################################################################################
def add_pragma_to_json(pragma, d, additional_fields=None):
if additional_fields is None:
additional_fields = {}
type_specific_fields = {
'directive': pragma.directive
}
element = _create_base_element('pragma',
pragma.version,
pragma.source_mapping,
type_specific_fields,
additional_fields)
d['elements'].append(element)
# endregion
###################################################################################
###################################################################################
# region File
###################################################################################
###################################################################################
def add_file_to_json(filename, content, d, additional_fields=None):
if additional_fields is None:
additional_fields = {}
type_specific_fields = {
'filename': filename,
'content': content
}
element = _create_base_element('file',
type_specific_fields,
additional_fields)
d['elements'].append(element)
# endregion
###################################################################################
###################################################################################
# region Pretty Table
###################################################################################
###################################################################################
def add_pretty_table_to_json(content, name, d, additional_fields=None):
if additional_fields is None:
additional_fields = {}
type_specific_fields = {
'content': content,
'name': name
}
element = _create_base_element('pretty_table',
type_specific_fields,
additional_fields)
d['elements'].append(element)
# endregion
###################################################################################
###################################################################################
# region Others
###################################################################################
###################################################################################
def add_other_to_json(name, source_mapping, d, slither, additional_fields=None):
# If this a tuple with (filename, start, end), convert it to a source mapping.
if additional_fields is None:
additional_fields = {}
if isinstance(source_mapping, tuple):
# Parse the source id
(filename, start, end) = source_mapping
source_id = next(
(source_unit_id for (source_unit_id, source_unit_filename) in slither.source_units.items() if
source_unit_filename == filename), -1)
# Convert to a source mapping string
source_mapping = f"{start}:{end}:{source_id}"
# If this is a source mapping string, parse it.
if isinstance(source_mapping, str):
source_mapping_str = source_mapping
source_mapping = SourceMapping()
source_mapping.set_offset(source_mapping_str, slither)
# If this is a source mapping object, get the underlying source mapping dictionary
if isinstance(source_mapping, SourceMapping):
source_mapping = source_mapping.source_mapping
# Create the underlying element and add it to our resulting json
element = _create_base_element('other',
name,
source_mapping,
{},
additional_fields)
d['elements'].append(element)

@ -0,0 +1,454 @@
import os
import json
import logging
from collections import OrderedDict
from slither.core.cfg.node import Node
from slither.core.declarations import Contract, Function, Enum, Event, Structure, Pragma
from slither.core.source_mapping.source_mapping import SourceMapping
from slither.core.variables.variable import Variable
from slither.exceptions import SlitherError
from slither.utils.colors import yellow
logger = logging.getLogger("Slither")
###################################################################################
###################################################################################
# region Output
###################################################################################
###################################################################################
def output_to_json(filename, error, results):
"""
:param filename: Filename where the json will be written. If None or "-", write to stdout
:param error: Error to report
:param results: Results to report
:param logger: Logger where to log potential info
:return:
"""
# Create our encapsulated JSON result.
json_result = {
"success": error is None,
"error": error,
"results": results
}
if filename == "-":
filename = None
# Determine if we should output to stdout
if filename is None:
# Write json to console
print(json.dumps(json_result))
else:
# Write json to file
if os.path.isfile(filename):
logger.info(yellow(f'{filename} exists already, the overwrite is prevented'))
else:
with open(filename, 'w', encoding='utf8') as f:
json.dump(json_result, f, indent=2)
# endregion
###################################################################################
###################################################################################
# region Json generation
###################################################################################
###################################################################################
def _convert_to_description(d):
if isinstance(d, str):
return d
if not isinstance(d, SourceMapping):
raise SlitherError(f'{d} does not inherit from SourceMapping, conversion impossible')
if isinstance(d, Node):
if d.expression:
return f'{d.expression} ({d.source_mapping_str})'
else:
return f'{str(d)} ({d.source_mapping_str})'
if hasattr(d, 'canonical_name'):
return f'{d.canonical_name} ({d.source_mapping_str})'
if hasattr(d, 'name'):
return f'{d.name} ({d.source_mapping_str})'
raise SlitherError(f'{type(d)} cannot be converted (no name, or canonical_name')
def _convert_to_markdown(d, markdown_root):
if isinstance(d, str):
return d
if not isinstance(d, SourceMapping):
raise SlitherError(f'{d} does not inherit from SourceMapping, conversion impossible')
if isinstance(d, Node):
if d.expression:
return f'[{d.expression}]({d.source_mapping_to_markdown(markdown_root)})'
else:
return f'[{str(d)}]({d.source_mapping_to_markdown(markdown_root)})'
if hasattr(d, 'canonical_name'):
return f'[{d.canonical_name}]({d.source_mapping_to_markdown(markdown_root)})'
if hasattr(d, 'name'):
return f'[{d.name}]({d.source_mapping_to_markdown(markdown_root)})'
raise SlitherError(f'{type(d)} cannot be converted (no name, or canonical_name')
# endregion
###################################################################################
###################################################################################
# region Internal functions
###################################################################################
###################################################################################
def _create_base_element(type, name, source_mapping, type_specific_fields=None, additional_fields=None):
if additional_fields is None:
additional_fields = {}
if type_specific_fields is None:
type_specific_fields = {}
element = {'type': type,
'name': name,
'source_mapping': source_mapping}
if type_specific_fields:
element['type_specific_fields'] = type_specific_fields
if additional_fields:
element['additional_fields'] = additional_fields
return element
def _create_parent_element(element):
from slither.core.children.child_contract import ChildContract
from slither.core.children.child_function import ChildFunction
from slither.core.children.child_inheritance import ChildInheritance
if isinstance(element, ChildInheritance):
if element.contract_declarer:
contract = Output('')
contract.add_contract(element.contract_declarer)
return contract.data['elements'][0]
elif isinstance(element, ChildContract):
if element.contract:
contract = Output('')
contract.add_contract(element.contract)
return contract.data['elements'][0]
elif isinstance(element, ChildFunction):
if element.function:
function = Output('')
function.add_function(element.function)
return function.data['elements'][0]
return None
class Output:
def __init__(self, info, additional_fields=None, markdown_root='', standard_format=True):
if additional_fields is None:
additional_fields = {}
# Allow info to be a string to simplify the API
if isinstance(info, str):
info = [info]
self._data = OrderedDict()
self._data['elements'] = []
self._data['description'] = ''.join(_convert_to_description(d) for d in info)
self._data['markdown'] = ''.join(_convert_to_markdown(d, markdown_root) for d in info)
if standard_format:
to_add = [i for i in info if not isinstance(i, str)]
for add in to_add:
self.add(add)
if additional_fields:
self._data['additional_fields'] = additional_fields
def add(self, add, additional_fields=None):
if isinstance(add, Variable):
self.add_variable(add, additional_fields=additional_fields)
elif isinstance(add, Contract):
self.add_contract(add, additional_fields=additional_fields)
elif isinstance(add, Function):
self.add_function(add, additional_fields=additional_fields)
elif isinstance(add, Enum):
self.add_enum(add, additional_fields=additional_fields)
elif isinstance(add, Event):
self.add_event(add, additional_fields=additional_fields)
elif isinstance(add, Structure):
self.add_struct(add, additional_fields=additional_fields)
elif isinstance(add, Pragma):
self.add_pragma(add, additional_fields=additional_fields)
elif isinstance(add, Node):
self.add_node(add, additional_fields=additional_fields)
else:
raise SlitherError(f'Impossible to add {type(add)} to the json')
@property
def data(self):
return self._data
@property
def elements(self):
return self._data['elements']
# endregion
###################################################################################
###################################################################################
# region Variables
###################################################################################
###################################################################################
def add_variable(self, variable, additional_fields=None):
if additional_fields is None:
additional_fields = {}
type_specific_fields = {
'parent': _create_parent_element(variable)
}
element = _create_base_element('variable',
variable.name,
variable.source_mapping,
type_specific_fields,
additional_fields)
self._data['elements'].append(element)
def add_variables_to_output(self, variables):
for variable in sorted(variables, key=lambda x: x.name):
self.add_variable(variable)
# endregion
###################################################################################
###################################################################################
# region Contract
###################################################################################
###################################################################################
def add_contract(self, contract, additional_fields=None):
if additional_fields is None:
additional_fields = {}
element = _create_base_element('contract',
contract.name,
contract.source_mapping,
{},
additional_fields)
self._data['elements'].append(element)
# endregion
###################################################################################
###################################################################################
# region Functions
###################################################################################
###################################################################################
def add_function(self, function, additional_fields=None):
if additional_fields is None:
additional_fields = {}
type_specific_fields = {
'parent': _create_parent_element(function),
'signature': function.full_name
}
element = _create_base_element('function',
function.name,
function.source_mapping,
type_specific_fields,
additional_fields)
self._data['elements'].append(element)
def add_functions(self, functions, additional_fields=None):
if additional_fields is None:
additional_fields = {}
for function in sorted(functions, key=lambda x: x.name):
self.add_function(function, additional_fields)
# endregion
###################################################################################
###################################################################################
# region Enum
###################################################################################
###################################################################################
def add_enum(self, enum, additional_fields=None):
if additional_fields is None:
additional_fields = {}
type_specific_fields = {
'parent': _create_parent_element(enum)
}
element = _create_base_element('enum',
enum.name,
enum.source_mapping,
type_specific_fields,
additional_fields)
self._data['elements'].append(element)
# endregion
###################################################################################
###################################################################################
# region Structures
###################################################################################
###################################################################################
def add_struct(self, struct, additional_fields=None):
if additional_fields is None:
additional_fields = {}
type_specific_fields = {
'parent': _create_parent_element(struct)
}
element = _create_base_element('struct',
struct.name,
struct.source_mapping,
type_specific_fields,
additional_fields)
self._data['elements'].append(element)
# endregion
###################################################################################
###################################################################################
# region Events
###################################################################################
###################################################################################
def add_event(self, event, additional_fields=None):
if additional_fields is None:
additional_fields = {}
type_specific_fields = {
'parent': _create_parent_element(event),
'signature': event.full_name
}
element = _create_base_element('event',
event.name,
event.source_mapping,
type_specific_fields,
additional_fields)
self._data['elements'].append(element)
# endregion
###################################################################################
###################################################################################
# region Nodes
###################################################################################
###################################################################################
def add_node(self, node, additional_fields=None):
if additional_fields is None:
additional_fields = {}
type_specific_fields = {
'parent': _create_parent_element(node),
}
node_name = str(node.expression) if node.expression else ""
element = _create_base_element('node',
node_name,
node.source_mapping,
type_specific_fields,
additional_fields)
self._data['elements'].append(element)
def add_nodes(self, nodes):
for node in sorted(nodes, key=lambda x: x.node_id):
self.add_node(node)
# endregion
###################################################################################
###################################################################################
# region Pragma
###################################################################################
###################################################################################
def add_pragma(self, pragma, additional_fields=None):
if additional_fields is None:
additional_fields = {}
type_specific_fields = {
'directive': pragma.directive
}
element = _create_base_element('pragma',
pragma.version,
pragma.source_mapping,
type_specific_fields,
additional_fields)
self._data['elements'].append(element)
# endregion
###################################################################################
###################################################################################
# region File
###################################################################################
###################################################################################
def add_file(self, filename, content, additional_fields=None):
if additional_fields is None:
additional_fields = {}
type_specific_fields = {
'filename': filename,
'content': content
}
element = _create_base_element('file',
type_specific_fields,
additional_fields)
self._data['elements'].append(element)
# endregion
###################################################################################
###################################################################################
# region Pretty Table
###################################################################################
###################################################################################
def add_pretty_table(self, content, name, additional_fields=None):
if additional_fields is None:
additional_fields = {}
type_specific_fields = {
'content': content,
'name': name
}
element = _create_base_element('pretty_table',
type_specific_fields,
additional_fields)
self._data['elements'].append(element)
# endregion
###################################################################################
###################################################################################
# region Others
###################################################################################
###################################################################################
def add_other(self, name, source_mapping, slither, additional_fields=None):
# If this a tuple with (filename, start, end), convert it to a source mapping.
if additional_fields is None:
additional_fields = {}
if isinstance(source_mapping, tuple):
# Parse the source id
(filename, start, end) = source_mapping
source_id = next(
(source_unit_id for (source_unit_id, source_unit_filename) in slither.source_units.items() if
source_unit_filename == filename), -1)
# Convert to a source mapping string
source_mapping = f"{start}:{end}:{source_id}"
# If this is a source mapping string, parse it.
if isinstance(source_mapping, str):
source_mapping_str = source_mapping
source_mapping = SourceMapping()
source_mapping.set_offset(source_mapping_str, slither)
# If this is a source mapping object, get the underlying source mapping dictionary
if isinstance(source_mapping, SourceMapping):
source_mapping = source_mapping.source_mapping
# Create the underlying element and add it to our resulting json
element = _create_base_element('other',
name,
source_mapping,
{},
additional_fields)
self._data['elements'].append(element)

@ -4,10 +4,6 @@
"results": { "results": {
"detectors": [ "detectors": [
{ {
"check": "arbitrary-send",
"impact": "High",
"confidence": "Medium",
"description": "Test.direct() (tests/arbitrary_send-0.5.1.sol#11-13) sends eth to arbitrary user\n\tDangerous calls:\n\t- msg.sender.send(address(this).balance) (tests/arbitrary_send-0.5.1.sol#12)\n",
"elements": [ "elements": [
{ {
"type": "function", "type": "function",
@ -191,13 +187,14 @@
} }
} }
} }
] ],
}, "description": "Test.direct() (tests/arbitrary_send-0.5.1.sol#11-13) sends eth to arbitrary user\n\tDangerous calls:\n\t- msg.sender.send(address(this).balance) (tests/arbitrary_send-0.5.1.sol#12)\n",
{ "markdown": "[Test.direct()](tests/arbitrary_send-0.5.1.sol#L11-L13) sends eth to arbitrary user\n\tDangerous calls:\n\t- [msg.sender.send(address(this).balance)](tests/arbitrary_send-0.5.1.sol#L12)\n",
"check": "arbitrary-send", "check": "arbitrary-send",
"impact": "High", "impact": "High",
"confidence": "Medium", "confidence": "Medium"
"description": "Test.indirect() (tests/arbitrary_send-0.5.1.sol#19-21) sends eth to arbitrary user\n\tDangerous calls:\n\t- destination.send(address(this).balance) (tests/arbitrary_send-0.5.1.sol#20)\n", },
{
"elements": [ "elements": [
{ {
"type": "function", "type": "function",
@ -381,7 +378,12 @@
} }
} }
} }
] ],
"description": "Test.indirect() (tests/arbitrary_send-0.5.1.sol#19-21) sends eth to arbitrary user\n\tDangerous calls:\n\t- destination.send(address(this).balance) (tests/arbitrary_send-0.5.1.sol#20)\n",
"markdown": "[Test.indirect()](tests/arbitrary_send-0.5.1.sol#L19-L21) sends eth to arbitrary user\n\tDangerous calls:\n\t- [destination.send(address(this).balance)](tests/arbitrary_send-0.5.1.sol#L20)\n",
"check": "arbitrary-send",
"impact": "High",
"confidence": "Medium"
} }
] ]
} }

@ -1,4 +1,4 @@
INFO:Detectors: 
Test.direct() (tests/arbitrary_send-0.5.1.sol#11-13) sends eth to arbitrary user Test.direct() (tests/arbitrary_send-0.5.1.sol#11-13) sends eth to arbitrary user
Dangerous calls: Dangerous calls:
- msg.sender.send(address(this).balance) (tests/arbitrary_send-0.5.1.sol#12) - msg.sender.send(address(this).balance) (tests/arbitrary_send-0.5.1.sol#12)
@ -6,4 +6,4 @@ Test.indirect() (tests/arbitrary_send-0.5.1.sol#19-21) sends eth to arbitrary us
Dangerous calls: Dangerous calls:
- destination.send(address(this).balance) (tests/arbitrary_send-0.5.1.sol#20) - destination.send(address(this).balance) (tests/arbitrary_send-0.5.1.sol#20)
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#functions-that-send-ether-to-arbitrary-destinations Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#functions-that-send-ether-to-arbitrary-destinations
INFO:Slither:tests/arbitrary_send-0.5.1.sol analyzed (1 contracts), 2 result(s) found tests/arbitrary_send-0.5.1.sol analyzed (1 contracts with 1 detectors), 2 result(s) found

@ -4,10 +4,6 @@
"results": { "results": {
"detectors": [ "detectors": [
{ {
"check": "arbitrary-send",
"impact": "High",
"confidence": "Medium",
"description": "Test.direct() (tests/arbitrary_send.sol#11-13) sends eth to arbitrary user\n\tDangerous calls:\n\t- msg.sender.send(address(this).balance) (tests/arbitrary_send.sol#12)\n",
"elements": [ "elements": [
{ {
"type": "function", "type": "function",
@ -191,13 +187,14 @@
} }
} }
} }
] ],
}, "description": "Test.direct() (tests/arbitrary_send.sol#11-13) sends eth to arbitrary user\n\tDangerous calls:\n\t- msg.sender.send(address(this).balance) (tests/arbitrary_send.sol#12)\n",
{ "markdown": "[Test.direct()](tests/arbitrary_send.sol#L11-L13) sends eth to arbitrary user\n\tDangerous calls:\n\t- [msg.sender.send(address(this).balance)](tests/arbitrary_send.sol#L12)\n",
"check": "arbitrary-send", "check": "arbitrary-send",
"impact": "High", "impact": "High",
"confidence": "Medium", "confidence": "Medium"
"description": "Test.indirect() (tests/arbitrary_send.sol#19-21) sends eth to arbitrary user\n\tDangerous calls:\n\t- destination.send(address(this).balance) (tests/arbitrary_send.sol#20)\n", },
{
"elements": [ "elements": [
{ {
"type": "function", "type": "function",
@ -381,7 +378,12 @@
} }
} }
} }
] ],
"description": "Test.indirect() (tests/arbitrary_send.sol#19-21) sends eth to arbitrary user\n\tDangerous calls:\n\t- destination.send(address(this).balance) (tests/arbitrary_send.sol#20)\n",
"markdown": "[Test.indirect()](tests/arbitrary_send.sol#L19-L21) sends eth to arbitrary user\n\tDangerous calls:\n\t- [destination.send(address(this).balance)](tests/arbitrary_send.sol#L20)\n",
"check": "arbitrary-send",
"impact": "High",
"confidence": "Medium"
} }
] ]
} }

@ -1,4 +1,4 @@
INFO:Detectors: 
Test.direct() (tests/arbitrary_send.sol#11-13) sends eth to arbitrary user Test.direct() (tests/arbitrary_send.sol#11-13) sends eth to arbitrary user
Dangerous calls: Dangerous calls:
- msg.sender.send(address(this).balance) (tests/arbitrary_send.sol#12) - msg.sender.send(address(this).balance) (tests/arbitrary_send.sol#12)
@ -6,4 +6,4 @@ Test.indirect() (tests/arbitrary_send.sol#19-21) sends eth to arbitrary user
Dangerous calls: Dangerous calls:
- destination.send(address(this).balance) (tests/arbitrary_send.sol#20) - destination.send(address(this).balance) (tests/arbitrary_send.sol#20)
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#functions-that-send-ether-to-arbitrary-destinations Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#functions-that-send-ether-to-arbitrary-destinations
INFO:Slither:tests/arbitrary_send.sol analyzed (1 contracts), 2 result(s) found tests/arbitrary_send.sol analyzed (1 contracts with 1 detectors), 2 result(s) found

@ -4,10 +4,6 @@
"results": { "results": {
"detectors": [ "detectors": [
{ {
"check": "backdoor",
"impact": "High",
"confidence": "High",
"description": "Backdoor function found in C.i_am_a_backdoor (tests/backdoor.sol#4-6)\n",
"elements": [ "elements": [
{ {
"type": "function", "type": "function",
@ -56,7 +52,12 @@
"signature": "i_am_a_backdoor()" "signature": "i_am_a_backdoor()"
} }
} }
] ],
"description": "Backdoor function found in C.i_am_a_backdoor() (tests/backdoor.sol#4-6)\n",
"markdown": "Backdoor function found in [C.i_am_a_backdoor()](tests/backdoor.sol#L4-L6)\n",
"check": "backdoor",
"impact": "High",
"confidence": "High"
} }
] ]
} }

@ -1,5 +1,5 @@
INFO:Detectors: 
Backdoor function found in C.i_am_a_backdoor (tests/backdoor.sol#4-6) Backdoor function found in C.i_am_a_backdoor() (tests/backdoor.sol#4-6)
Reference: https://github.com/trailofbits/slither/wiki/Adding-a-new-detector Reference: https://github.com/trailofbits/slither/wiki/Adding-a-new-detector
tests/backdoor.sol analyzed (1 contracts with 1 detectors), 1 result(s) found
INFO:Slither:/home/travis/build/crytic/slither/scripts/../tests/expected_json/backdoor.backdoor.json exists already, the overwrite is prevented INFO:Slither:/home/travis/build/crytic/slither/scripts/../tests/expected_json/backdoor.backdoor.json exists already, the overwrite is prevented
INFO:Slither:tests/backdoor.sol analyzed (1 contracts), 1 result(s) found

@ -4,10 +4,6 @@
"results": { "results": {
"detectors": [ "detectors": [
{ {
"check": "suicidal",
"impact": "High",
"confidence": "High",
"description": "C.i_am_a_backdoor() (tests/backdoor.sol#4-6) allows anyone to destruct the contract\n",
"elements": [ "elements": [
{ {
"type": "function", "type": "function",
@ -56,7 +52,12 @@
"signature": "i_am_a_backdoor()" "signature": "i_am_a_backdoor()"
} }
} }
] ],
"description": "C.i_am_a_backdoor() (tests/backdoor.sol#4-6) allows anyone to destruct the contract\n",
"markdown": "[C.i_am_a_backdoor()](tests/backdoor.sol#L4-L6) allows anyone to destruct the contract\n",
"check": "suicidal",
"impact": "High",
"confidence": "High"
} }
] ]
} }

@ -1,5 +1,5 @@
INFO:Detectors: 
C.i_am_a_backdoor() (tests/backdoor.sol#4-6) allows anyone to destruct the contract C.i_am_a_backdoor() (tests/backdoor.sol#4-6) allows anyone to destruct the contract
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#suicidal Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#suicidal
tests/backdoor.sol analyzed (1 contracts with 1 detectors), 1 result(s) found
INFO:Slither:/home/travis/build/crytic/slither/scripts/../tests/expected_json/backdoor.suicidal.json exists already, the overwrite is prevented INFO:Slither:/home/travis/build/crytic/slither/scripts/../tests/expected_json/backdoor.suicidal.json exists already, the overwrite is prevented
INFO:Slither:tests/backdoor.sol analyzed (1 contracts), 1 result(s) found

@ -4,10 +4,6 @@
"results": { "results": {
"detectors": [ "detectors": [
{ {
"check": "constable-states",
"impact": "Optimization",
"confidence": "High",
"description": "A.myFriendsAddress should be constant (tests/const_state_variables.sol#7)\n",
"elements": [ "elements": [
{ {
"type": "variable", "type": "variable",
@ -64,13 +60,14 @@
} }
} }
} }
] ],
}, "description": "A.myFriendsAddress (tests/const_state_variables.sol#7) should be constant\n",
{ "markdown": "[A.myFriendsAddress](tests/const_state_variables.sol#L7) should be constant\n",
"check": "constable-states", "check": "constable-states",
"impact": "Optimization", "impact": "Optimization",
"confidence": "High", "confidence": "High"
"description": "A.test should be constant (tests/const_state_variables.sol#10)\n", },
{
"elements": [ "elements": [
{ {
"type": "variable", "type": "variable",
@ -127,13 +124,14 @@
} }
} }
} }
] ],
}, "description": "A.test (tests/const_state_variables.sol#10) should be constant\n",
{ "markdown": "[A.test](tests/const_state_variables.sol#L10) should be constant\n",
"check": "constable-states", "check": "constable-states",
"impact": "Optimization", "impact": "Optimization",
"confidence": "High", "confidence": "High"
"description": "A.text2 should be constant (tests/const_state_variables.sol#14)\n", },
{
"elements": [ "elements": [
{ {
"type": "variable", "type": "variable",
@ -190,13 +188,14 @@
} }
} }
} }
] ],
}, "description": "A.text2 (tests/const_state_variables.sol#14) should be constant\n",
{ "markdown": "[A.text2](tests/const_state_variables.sol#L14) should be constant\n",
"check": "constable-states", "check": "constable-states",
"impact": "Optimization", "impact": "Optimization",
"confidence": "High", "confidence": "High"
"description": "B.mySistersAddress should be constant (tests/const_state_variables.sol#26)\n", },
{
"elements": [ "elements": [
{ {
"type": "variable", "type": "variable",
@ -249,13 +248,14 @@
} }
} }
} }
] ],
}, "description": "B.mySistersAddress (tests/const_state_variables.sol#26) should be constant\n",
{ "markdown": "[B.mySistersAddress](tests/const_state_variables.sol#L26) should be constant\n",
"check": "constable-states", "check": "constable-states",
"impact": "Optimization", "impact": "Optimization",
"confidence": "High", "confidence": "High"
"description": "MyConc.should_be_constant should be constant (tests/const_state_variables.sol#42)\n", },
{
"elements": [ "elements": [
{ {
"type": "variable", "type": "variable",
@ -308,13 +308,14 @@
} }
} }
} }
] ],
}, "description": "MyConc.should_be_constant (tests/const_state_variables.sol#42) should be constant\n",
{ "markdown": "[MyConc.should_be_constant](tests/const_state_variables.sol#L42) should be constant\n",
"check": "constable-states", "check": "constable-states",
"impact": "Optimization", "impact": "Optimization",
"confidence": "High", "confidence": "High"
"description": "MyConc.should_be_constant_2 should be constant (tests/const_state_variables.sol#43)\n", },
{
"elements": [ "elements": [
{ {
"type": "variable", "type": "variable",
@ -367,7 +368,12 @@
} }
} }
} }
] ],
"description": "MyConc.should_be_constant_2 (tests/const_state_variables.sol#43) should be constant\n",
"markdown": "[MyConc.should_be_constant_2](tests/const_state_variables.sol#L43) should be constant\n",
"check": "constable-states",
"impact": "Optimization",
"confidence": "High"
} }
] ]
} }

@ -1,9 +1,9 @@
INFO:Detectors: 
A.myFriendsAddress should be constant (tests/const_state_variables.sol#7) A.myFriendsAddress (tests/const_state_variables.sol#7) should be constant
A.test should be constant (tests/const_state_variables.sol#10) A.test (tests/const_state_variables.sol#10) should be constant
A.text2 should be constant (tests/const_state_variables.sol#14) A.text2 (tests/const_state_variables.sol#14) should be constant
B.mySistersAddress should be constant (tests/const_state_variables.sol#26) B.mySistersAddress (tests/const_state_variables.sol#26) should be constant
MyConc.should_be_constant should be constant (tests/const_state_variables.sol#42) MyConc.should_be_constant (tests/const_state_variables.sol#42) should be constant
MyConc.should_be_constant_2 should be constant (tests/const_state_variables.sol#43) MyConc.should_be_constant_2 (tests/const_state_variables.sol#43) should be constant
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#state-variables-that-could-be-declared-constant Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#state-variables-that-could-be-declared-constant
INFO:Slither:tests/const_state_variables.sol analyzed (3 contracts), 6 result(s) found tests/const_state_variables.sol analyzed (3 contracts with 1 detectors), 6 result(s) found

@ -4,10 +4,6 @@
"results": { "results": {
"detectors": [ "detectors": [
{ {
"check": "constant-function",
"impact": "Medium",
"confidence": "Medium",
"description": "Constant.test_assembly_bug() (tests/constant-0.5.1.sol#15-17) is declared view but contains assembly code\n",
"elements": [ "elements": [
{ {
"type": "function", "type": "function",
@ -68,9 +64,14 @@
} }
} }
], ],
"description": "Constant.test_assembly_bug() (tests/constant-0.5.1.sol#15-17) is declared view but contains assembly code\n",
"markdown": "[Constant.test_assembly_bug()](tests/constant-0.5.1.sol#L15-L17) is declared view but contains assembly code\n",
"additional_fields": { "additional_fields": {
"contains_assembly": true "contains_assembly": true
} },
"check": "constant-function",
"impact": "Medium",
"confidence": "Medium"
} }
] ]
} }

@ -1,4 +1,4 @@
INFO:Detectors: 
Constant.test_assembly_bug() (tests/constant-0.5.1.sol#15-17) is declared view but contains assembly code Constant.test_assembly_bug() (tests/constant-0.5.1.sol#15-17) is declared view but contains assembly code
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#constant-functions-changing-the-state Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#constant-functions-changing-the-state
INFO:Slither:tests/constant-0.5.1.sol analyzed (1 contracts), 1 result(s) found tests/constant-0.5.1.sol analyzed (1 contracts with 1 detectors), 1 result(s) found

@ -4,10 +4,6 @@
"results": { "results": {
"detectors": [ "detectors": [
{ {
"check": "constant-function",
"impact": "Medium",
"confidence": "Medium",
"description": "Constant.test_view_bug() (tests/constant.sol#5-7) is declared view but changes state variables:\n\t- Constant.a\n",
"elements": [ "elements": [
{ {
"type": "function", "type": "function",
@ -137,15 +133,16 @@
} }
} }
], ],
"description": "Constant.test_view_bug() (tests/constant.sol#5-7) is declared view but changes state variables:\n\t- Constant.a (tests/constant.sol#3)\n",
"markdown": "[Constant.test_view_bug()](tests/constant.sol#L5-L7) is declared view but changes state variables:\n\t- [Constant.a](tests/constant.sol#L3)\n",
"additional_fields": { "additional_fields": {
"contains_assembly": false "contains_assembly": false
} },
},
{
"check": "constant-function", "check": "constant-function",
"impact": "Medium", "impact": "Medium",
"confidence": "Medium", "confidence": "Medium"
"description": "Constant.test_constant_bug() (tests/constant.sol#9-11) is declared view but changes state variables:\n\t- Constant.a\n", },
{
"elements": [ "elements": [
{ {
"type": "function", "type": "function",
@ -275,15 +272,16 @@
} }
} }
], ],
"description": "Constant.test_constant_bug() (tests/constant.sol#9-11) is declared view but changes state variables:\n\t- Constant.a (tests/constant.sol#3)\n",
"markdown": "[Constant.test_constant_bug()](tests/constant.sol#L9-L11) is declared view but changes state variables:\n\t- [Constant.a](tests/constant.sol#L3)\n",
"additional_fields": { "additional_fields": {
"contains_assembly": false "contains_assembly": false
} },
},
{
"check": "constant-function", "check": "constant-function",
"impact": "Medium", "impact": "Medium",
"confidence": "Medium", "confidence": "Medium"
"description": "Constant.test_assembly_bug() (tests/constant.sol#22-24) is declared view but contains assembly code\n", },
{
"elements": [ "elements": [
{ {
"type": "function", "type": "function",
@ -351,9 +349,14 @@
} }
} }
], ],
"description": "Constant.test_assembly_bug() (tests/constant.sol#22-24) is declared view but contains assembly code\n",
"markdown": "[Constant.test_assembly_bug()](tests/constant.sol#L22-L24) is declared view but contains assembly code\n",
"additional_fields": { "additional_fields": {
"contains_assembly": true "contains_assembly": true
} },
"check": "constant-function",
"impact": "Medium",
"confidence": "Medium"
} }
] ]
} }

@ -1,8 +1,8 @@
INFO:Detectors: 
Constant.test_view_bug() (tests/constant.sol#5-7) is declared view but changes state variables: Constant.test_view_bug() (tests/constant.sol#5-7) is declared view but changes state variables:
- Constant.a - Constant.a (tests/constant.sol#3)
Constant.test_constant_bug() (tests/constant.sol#9-11) is declared view but changes state variables: Constant.test_constant_bug() (tests/constant.sol#9-11) is declared view but changes state variables:
- Constant.a - Constant.a (tests/constant.sol#3)
Constant.test_assembly_bug() (tests/constant.sol#22-24) is declared view but contains assembly code Constant.test_assembly_bug() (tests/constant.sol#22-24) is declared view but contains assembly code
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#constant-functions-changing-the-state Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#constant-functions-changing-the-state
INFO:Slither:tests/constant.sol analyzed (1 contracts), 3 result(s) found tests/constant.sol analyzed (1 contracts with 1 detectors), 3 result(s) found

@ -4,11 +4,73 @@
"results": { "results": {
"detectors": [ "detectors": [
{ {
"check": "controlled-delegatecall",
"impact": "High",
"confidence": "Medium",
"description": "C.bad_delegate_call (tests/controlled_delegatecall.sol#8-11) uses delegatecall to a input-controlled function id\n\t- addr_bad.delegatecall(data) (tests/controlled_delegatecall.sol#10)\n",
"elements": [ "elements": [
{
"type": "function",
"name": "bad_delegate_call",
"source_mapping": {
"start": 101,
"length": 134,
"filename_used": "/home/travis/build/crytic/slither/tests/controlled_delegatecall.sol",
"filename_relative": "tests/controlled_delegatecall.sol",
"filename_absolute": "/home/travis/build/crytic/slither/tests/controlled_delegatecall.sol",
"filename_short": "tests/controlled_delegatecall.sol",
"is_dependency": false,
"lines": [
8,
9,
10,
11
],
"starting_column": 5,
"ending_column": 6
},
"type_specific_fields": {
"parent": {
"type": "contract",
"name": "C",
"source_mapping": {
"start": 0,
"length": 585,
"filename_used": "/home/travis/build/crytic/slither/tests/controlled_delegatecall.sol",
"filename_relative": "tests/controlled_delegatecall.sol",
"filename_absolute": "/home/travis/build/crytic/slither/tests/controlled_delegatecall.sol",
"filename_short": "tests/controlled_delegatecall.sol",
"is_dependency": false,
"lines": [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25
],
"starting_column": 1,
"ending_column": 2
}
},
"signature": "bad_delegate_call(bytes)"
}
},
{ {
"type": "node", "type": "node",
"name": "addr_bad.delegatecall(data)", "name": "addr_bad.delegatecall(data)",
@ -94,23 +156,31 @@
} }
} }
} }
}, }
],
"description": "C.bad_delegate_call(bytes) (tests/controlled_delegatecall.sol#8-11) uses delegatecall to a input-controlled function id\n\t- addr_bad.delegatecall(data) (tests/controlled_delegatecall.sol#10)\n",
"markdown": "[C.bad_delegate_call(bytes)](tests/controlled_delegatecall.sol#L8-L11) uses delegatecall to a input-controlled function id\n\t- [addr_bad.delegatecall(data)](tests/controlled_delegatecall.sol#L10)\n",
"check": "controlled-delegatecall",
"impact": "High",
"confidence": "Medium"
},
{
"elements": [
{ {
"type": "function", "type": "function",
"name": "bad_delegate_call", "name": "bad_delegate_call2",
"source_mapping": { "source_mapping": {
"start": 101, "start": 337,
"length": 134, "length": 118,
"filename_used": "/home/travis/build/crytic/slither/tests/controlled_delegatecall.sol", "filename_used": "/home/travis/build/crytic/slither/tests/controlled_delegatecall.sol",
"filename_relative": "tests/controlled_delegatecall.sol", "filename_relative": "tests/controlled_delegatecall.sol",
"filename_absolute": "/home/travis/build/crytic/slither/tests/controlled_delegatecall.sol", "filename_absolute": "/home/travis/build/crytic/slither/tests/controlled_delegatecall.sol",
"filename_short": "tests/controlled_delegatecall.sol", "filename_short": "tests/controlled_delegatecall.sol",
"is_dependency": false, "is_dependency": false,
"lines": [ "lines": [
8, 18,
9, 19,
10, 20
11
], ],
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
@ -158,17 +228,9 @@
"ending_column": 2 "ending_column": 2
} }
}, },
"signature": "bad_delegate_call(bytes)" "signature": "bad_delegate_call2(bytes)"
} }
} },
]
},
{
"check": "controlled-delegatecall",
"impact": "High",
"confidence": "Medium",
"description": "C.bad_delegate_call2 (tests/controlled_delegatecall.sol#18-20) uses delegatecall to a input-controlled function id\n\t- addr_bad.delegatecall(abi.encode(func_id,data)) (tests/controlled_delegatecall.sol#19)\n",
"elements": [
{ {
"type": "node", "type": "node",
"name": "addr_bad.delegatecall(abi.encode(func_id,data))", "name": "addr_bad.delegatecall(abi.encode(func_id,data))",
@ -253,73 +315,13 @@
} }
} }
} }
},
{
"type": "function",
"name": "bad_delegate_call2",
"source_mapping": {
"start": 337,
"length": 118,
"filename_used": "/home/travis/build/crytic/slither/tests/controlled_delegatecall.sol",
"filename_relative": "tests/controlled_delegatecall.sol",
"filename_absolute": "/home/travis/build/crytic/slither/tests/controlled_delegatecall.sol",
"filename_short": "tests/controlled_delegatecall.sol",
"is_dependency": false,
"lines": [
18,
19,
20
],
"starting_column": 5,
"ending_column": 6
},
"type_specific_fields": {
"parent": {
"type": "contract",
"name": "C",
"source_mapping": {
"start": 0,
"length": 585,
"filename_used": "/home/travis/build/crytic/slither/tests/controlled_delegatecall.sol",
"filename_relative": "tests/controlled_delegatecall.sol",
"filename_absolute": "/home/travis/build/crytic/slither/tests/controlled_delegatecall.sol",
"filename_short": "tests/controlled_delegatecall.sol",
"is_dependency": false,
"lines": [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25
],
"starting_column": 1,
"ending_column": 2
}
},
"signature": "bad_delegate_call2(bytes)"
}
} }
] ],
"description": "C.bad_delegate_call2(bytes) (tests/controlled_delegatecall.sol#18-20) uses delegatecall to a input-controlled function id\n\t- addr_bad.delegatecall(abi.encode(func_id,data)) (tests/controlled_delegatecall.sol#19)\n",
"markdown": "[C.bad_delegate_call2(bytes)](tests/controlled_delegatecall.sol#L18-L20) uses delegatecall to a input-controlled function id\n\t- [addr_bad.delegatecall(abi.encode(func_id,data))](tests/controlled_delegatecall.sol#L19)\n",
"check": "controlled-delegatecall",
"impact": "High",
"confidence": "Medium"
} }
] ]
} }

@ -1,7 +1,7 @@
INFO:Detectors: 
C.bad_delegate_call (tests/controlled_delegatecall.sol#8-11) uses delegatecall to a input-controlled function id C.bad_delegate_call(bytes) (tests/controlled_delegatecall.sol#8-11) uses delegatecall to a input-controlled function id
- addr_bad.delegatecall(data) (tests/controlled_delegatecall.sol#10) - addr_bad.delegatecall(data) (tests/controlled_delegatecall.sol#10)
C.bad_delegate_call2 (tests/controlled_delegatecall.sol#18-20) uses delegatecall to a input-controlled function id C.bad_delegate_call2(bytes) (tests/controlled_delegatecall.sol#18-20) uses delegatecall to a input-controlled function id
- addr_bad.delegatecall(abi.encode(func_id,data)) (tests/controlled_delegatecall.sol#19) - addr_bad.delegatecall(abi.encode(func_id,data)) (tests/controlled_delegatecall.sol#19)
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#controlled-delegatecall Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#controlled-delegatecall
INFO:Slither:tests/controlled_delegatecall.sol analyzed (1 contracts), 2 result(s) found tests/controlled_delegatecall.sol analyzed (1 contracts with 1 detectors), 2 result(s) found

@ -4,10 +4,6 @@
"results": { "results": {
"detectors": [ "detectors": [
{ {
"check": "deprecated-standards",
"impact": "Informational",
"confidence": "High",
"description": "Deprecated standard detected @ tests/deprecated_calls.sol#2:\n\t- Usage of \"block.blockhash()\" should be replaced with \"blockhash()\"\n",
"elements": [ "elements": [
{ {
"type": "variable", "type": "variable",
@ -73,13 +69,14 @@
} }
} }
} }
] ],
}, "description": "Deprecated standard detected ContractWithDeprecatedReferences.globalBlockHash (tests/deprecated_calls.sol#2):\n\t- Usage of \"block.blockhash()\" should be replaced with \"blockhash()\"\n",
{ "markdown": "Deprecated standard detected [ContractWithDeprecatedReferences.globalBlockHash](tests/deprecated_calls.sol#L2):\n\t- Usage of \"block.blockhash()\" should be replaced with \"blockhash()\"\n",
"check": "deprecated-standards", "check": "deprecated-standards",
"impact": "Informational", "impact": "Informational",
"confidence": "High", "confidence": "High"
"description": "Deprecated standard detected @ tests/deprecated_calls.sol#7:\n\t- Usage of \"msg.gas\" should be replaced with \"gasleft()\"\n", },
{
"elements": [ "elements": [
{ {
"type": "node", "type": "node",
@ -172,13 +169,14 @@
} }
} }
} }
] ],
}, "description": "Deprecated standard detected msg.gas == msg.value (tests/deprecated_calls.sol#7):\n\t- Usage of \"msg.gas\" should be replaced with \"gasleft()\"\n",
{ "markdown": "Deprecated standard detected [msg.gas == msg.value](tests/deprecated_calls.sol#L7):\n\t- Usage of \"msg.gas\" should be replaced with \"gasleft()\"\n",
"check": "deprecated-standards", "check": "deprecated-standards",
"impact": "Informational", "impact": "Informational",
"confidence": "High", "confidence": "High"
"description": "Deprecated standard detected @ tests/deprecated_calls.sol#9:\n\t- Usage of \"throw\" should be replaced with \"revert()\"\n", },
{
"elements": [ "elements": [
{ {
"type": "node", "type": "node",
@ -271,13 +269,14 @@
} }
} }
} }
] ],
}, "description": "Deprecated standard detected THROW None (tests/deprecated_calls.sol#9):\n\t- Usage of \"throw\" should be replaced with \"revert()\"\n",
{ "markdown": "Deprecated standard detected [THROW None](tests/deprecated_calls.sol#L9):\n\t- Usage of \"throw\" should be replaced with \"revert()\"\n",
"check": "deprecated-standards", "check": "deprecated-standards",
"impact": "Informational", "impact": "Informational",
"confidence": "High", "confidence": "High"
"description": "Deprecated standard detected @ tests/deprecated_calls.sol#16:\n\t- Usage of \"sha3()\" should be replaced with \"keccak256()\"\n", },
{
"elements": [ "elements": [
{ {
"type": "node", "type": "node",
@ -376,13 +375,14 @@
} }
} }
} }
] ],
}, "description": "Deprecated standard detected sha3Result = sha3()(test deprecated sha3 usage) (tests/deprecated_calls.sol#16):\n\t- Usage of \"sha3()\" should be replaced with \"keccak256()\"\n",
{ "markdown": "Deprecated standard detected [sha3Result = sha3()(test deprecated sha3 usage)](tests/deprecated_calls.sol#L16):\n\t- Usage of \"sha3()\" should be replaced with \"keccak256()\"\n",
"check": "deprecated-standards", "check": "deprecated-standards",
"impact": "Informational", "impact": "Informational",
"confidence": "High", "confidence": "High"
"description": "Deprecated standard detected @ tests/deprecated_calls.sol#19:\n\t- Usage of \"block.blockhash()\" should be replaced with \"blockhash()\"\n", },
{
"elements": [ "elements": [
{ {
"type": "node", "type": "node",
@ -481,13 +481,14 @@
} }
} }
} }
] ],
}, "description": "Deprecated standard detected blockHashResult = block.blockhash(0) (tests/deprecated_calls.sol#19):\n\t- Usage of \"block.blockhash()\" should be replaced with \"blockhash()\"\n",
{ "markdown": "Deprecated standard detected [blockHashResult = block.blockhash(0)](tests/deprecated_calls.sol#L19):\n\t- Usage of \"block.blockhash()\" should be replaced with \"blockhash()\"\n",
"check": "deprecated-standards", "check": "deprecated-standards",
"impact": "Informational", "impact": "Informational",
"confidence": "High", "confidence": "High"
"description": "Deprecated standard detected @ tests/deprecated_calls.sol#22:\n\t- Usage of \"callcode\" should be replaced with \"delegatecall\"\n", },
{
"elements": [ "elements": [
{ {
"type": "node", "type": "node",
@ -586,13 +587,14 @@
} }
} }
} }
] ],
}, "description": "Deprecated standard detected address(this).callcode() (tests/deprecated_calls.sol#22):\n\t- Usage of \"callcode\" should be replaced with \"delegatecall\"\n",
{ "markdown": "Deprecated standard detected [address(this).callcode()](tests/deprecated_calls.sol#L22):\n\t- Usage of \"callcode\" should be replaced with \"delegatecall\"\n",
"check": "deprecated-standards", "check": "deprecated-standards",
"impact": "Informational", "impact": "Informational",
"confidence": "High", "confidence": "High"
"description": "Deprecated standard detected @ tests/deprecated_calls.sol#25:\n\t- Usage of \"suicide()\" should be replaced with \"selfdestruct()\"\n", },
{
"elements": [ "elements": [
{ {
"type": "node", "type": "node",
@ -691,13 +693,14 @@
} }
} }
} }
] ],
}, "description": "Deprecated standard detected suicide(address)(address(0)) (tests/deprecated_calls.sol#25):\n\t- Usage of \"suicide()\" should be replaced with \"selfdestruct()\"\n",
{ "markdown": "Deprecated standard detected [suicide(address)(address(0))](tests/deprecated_calls.sol#L25):\n\t- Usage of \"suicide()\" should be replaced with \"selfdestruct()\"\n",
"check": "deprecated-standards", "check": "deprecated-standards",
"impact": "Informational", "impact": "Informational",
"confidence": "High", "confidence": "High"
"description": "Deprecated standard detected @ tests/deprecated_calls.sol#2:\n\t- Usage of \"block.blockhash()\" should be replaced with \"blockhash()\"\n", },
{
"elements": [ "elements": [
{ {
"type": "node", "type": "node",
@ -810,7 +813,12 @@
} }
} }
} }
] ],
"description": "Deprecated standard detected globalBlockHash = block.blockhash(0) (tests/deprecated_calls.sol#2):\n\t- Usage of \"block.blockhash()\" should be replaced with \"blockhash()\"\n",
"markdown": "Deprecated standard detected [globalBlockHash = block.blockhash(0)](tests/deprecated_calls.sol#L2):\n\t- Usage of \"block.blockhash()\" should be replaced with \"blockhash()\"\n",
"check": "deprecated-standards",
"impact": "Informational",
"confidence": "High"
} }
] ]
} }

@ -1,19 +1,19 @@
INFO:Detectors: 
Deprecated standard detected @ tests/deprecated_calls.sol#2: Deprecated standard detected ContractWithDeprecatedReferences.globalBlockHash (tests/deprecated_calls.sol#2):
- Usage of "block.blockhash()" should be replaced with "blockhash()" - Usage of "block.blockhash()" should be replaced with "blockhash()"
Deprecated standard detected @ tests/deprecated_calls.sol#7: Deprecated standard detected msg.gas == msg.value (tests/deprecated_calls.sol#7):
- Usage of "msg.gas" should be replaced with "gasleft()" - Usage of "msg.gas" should be replaced with "gasleft()"
Deprecated standard detected @ tests/deprecated_calls.sol#9: Deprecated standard detected THROW None (tests/deprecated_calls.sol#9):
- Usage of "throw" should be replaced with "revert()" - Usage of "throw" should be replaced with "revert()"
Deprecated standard detected @ tests/deprecated_calls.sol#16: Deprecated standard detected sha3Result = sha3()(test deprecated sha3 usage) (tests/deprecated_calls.sol#16):
- Usage of "sha3()" should be replaced with "keccak256()" - Usage of "sha3()" should be replaced with "keccak256()"
Deprecated standard detected @ tests/deprecated_calls.sol#19: Deprecated standard detected blockHashResult = block.blockhash(0) (tests/deprecated_calls.sol#19):
- Usage of "block.blockhash()" should be replaced with "blockhash()" - Usage of "block.blockhash()" should be replaced with "blockhash()"
Deprecated standard detected @ tests/deprecated_calls.sol#22: Deprecated standard detected address(this).callcode() (tests/deprecated_calls.sol#22):
- Usage of "callcode" should be replaced with "delegatecall" - Usage of "callcode" should be replaced with "delegatecall"
Deprecated standard detected @ tests/deprecated_calls.sol#25: Deprecated standard detected suicide(address)(address(0)) (tests/deprecated_calls.sol#25):
- Usage of "suicide()" should be replaced with "selfdestruct()" - Usage of "suicide()" should be replaced with "selfdestruct()"
Deprecated standard detected @ tests/deprecated_calls.sol#2: Deprecated standard detected globalBlockHash = block.blockhash(0) (tests/deprecated_calls.sol#2):
- Usage of "block.blockhash()" should be replaced with "blockhash()" - Usage of "block.blockhash()" should be replaced with "blockhash()"
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#deprecated-standards Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#deprecated-standards
INFO:Slither:tests/deprecated_calls.sol analyzed (1 contracts), 8 result(s) found tests/deprecated_calls.sol analyzed (1 contracts with 1 detectors), 8 result(s) found

@ -4,10 +4,6 @@
"results": { "results": {
"detectors": [ "detectors": [
{ {
"check": "erc20-indexed",
"impact": "Informational",
"confidence": "High",
"description": "ERC20 event IERC20Bad.Transfer (tests/erc20_indexed.sol#19) does not index parameter 'from'\n",
"elements": [ "elements": [
{ {
"type": "event", "type": "event",
@ -60,13 +56,14 @@
"parameter_name": "from" "parameter_name": "from"
} }
} }
] ],
}, "description": "ERC20 event IERC20BadTransfer(address,address,uint256) (tests/erc20_indexed.sol#19)does not index parameter from\n",
{ "markdown": "ERC20 event [IERC20BadTransfer(address,address,uint256)](tests/erc20_indexed.sol#L19)does not index parameter from\n",
"check": "erc20-indexed", "check": "erc20-indexed",
"impact": "Informational", "impact": "Informational",
"confidence": "High", "confidence": "High"
"description": "ERC20 event IERC20Bad.Transfer (tests/erc20_indexed.sol#19) does not index parameter 'to'\n", },
{
"elements": [ "elements": [
{ {
"type": "event", "type": "event",
@ -119,13 +116,14 @@
"parameter_name": "to" "parameter_name": "to"
} }
} }
] ],
}, "description": "ERC20 event IERC20BadTransfer(address,address,uint256) (tests/erc20_indexed.sol#19)does not index parameter to\n",
{ "markdown": "ERC20 event [IERC20BadTransfer(address,address,uint256)](tests/erc20_indexed.sol#L19)does not index parameter to\n",
"check": "erc20-indexed", "check": "erc20-indexed",
"impact": "Informational", "impact": "Informational",
"confidence": "High", "confidence": "High"
"description": "ERC20 event IERC20Bad.Approval (tests/erc20_indexed.sol#20) does not index parameter 'owner'\n", },
{
"elements": [ "elements": [
{ {
"type": "event", "type": "event",
@ -178,13 +176,14 @@
"parameter_name": "owner" "parameter_name": "owner"
} }
} }
] ],
}, "description": "ERC20 event IERC20BadApproval(address,address,uint256) (tests/erc20_indexed.sol#20)does not index parameter owner\n",
{ "markdown": "ERC20 event [IERC20BadApproval(address,address,uint256)](tests/erc20_indexed.sol#L20)does not index parameter owner\n",
"check": "erc20-indexed", "check": "erc20-indexed",
"impact": "Informational", "impact": "Informational",
"confidence": "High", "confidence": "High"
"description": "ERC20 event IERC20Bad.Approval (tests/erc20_indexed.sol#20) does not index parameter 'spender'\n", },
{
"elements": [ "elements": [
{ {
"type": "event", "type": "event",
@ -237,7 +236,12 @@
"parameter_name": "spender" "parameter_name": "spender"
} }
} }
] ],
"description": "ERC20 event IERC20BadApproval(address,address,uint256) (tests/erc20_indexed.sol#20)does not index parameter spender\n",
"markdown": "ERC20 event [IERC20BadApproval(address,address,uint256)](tests/erc20_indexed.sol#L20)does not index parameter spender\n",
"check": "erc20-indexed",
"impact": "Informational",
"confidence": "High"
} }
] ]
} }

@ -1,7 +1,7 @@
INFO:Detectors: 
ERC20 event IERC20Bad.Transfer (tests/erc20_indexed.sol#19) does not index parameter 'from' ERC20 event IERC20BadTransfer(address,address,uint256) (tests/erc20_indexed.sol#19)does not index parameter from
ERC20 event IERC20Bad.Transfer (tests/erc20_indexed.sol#19) does not index parameter 'to' ERC20 event IERC20BadTransfer(address,address,uint256) (tests/erc20_indexed.sol#19)does not index parameter to
ERC20 event IERC20Bad.Approval (tests/erc20_indexed.sol#20) does not index parameter 'owner' ERC20 event IERC20BadApproval(address,address,uint256) (tests/erc20_indexed.sol#20)does not index parameter owner
ERC20 event IERC20Bad.Approval (tests/erc20_indexed.sol#20) does not index parameter 'spender' ERC20 event IERC20BadApproval(address,address,uint256) (tests/erc20_indexed.sol#20)does not index parameter spender
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#unindexed-erc20-event-parameters Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#unindexed-erc20-event-parameters
INFO:Slither:tests/erc20_indexed.sol analyzed (3 contracts), 4 result(s) found tests/erc20_indexed.sol analyzed (3 contracts with 1 detectors), 4 result(s) found

@ -4,10 +4,6 @@
"results": { "results": {
"detectors": [ "detectors": [
{ {
"check": "external-function",
"impact": "Optimization",
"confidence": "High",
"description": "funcNotCalled3() should be declared external:\n\t- ContractWithFunctionNotCalled.funcNotCalled3() (tests/external_function.sol#13-15)\n",
"elements": [ "elements": [
{ {
"type": "function", "type": "function",
@ -68,13 +64,14 @@
"signature": "funcNotCalled3()" "signature": "funcNotCalled3()"
} }
} }
] ],
}, "description": "funcNotCalled3() should be declared external:\n\t- ContractWithFunctionNotCalled.funcNotCalled3() (tests/external_function.sol#13-15)\n",
{ "markdown": "funcNotCalled3() should be declared external:\n\t- [ContractWithFunctionNotCalled.funcNotCalled3()](tests/external_function.sol#L13-L15)\n",
"check": "external-function", "check": "external-function",
"impact": "Optimization", "impact": "Optimization",
"confidence": "High", "confidence": "High"
"description": "funcNotCalled2() should be declared external:\n\t- ContractWithFunctionNotCalled.funcNotCalled2() (tests/external_function.sol#17-19)\n", },
{
"elements": [ "elements": [
{ {
"type": "function", "type": "function",
@ -135,13 +132,14 @@
"signature": "funcNotCalled2()" "signature": "funcNotCalled2()"
} }
} }
] ],
}, "description": "funcNotCalled2() should be declared external:\n\t- ContractWithFunctionNotCalled.funcNotCalled2() (tests/external_function.sol#17-19)\n",
{ "markdown": "funcNotCalled2() should be declared external:\n\t- [ContractWithFunctionNotCalled.funcNotCalled2()](tests/external_function.sol#L17-L19)\n",
"check": "external-function", "check": "external-function",
"impact": "Optimization", "impact": "Optimization",
"confidence": "High", "confidence": "High"
"description": "funcNotCalled() should be declared external:\n\t- ContractWithFunctionNotCalled.funcNotCalled() (tests/external_function.sol#21-23)\n", },
{
"elements": [ "elements": [
{ {
"type": "function", "type": "function",
@ -202,13 +200,14 @@
"signature": "funcNotCalled()" "signature": "funcNotCalled()"
} }
} }
] ],
}, "description": "funcNotCalled() should be declared external:\n\t- ContractWithFunctionNotCalled.funcNotCalled() (tests/external_function.sol#21-23)\n",
{ "markdown": "funcNotCalled() should be declared external:\n\t- [ContractWithFunctionNotCalled.funcNotCalled()](tests/external_function.sol#L21-L23)\n",
"check": "external-function", "check": "external-function",
"impact": "Optimization", "impact": "Optimization",
"confidence": "High", "confidence": "High"
"description": "funcNotCalled() should be declared external:\n\t- ContractWithFunctionNotCalled2.funcNotCalled() (tests/external_function.sol#32-39)\n", },
{
"elements": [ "elements": [
{ {
"type": "function", "type": "function",
@ -265,13 +264,14 @@
"signature": "funcNotCalled()" "signature": "funcNotCalled()"
} }
} }
] ],
}, "description": "funcNotCalled() should be declared external:\n\t- ContractWithFunctionNotCalled2.funcNotCalled() (tests/external_function.sol#32-39)\n",
{ "markdown": "funcNotCalled() should be declared external:\n\t- [ContractWithFunctionNotCalled2.funcNotCalled()](tests/external_function.sol#L32-L39)\n",
"check": "external-function", "check": "external-function",
"impact": "Optimization", "impact": "Optimization",
"confidence": "High", "confidence": "High"
"description": "parameter_read_ok_for_external(uint256) should be declared external:\n\t- FunctionParameterWrite.parameter_read_ok_for_external(uint256) (tests/external_function.sol#74-76)\n", },
{
"elements": [ "elements": [
{ {
"type": "function", "type": "function",
@ -324,7 +324,12 @@
"signature": "parameter_read_ok_for_external(uint256)" "signature": "parameter_read_ok_for_external(uint256)"
} }
} }
] ],
"description": "parameter_read_ok_for_external(uint256) should be declared external:\n\t- FunctionParameterWrite.parameter_read_ok_for_external(uint256) (tests/external_function.sol#74-76)\n",
"markdown": "parameter_read_ok_for_external(uint256) should be declared external:\n\t- [FunctionParameterWrite.parameter_read_ok_for_external(uint256)](tests/external_function.sol#L74-L76)\n",
"check": "external-function",
"impact": "Optimization",
"confidence": "High"
} }
] ]
} }

@ -1,27 +1,27 @@
INFO:Detectors: 
ERC20TestBalance.bad0(ERC20Function) (tests/incorrect_equality.sol#21-23) uses a dangerous strict equality: ERC20TestBalance.bad0(ERC20Function) (tests/incorrect_equality.sol#21-23) uses a dangerous strict equality:
- require(bool)(erc.balanceOf(address(this)) == 10) - require(bool)(erc.balanceOf(address(this)) == 10) (tests/incorrect_equality.sol#22)
ERC20TestBalance.bad1(ERC20Variable) (tests/incorrect_equality.sol#25-27) uses a dangerous strict equality: ERC20TestBalance.bad1(ERC20Variable) (tests/incorrect_equality.sol#25-27) uses a dangerous strict equality:
- require(bool)(erc.balanceOf(msg.sender) == 10) - require(bool)(erc.balanceOf(msg.sender) == 10) (tests/incorrect_equality.sol#26)
TestContractBalance.bad0() (tests/incorrect_equality.sol#32-35) uses a dangerous strict equality: TestContractBalance.bad0() (tests/incorrect_equality.sol#32-35) uses a dangerous strict equality:
- require(bool)(address(address(this)).balance == 10000000000000000000) - require(bool)(address(address(this)).balance == 10000000000000000000) (tests/incorrect_equality.sol#33)
TestContractBalance.bad1() (tests/incorrect_equality.sol#37-40) uses a dangerous strict equality: TestContractBalance.bad1() (tests/incorrect_equality.sol#37-40) uses a dangerous strict equality:
- require(bool)(10000000000000000000 == address(address(this)).balance) - require(bool)(10000000000000000000 == address(address(this)).balance) (tests/incorrect_equality.sol#38)
TestContractBalance.bad2() (tests/incorrect_equality.sol#42-45) uses a dangerous strict equality: TestContractBalance.bad2() (tests/incorrect_equality.sol#42-45) uses a dangerous strict equality:
- require(bool)(address(this).balance == 10000000000000000000) - require(bool)(address(this).balance == 10000000000000000000) (tests/incorrect_equality.sol#43)
TestContractBalance.bad3() (tests/incorrect_equality.sol#47-50) uses a dangerous strict equality: TestContractBalance.bad3() (tests/incorrect_equality.sol#47-50) uses a dangerous strict equality:
- require(bool)(10000000000000000000 == address(this).balance) - require(bool)(10000000000000000000 == address(this).balance) (tests/incorrect_equality.sol#48)
TestContractBalance.bad4() (tests/incorrect_equality.sol#52-57) uses a dangerous strict equality: TestContractBalance.bad4() (tests/incorrect_equality.sol#52-57) uses a dangerous strict equality:
- balance == 10000000000000000000 - balance == 10000000000000000000 (tests/incorrect_equality.sol#54)
TestContractBalance.bad5() (tests/incorrect_equality.sol#59-64) uses a dangerous strict equality: TestContractBalance.bad5() (tests/incorrect_equality.sol#59-64) uses a dangerous strict equality:
- 10000000000000000000 == balance - 10000000000000000000 == balance (tests/incorrect_equality.sol#61)
TestContractBalance.bad6() (tests/incorrect_equality.sol#66-71) uses a dangerous strict equality: TestContractBalance.bad6() (tests/incorrect_equality.sol#66-71) uses a dangerous strict equality:
- balance == 10000000000000000000 - balance == 10000000000000000000 (tests/incorrect_equality.sol#68)
TestSolidityKeyword.bad0() (tests/incorrect_equality.sol#123-125) uses a dangerous strict equality: TestSolidityKeyword.bad0() (tests/incorrect_equality.sol#123-125) uses a dangerous strict equality:
- require(bool)(now == 0) - require(bool)(now == 0) (tests/incorrect_equality.sol#124)
TestSolidityKeyword.bad1() (tests/incorrect_equality.sol#127-129) uses a dangerous strict equality: TestSolidityKeyword.bad1() (tests/incorrect_equality.sol#127-129) uses a dangerous strict equality:
- require(bool)(block.number == 0) - require(bool)(block.number == 0) (tests/incorrect_equality.sol#128)
TestSolidityKeyword.bad2() (tests/incorrect_equality.sol#131-133) uses a dangerous strict equality: TestSolidityKeyword.bad2() (tests/incorrect_equality.sol#131-133) uses a dangerous strict equality:
- require(bool)(block.number == 0) - require(bool)(block.number == 0) (tests/incorrect_equality.sol#132)
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-strict-equalities Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-strict-equalities
INFO:Slither:tests/incorrect_equality.sol analyzed (5 contracts), 12 result(s) found tests/incorrect_equality.sol analyzed (5 contracts with 1 detectors), 12 result(s) found

@ -4,11 +4,32 @@
"results": { "results": {
"detectors": [ "detectors": [
{ {
"check": "erc20-interface",
"impact": "Medium",
"confidence": "High",
"description": "Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface: transfer(address,uint256) (tests/incorrect_erc20_interface.sol#4)\n",
"elements": [ "elements": [
{
"type": "contract",
"name": "Token",
"source_mapping": {
"start": 26,
"length": 355,
"filename_used": "/home/travis/build/crytic/slither/tests/incorrect_erc20_interface.sol",
"filename_relative": "tests/incorrect_erc20_interface.sol",
"filename_absolute": "/home/travis/build/crytic/slither/tests/incorrect_erc20_interface.sol",
"filename_short": "tests/incorrect_erc20_interface.sol",
"is_dependency": false,
"lines": [
3,
4,
5,
6,
7,
8,
9,
10
],
"starting_column": 1,
"ending_column": 2
}
},
{ {
"type": "function", "type": "function",
"name": "transfer", "name": "transfer",
@ -55,14 +76,40 @@
"signature": "transfer(address,uint256)" "signature": "transfer(address,uint256)"
} }
} }
] ],
}, "description": "Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface:Token.transfer(address,uint256) (tests/incorrect_erc20_interface.sol#4)\n",
{ "markdown": "[Token](tests/incorrect_erc20_interface.sol#L3-L10) has incorrect ERC20 function interface:[Token.transfer(address,uint256)](tests/incorrect_erc20_interface.sol#L4)\n",
"check": "erc20-interface", "check": "erc20-interface",
"impact": "Medium", "impact": "Medium",
"confidence": "High", "confidence": "High"
"description": "Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface: approve(address,uint256) (tests/incorrect_erc20_interface.sol#5)\n", },
{
"elements": [ "elements": [
{
"type": "contract",
"name": "Token",
"source_mapping": {
"start": 26,
"length": 355,
"filename_used": "/home/travis/build/crytic/slither/tests/incorrect_erc20_interface.sol",
"filename_relative": "tests/incorrect_erc20_interface.sol",
"filename_absolute": "/home/travis/build/crytic/slither/tests/incorrect_erc20_interface.sol",
"filename_short": "tests/incorrect_erc20_interface.sol",
"is_dependency": false,
"lines": [
3,
4,
5,
6,
7,
8,
9,
10
],
"starting_column": 1,
"ending_column": 2
}
},
{ {
"type": "function", "type": "function",
"name": "approve", "name": "approve",
@ -109,14 +156,40 @@
"signature": "approve(address,uint256)" "signature": "approve(address,uint256)"
} }
} }
] ],
}, "description": "Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface:Token.approve(address,uint256) (tests/incorrect_erc20_interface.sol#5)\n",
{ "markdown": "[Token](tests/incorrect_erc20_interface.sol#L3-L10) has incorrect ERC20 function interface:[Token.approve(address,uint256)](tests/incorrect_erc20_interface.sol#L5)\n",
"check": "erc20-interface", "check": "erc20-interface",
"impact": "Medium", "impact": "Medium",
"confidence": "High", "confidence": "High"
"description": "Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface: transferFrom(address,address,uint256) (tests/incorrect_erc20_interface.sol#6)\n", },
{
"elements": [ "elements": [
{
"type": "contract",
"name": "Token",
"source_mapping": {
"start": 26,
"length": 355,
"filename_used": "/home/travis/build/crytic/slither/tests/incorrect_erc20_interface.sol",
"filename_relative": "tests/incorrect_erc20_interface.sol",
"filename_absolute": "/home/travis/build/crytic/slither/tests/incorrect_erc20_interface.sol",
"filename_short": "tests/incorrect_erc20_interface.sol",
"is_dependency": false,
"lines": [
3,
4,
5,
6,
7,
8,
9,
10
],
"starting_column": 1,
"ending_column": 2
}
},
{ {
"type": "function", "type": "function",
"name": "transferFrom", "name": "transferFrom",
@ -163,14 +236,40 @@
"signature": "transferFrom(address,address,uint256)" "signature": "transferFrom(address,address,uint256)"
} }
} }
] ],
}, "description": "Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface:Token.transferFrom(address,address,uint256) (tests/incorrect_erc20_interface.sol#6)\n",
{ "markdown": "[Token](tests/incorrect_erc20_interface.sol#L3-L10) has incorrect ERC20 function interface:[Token.transferFrom(address,address,uint256)](tests/incorrect_erc20_interface.sol#L6)\n",
"check": "erc20-interface", "check": "erc20-interface",
"impact": "Medium", "impact": "Medium",
"confidence": "High", "confidence": "High"
"description": "Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface: totalSupply() (tests/incorrect_erc20_interface.sol#7)\n", },
{
"elements": [ "elements": [
{
"type": "contract",
"name": "Token",
"source_mapping": {
"start": 26,
"length": 355,
"filename_used": "/home/travis/build/crytic/slither/tests/incorrect_erc20_interface.sol",
"filename_relative": "tests/incorrect_erc20_interface.sol",
"filename_absolute": "/home/travis/build/crytic/slither/tests/incorrect_erc20_interface.sol",
"filename_short": "tests/incorrect_erc20_interface.sol",
"is_dependency": false,
"lines": [
3,
4,
5,
6,
7,
8,
9,
10
],
"starting_column": 1,
"ending_column": 2
}
},
{ {
"type": "function", "type": "function",
"name": "totalSupply", "name": "totalSupply",
@ -217,14 +316,40 @@
"signature": "totalSupply()" "signature": "totalSupply()"
} }
} }
] ],
}, "description": "Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface:Token.totalSupply() (tests/incorrect_erc20_interface.sol#7)\n",
{ "markdown": "[Token](tests/incorrect_erc20_interface.sol#L3-L10) has incorrect ERC20 function interface:[Token.totalSupply()](tests/incorrect_erc20_interface.sol#L7)\n",
"check": "erc20-interface", "check": "erc20-interface",
"impact": "Medium", "impact": "Medium",
"confidence": "High", "confidence": "High"
"description": "Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface: balanceOf(address) (tests/incorrect_erc20_interface.sol#8)\n", },
{
"elements": [ "elements": [
{
"type": "contract",
"name": "Token",
"source_mapping": {
"start": 26,
"length": 355,
"filename_used": "/home/travis/build/crytic/slither/tests/incorrect_erc20_interface.sol",
"filename_relative": "tests/incorrect_erc20_interface.sol",
"filename_absolute": "/home/travis/build/crytic/slither/tests/incorrect_erc20_interface.sol",
"filename_short": "tests/incorrect_erc20_interface.sol",
"is_dependency": false,
"lines": [
3,
4,
5,
6,
7,
8,
9,
10
],
"starting_column": 1,
"ending_column": 2
}
},
{ {
"type": "function", "type": "function",
"name": "balanceOf", "name": "balanceOf",
@ -271,14 +396,40 @@
"signature": "balanceOf(address)" "signature": "balanceOf(address)"
} }
} }
] ],
}, "description": "Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface:Token.balanceOf(address) (tests/incorrect_erc20_interface.sol#8)\n",
{ "markdown": "[Token](tests/incorrect_erc20_interface.sol#L3-L10) has incorrect ERC20 function interface:[Token.balanceOf(address)](tests/incorrect_erc20_interface.sol#L8)\n",
"check": "erc20-interface", "check": "erc20-interface",
"impact": "Medium", "impact": "Medium",
"confidence": "High", "confidence": "High"
"description": "Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface: allowance(address,address) (tests/incorrect_erc20_interface.sol#9)\n", },
{
"elements": [ "elements": [
{
"type": "contract",
"name": "Token",
"source_mapping": {
"start": 26,
"length": 355,
"filename_used": "/home/travis/build/crytic/slither/tests/incorrect_erc20_interface.sol",
"filename_relative": "tests/incorrect_erc20_interface.sol",
"filename_absolute": "/home/travis/build/crytic/slither/tests/incorrect_erc20_interface.sol",
"filename_short": "tests/incorrect_erc20_interface.sol",
"is_dependency": false,
"lines": [
3,
4,
5,
6,
7,
8,
9,
10
],
"starting_column": 1,
"ending_column": 2
}
},
{ {
"type": "function", "type": "function",
"name": "allowance", "name": "allowance",
@ -325,7 +476,12 @@
"signature": "allowance(address,address)" "signature": "allowance(address,address)"
} }
} }
] ],
"description": "Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface:Token.allowance(address,address) (tests/incorrect_erc20_interface.sol#9)\n",
"markdown": "[Token](tests/incorrect_erc20_interface.sol#L3-L10) has incorrect ERC20 function interface:[Token.allowance(address,address)](tests/incorrect_erc20_interface.sol#L9)\n",
"check": "erc20-interface",
"impact": "Medium",
"confidence": "High"
} }
] ]
} }

@ -1,9 +1,9 @@
INFO:Detectors: 
Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface: transfer(address,uint256) (tests/incorrect_erc20_interface.sol#4) Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface:Token.transfer(address,uint256) (tests/incorrect_erc20_interface.sol#4)
Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface: approve(address,uint256) (tests/incorrect_erc20_interface.sol#5) Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface:Token.approve(address,uint256) (tests/incorrect_erc20_interface.sol#5)
Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface: transferFrom(address,address,uint256) (tests/incorrect_erc20_interface.sol#6) Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface:Token.transferFrom(address,address,uint256) (tests/incorrect_erc20_interface.sol#6)
Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface: totalSupply() (tests/incorrect_erc20_interface.sol#7) Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface:Token.totalSupply() (tests/incorrect_erc20_interface.sol#7)
Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface: balanceOf(address) (tests/incorrect_erc20_interface.sol#8) Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface:Token.balanceOf(address) (tests/incorrect_erc20_interface.sol#8)
Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface: allowance(address,address) (tests/incorrect_erc20_interface.sol#9) Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface:Token.allowance(address,address) (tests/incorrect_erc20_interface.sol#9)
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-erc20-interface Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-erc20-interface
INFO:Slither:tests/incorrect_erc20_interface.sol analyzed (1 contracts), 6 result(s) found tests/incorrect_erc20_interface.sol analyzed (1 contracts with 1 detectors), 6 result(s) found

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save