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!'
json = self.generate_json_result(info)
json = self.generate_result(info)
return [json]

@ -19,7 +19,7 @@ from slither.detectors.abstract_detector import (AbstractDetector,
from slither.printers import all_printers
from slither.printers.abstract_printer import AbstractPrinter
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.colors import red, yellow, set_colorization_enabled
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',
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',
help='Disable output colorization',
action='store_true',
@ -633,7 +638,7 @@ def main_impl(all_detector_classes, all_printer_classes):
'stderr': StandardOutputCapture.get_stderr_output()
}
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
if output_error:

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

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

@ -132,16 +132,22 @@ class SourceMapping(Context):
else:
self._source_mapping = self._convert_source_mapping(offset, slither)
@property
def source_mapping_str(self):
def _get_lines_str(self, line_descr=""):
lines = self.source_mapping.get('lines', None)
if not lines:
lines = ''
elif len(lines) == 1:
lines = '#{}'.format(lines[0])
lines = '#{}{}'.format(line_descr, lines[0])
else:
lines = '#{}-{}'.format(lines[0], lines[-1])
return '{}{}'.format(self.source_mapping['filename_short'], lines)
lines = '#{}{}-{}{}'.format(line_descr, lines[0], line_descr, lines[-1])
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
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.formatters.exceptions import FormatImpossible
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):
@ -49,6 +49,8 @@ class AbstractDetector(metaclass=abc.ABCMeta):
WIKI_EXPLOIT_SCENARIO = ''
WIKI_RECOMMENDATION = ''
STANDARD_JSON = True
def __init__(self, slither, logger):
self.slither = slither
self.contracts = slither.contracts
@ -101,10 +103,12 @@ class AbstractDetector(metaclass=abc.ABCMeta):
@abc.abstractmethod
def _detect(self):
"""TODO Documentation"""
return
return []
def detect(self):
all_results = self._detect()
# Keep only dictionaries
all_results = [r.data for r in all_results]
results = []
# 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]
@ -168,62 +172,17 @@ class AbstractDetector(metaclass=abc.ABCMeta):
def color(self):
return classification_colors[self.IMPACT]
def generate_json_result(self, info, additional_fields=None):
d = json_utils.generate_json_result(info, additional_fields)
d['check'] = self.ARGUMENT
d['impact'] = classification_txt[self.IMPACT]
d['confidence'] = classification_txt[self.CONFIDENCE]
def generate_result(self, info, additional_fields=None):
output = Output(info,
additional_fields,
standard_format=self.STANDARD_JSON,
markdown_root=self.slither.markdown_root)
return d
@staticmethod
def add_variable_to_json(e, d, additional_fields=None):
json_utils.add_variable_to_json(e, d, additional_fields=additional_fields)
output.data['check'] = self.ARGUMENT
output.data['impact'] = classification_txt[self.IMPACT]
output.data['confidence'] = classification_txt[self.CONFIDENCE]
@staticmethod
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)
return output
@staticmethod
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.contains_assembly:
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)
json = self.generate_json_result(info, {'contains_assembly': True})
self.add_function_to_json(f, json)
results.append(json)
info = [f, f' is declared {attr} but contains assembly code\n']
res = self.generate_result(info, {'contains_assembly': True})
results.append(res)
variables_written = f.all_state_variables_written()
if variables_written:
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:
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})
self.add_function_to_json(f, json)
self.add_variables_to_json(variables_written, json)
results.append(json)
results.append(res)
return results

@ -30,17 +30,15 @@ class ConstantPragma(AbstractDetector):
versions = sorted(list(set(versions)))
if len(versions) > 1:
info = "Different versions of Solidity is used in {}:\n".format(self.filename)
info += "\t- Version used: {}\n".format([str(v) for v in versions])
info = [f"Different versions of Solidity is used in {self.filename}:\n"]
info += [f"\t- Version used: {[str(v) for v in versions]}\n"]
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
for p in pragma:
self.add_pragma_to_json(p, json)
results.append(json)
results.append(res)
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 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)
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]
if funcs_payable:
if self.do_no_send_ether(contract):
txt = "Contract locking ether found in {}:\n".format(self.filename)
txt += "\tContract {} has payable functions:\n".format(contract.name)
info = [f"Contract locking ether found in {self.filename}:\n"]
info += ["\tContract ", contract, " has payable functions:\n"]
for function in funcs_payable:
txt += "\t - {} ({})\n".format(function.name, function.source_mapping_str)
txt += "\tBut does not have a function to withdraw the ether\n"
info = txt.format(self.filename,
contract.name,
[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)
info += [f"\t - ", function, "\n"]
info += "\tBut does not have a function to withdraw the ether\n"
json = self.generate_result(info)
results.append(json)
return results

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

@ -86,12 +86,9 @@ contract Token{
functions = IncorrectERC721InterfaceDetection.detect_incorrect_erc721_interface(c)
if functions:
for function in functions:
info = "{} ({}) has incorrect ERC721 function interface: {} ({})\n".format(c.name,
c.source_mapping_str,
function.full_name,
function.source_mapping_str)
json = self.generate_json_result(info)
self.add_function_to_json(function, json)
results.append(json)
info = [c, " has incorrect ERC721 function interface:", function, "\n"]
res = self.generate_result(info)
results.append(res)
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.'
STANDARD_JSON = False
@staticmethod
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
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).
json = self.generate_json_result(info)
self.add_event_to_json(event, json, {
"parameter_name": parameter.name
})
results.append(json)
res = self.generate_result(info)
res.add(event, {"parameter_name": parameter.name})
results.append(res)
return results

@ -26,11 +26,11 @@ class Backdoor(AbstractDetector):
for f in contract.functions:
if 'backdoor' in f.name:
# Info to be printed
info = 'Backdoor function found in {}.{} ({})\n'
info = info.format(contract.name, f.name, f.source_mapping_str)
info = ['Backdoor function found in ', f, '\n']
# Add the result in result
json = self.generate_json_result(info)
self.add_function_to_json(f, json)
results.append(json)
res = self.generate_result(info)
results.append(res)
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)
for (func, nodes) in arbitrary_send:
info = "{} ({}) sends eth to arbitrary user\n"
info = info.format(func.canonical_name,
func.source_mapping_str)
info += '\tDangerous calls:\n'
info = [func, " sends eth to arbitrary user\n"]
info += ['\tDangerous calls:\n']
for node in nodes:
info += '\t- {} ({})\n'.format(node.expression, node.source_mapping_str)
info += ['\t- ', node, '\n']
json = self.generate_json_result(info)
self.add_function_to_json(func, json)
self.add_nodes_to_json(nodes, json)
results.append(json)
res = self.generate_result(info)
results.append(res)
return results

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

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

@ -73,12 +73,10 @@ Bob calls `kill` and destructs the contract.'''
functions = self.detect_suicidal(c)
for func in functions:
txt = "{} ({}) allows anyone to destruct the contract\n"
info = txt.format(func.canonical_name,
func.source_mapping_str)
info = [func, " allows anyone to destruct the contract\n"]
json = self.generate_json_result(info)
self.add_function_to_json(func, json)
results.append(json)
res = self.generate_result(info)
results.append(res)
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).'
STANDARD_JSON = False
@staticmethod
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:
if not self.is_cap_words(contract.name):
info = "Contract '{}' ({}) is not in CapWords\n".format(contract.name,
contract.source_mapping_str)
info = ["Contract ", contract, " is not in CapWords\n"]
json = self.generate_json_result(info)
self.add_contract_to_json(contract, json, {
res = self.generate_result(info)
res.add(contract, {
"target": "contract",
"convention": "CapWords"
})
results.append(json)
results.append(res)
for struct in contract.structures_declared:
if not self.is_cap_words(struct.name):
info = "Struct '{}' ({}) is not in CapWords\n"
info = info.format(struct.canonical_name, struct.source_mapping_str)
info = ["Struct ", struct, " is not in CapWords\n"]
json = self.generate_json_result(info)
self.add_struct_to_json(struct, json, {
res = self.generate_result(info)
res.add(struct, {
"target": "structure",
"convention": "CapWords"
})
results.append(json)
results.append(res)
for event in contract.events_declared:
if not self.is_cap_words(event.name):
info = "Event '{}' ({}) is not in CapWords\n"
info = info.format(event.canonical_name, event.source_mapping_str)
info = ["Event ", event, " is not in CapWords\n"]
json = self.generate_json_result(info)
self.add_event_to_json(event, json, {
res = self.generate_result(info)
res.add(event, {
"target": "event",
"convention": "CapWords"
})
results.append(json)
results.append(res)
for func in contract.functions_declared:
if func.is_constructor:
@ -101,15 +99,14 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
continue
if func.name.startswith("echidna_") or func.name.startswith("crytic_"):
continue
info = "Function '{}' ({}) is not in mixedCase\n"
info = info.format(func.canonical_name, func.source_mapping_str)
info = ["Function ", func, " is not in mixedCase\n"]
json = self.generate_json_result(info)
self.add_function_to_json(func, json, {
res = self.generate_result(info)
res.add(func, {
"target": "function",
"convention": "mixedCase"
})
results.append(json)
results.append(res)
for argument in func.parameters:
# 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:
correct_naming = self.is_mixed_case_with_underscore(argument.name)
if not correct_naming:
info = "Parameter '{}' of {} ({}) is not in mixedCase\n"
info = info.format(argument.name,
argument.canonical_name,
argument.source_mapping_str)
info = ["Parameter ", argument, " is not in mixedCase\n"]
json = self.generate_json_result(info)
self.add_variable_to_json(argument, json, {
res = self.generate_result(info)
res.add(argument, {
"target": "parameter",
"convention": "mixedCase"
})
results.append(json)
results.append(res)
for var in contract.state_variables_declared:
if self.should_avoid_name(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 = info.format(var.canonical_name, var.source_mapping_str)
info = ["Variable ", var," used l, O, I, which should not be used\n"]
json = self.generate_json_result(info)
self.add_variable_to_json(var, json, {
res = self.generate_result(info)
res.add(var, {
"target": "variable",
"convention": "l_O_I_should_not_be_used"
})
results.append(json)
results.append(res)
if var.is_constant is True:
# For ERC20 compatibility
@ -151,15 +144,14 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
continue
if not self.is_upper_case_with_underscores(var.name):
info = "Constant '{}' ({}) is not in UPPER_CASE_WITH_UNDERSCORES\n"
info = info.format(var.canonical_name, var.source_mapping_str)
info = ["Constant ", var," is not in UPPER_CASE_WITH_UNDERSCORES\n"]
json = self.generate_json_result(info)
self.add_variable_to_json(var, json, {
res = self.generate_result(info)
res.add(var, {
"target": "variable_constant",
"convention": "UPPER_CASE_WITH_UNDERSCORES"
})
results.append(json)
results.append(res)
else:
if var.visibility == 'private':
@ -167,40 +159,36 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
else:
correct_naming = self.is_mixed_case(var.name)
if not correct_naming:
info = "Variable '{}' ({}) is not in mixedCase\n"
info = info.format(var.canonical_name, var.source_mapping_str)
info = ["Variable ", var, " is not in mixedCase\n"]
json = self.generate_json_result(info)
self.add_variable_to_json(var, json, {
res = self.generate_result(info)
res.add(var, {
"target": "variable",
"convention": "mixedCase"
})
results.append(json)
results.append(res)
for enum in contract.enums_declared:
if not self.is_cap_words(enum.name):
info = "Enum '{}' ({}) is not in CapWords\n"
info = info.format(enum.canonical_name, enum.source_mapping_str)
info = ["Enum ", enum, " is not in CapWords\n"]
json = self.generate_json_result(info)
self.add_enum_to_json(enum, json, {
res = self.generate_result(info)
res.add(enum, {
"target": "enum",
"convention": "CapWords"
})
results.append(json)
results.append(res)
for modifier in contract.modifiers_declared:
if not self.is_mixed_case(modifier.name):
info = "Modifier '{}' ({}) is not in mixedCase\n"
info = info.format(modifier.canonical_name,
modifier.source_mapping_str)
info = ["Modifier ", modifier, " is not in mixedCase\n"]
json = self.generate_json_result(info)
self.add_function_to_json(modifier, json, {
res = self.generate_result(info)
res.add(modifier, {
"target": "modifier",
"convention": "mixedCase"
})
results.append(json)
results.append(res)
return results

@ -69,16 +69,14 @@ class Timestamp(AbstractDetector):
dangerous_timestamp = self.detect_dangerous_timestamp(c)
for (func, nodes) in dangerous_timestamp:
info = "{} ({}) uses timestamp for comparisons\n"
info = info.format(func.canonical_name,
func.source_mapping_str)
info += '\tDangerous comparisons:\n'
info = [func, " uses timestamp for comparisons\n"]
info += ['\tDangerous comparisons:\n']
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)
self.add_function_to_json(func, json)
self.add_nodes_to_json(nodes, json)
results.append(json)
results.append(res)
return results

@ -48,14 +48,13 @@ class LowLevelCalls(AbstractDetector):
for c in self.contracts:
values = self.detect_low_level_calls(c)
for func, nodes in values:
info = "Low level call in {} ({}):\n"
info = info.format(func.canonical_name, func.source_mapping_str)
info = ["Low level call in ", func,":\n"]
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)
self.add_function_to_json(func, json)
self.add_nodes_to_json(nodes, json)
results.append(json)
results.append(res)
return results

@ -74,17 +74,11 @@ contract MyConc{
if unused_return:
for node in unused_return:
info = "{} ({}) ignores return value by {} \"{}\" ({})\n"
info = info.format(f.canonical_name,
f.source_mapping_str,
self._txt_description,
node.expression,
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)
info = [f, f" ignores return value by ", node, "\n"]
res = self.generate_result(info)
results.append(res)
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 node in constructor_call.nodes:
if any(isinstance(ir, Nop) for ir in node.irs):
info = "Void constructor called in {} ({}):\n"
info = info.format(cst.canonical_name, cst.source_mapping_str)
info += "\t-{} {}\n".format(str(node.expression), node.source_mapping_str)
json = self.generate_json_result(info)
self.add_function_to_json(cst, json)
self.add_nodes_to_json([node], json)
results.append(json)
info = ["Void constructor called in ", cst, ":\n"]
info += ["\t- ", node, "\n"]
res = self.generate_result(info)
results.append(res)
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).'
STANDARD_JSON = False
def find_reentrancies(self):
result = {}
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:
calls = sorted(list(set(calls)), key=lambda x: x.node_id)
send_eth = sorted(list(set(send_eth)), key=lambda x: x.node_id)
info = 'Reentrancy in {} ({}):\n'
info = info.format(func.canonical_name, func.source_mapping_str)
info += '\tExternal calls:\n'
info = ['Reentrancy in ', func, ':\n']
info += ['\tExternal calls:\n']
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:
info += '\tExternal calls sending eth:\n'
info += ['\tExternal calls sending eth:\n']
for call_info in send_eth:
info += '\t- {} ({})\n'.format(call_info.expression, call_info.source_mapping_str)
info += '\tState variables written after the call(s):\n'
info += ['\t- ', call_info, '\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)):
info += '\t- {} ({})\n'.format(v, node.source_mapping_str)
info += ['\t- ', v, ' in ', node, '\n']
# Create our JSON result
json = self.generate_json_result(info)
res = self.generate_result(info)
# 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.
for call_info in calls:
self.add_node_to_json(call_info, json, {
res.add(call_info, {
"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 calls != 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"
})
# Add all variables written via nodes which write them.
for (v, node) in varsWritten:
self.add_node_to_json(node, json, {
res.add(node, {
"underlying_type": "variables_written",
"variable_name": v.name
})
# Append our result
results.append(json)
results.append(res)
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).'
STANDARD_JSON = False
def find_reentrancies(self):
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)
send_eth = sorted(list(set(send_eth)), key=lambda x: x.node_id)
info = 'Reentrancy in {} ({}):\n'
info = info.format(func.canonical_name, func.source_mapping_str)
info += '\tExternal calls:\n'
info = ['Reentrancy in ', func, ':\n']
info += ['\tExternal calls:\n']
for call_info in calls:
info += '\t- {} ({})\n'.format(call_info.expression, call_info.source_mapping_str)
if calls != send_eth:
info += '\tExternal calls sending eth:\n'
info += ['\t- ' , call_info, '\n']
if calls != send_eth and send_eth:
info += ['\tExternal calls sending eth:\n']
for call_info in send_eth:
info += '\t- {} ({})\n'.format(call_info.expression, call_info.source_mapping_str)
info += '\tState variables written after the call(s):\n'
info += ['\t- ', call_info, '\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)):
info += '\t- {} ({})\n'.format(v, node.source_mapping_str)
info += ['\t- ', v, ' in ', node, '\n']
# Create our JSON result
json = self.generate_json_result(info)
res = self.generate_result(info)
# 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.
for call_info in calls:
self.add_node_to_json(call_info, json, {
res.add(call_info, {
"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 calls != 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"
})
# Add all variables written via nodes which write them.
for (v, node) in varsWritten:
self.add_node_to_json(node, json, {
res.add(node, {
"underlying_type": "variables_written",
"variable_name": v.name
})
# Append our result
results.append(json)
results.append(res)
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).'
STANDARD_JSON = False
def find_reentrancies(self):
result = {}
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)
for (func, calls), varsWritten in result_sorted:
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 += '\tExternal calls:\n'
info = ['Reentrancy in ', func, ':\n']
info += ['\tExternal calls:\n']
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'
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
json = self.generate_json_result(info)
res = self.generate_result(info)
# 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.
for call_info in calls:
self.add_node_to_json(call_info, json, {
res.add(call_info, {
"underlying_type": "external_calls"
})
# Add all variables written via nodes which write them.
for (v, node) in varsWritten:
self.add_node_to_json(node, json, {
res.add(node, {
"underlying_type": "variables_written",
"variable_name": v.name
})
# Append our result
results.append(json)
results.append(res)
return results

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

@ -77,7 +77,7 @@ contract Bug {
results = []
for local in function_or_modifier.variables:
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
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.
for function in contract.functions_declared:
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)
for modifier in contract.modifiers_declared:
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)
for variable in contract.state_variables_declared:
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:
if self.is_builtin_symbol(event.name):
result.append((self.SHADOWING_EVENT, event, None))
result.append((self.SHADOWING_EVENT, event))
return result
@ -124,27 +124,10 @@ contract Bug {
# Obtain components
shadow_type = shadow[0]
shadow_object = shadow[1]
local_variable_parent = shadow[2]
# Build the path for our info string
local_variable_path = contract.name + "."
if local_variable_parent is not None:
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)
info = [shadow_object, f' ({shadow_type}) shadows built-in symbol"\n']
res = self.generate_result(info)
results.append(res)
return results

@ -68,23 +68,23 @@ contract Bug {
# Check functions
for scope_function in scope_contract.functions_declared:
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
for scope_modifier in scope_contract.modifiers_declared:
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
for scope_event in scope_contract.events_declared:
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
for scope_state_variable in scope_contract.state_variables_declared:
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 overshadowed:
result.append((contract.name, function.name, variable, overshadowed))
result.append((variable, overshadowed))
return result
@ -102,29 +102,15 @@ contract Bug {
shadows = self.detect_shadowing_definitions(contract)
if shadows:
for shadow in shadows:
local_parent_name = shadow[1]
local_variable = shadow[2]
overshadowed = shadow[3]
info = '{}.{}.{} (local variable @ {}) shadows:\n'.format(contract.name,
local_parent_name,
local_variable.name,
local_variable.source_mapping_str)
local_variable = shadow[0]
overshadowed = shadow[1]
info = [local_variable, ' shadows:\n']
for overshadowed_entry in overshadowed:
info += "\t- {}.{} ({} @ {})\n".format(overshadowed_entry[1],
overshadowed_entry[2],
overshadowed_entry[0],
overshadowed_entry[2].source_mapping_str)
info += ["\t- ", overshadowed_entry[1], f" ({overshadowed_entry[0]})\n"]
# Generate relevant JSON data for this shadowing definition.
json = self.generate_json_result(info)
self.add_variable_to_json(local_variable, json)
for overshadowed_entry in overshadowed:
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)
res = self.generate_result(info)
results.append(res)
return results

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

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

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

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

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

@ -152,20 +152,12 @@ contract ContractWithDeprecatedReferences {
for deprecated_reference in deprecated_references:
source_object = deprecated_reference[0]
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:
info += "\t- Usage of \"{}\" should be replaced with \"{}\"\n".format(original_desc,
recommended_disc)
info += [f"\t- Usage of \"{original_desc}\" should be replaced with \"{recommended_disc}\"\n"]
# Generate relevant JSON data for this deprecated standard.
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)
res = self.generate_result(info)
results.append(res)
return results

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

@ -64,15 +64,12 @@ Use:
# iterate over all the nodes
ret = self._detect_too_many_digits(f)
if ret:
func_info = '{}.{} ({}) uses literals with too many digits:'.format(f.contract.name,
f.name,
f.source_mapping_str)
func_info = [f, ' uses literals with too many digits:']
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
json = self.generate_json_result(node_info)
self.add_node_to_json(node, json)
results.append(json)
res = self.generate_result(node_info)
results.append(res)
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 node in nodes:
info = "{} uses tx.origin for authorization: \"{}\" ({})\n".format(func.canonical_name,
node.expression,
node.source_mapping_str)
json = self.generate_json_result(info)
self.add_node_to_json(node, json)
results.append(json)
info = [func, " uses tx.origin for authorization: ", node, "\n"]
res = self.generate_result(info)
results.append(res)
return results

@ -88,11 +88,8 @@ class ConstCandidateStateVars(AbstractDetector):
# Create a result for each finding
for v in constable_variables:
info = "{} should be constant ({})\n".format(v.canonical_name,
v.source_mapping_str)
json = self.generate_json_result(info)
self.add_variable_to_json(v, json)
info = [v, " should be constant\n"]
json = self.generate_result(info)
results.append(json)
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, [])
all_results = list(set(self.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 = info.format(var_name,
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)
info = [uninitialized_local_variable, " is a local variable never initialiazed\n"]
json = self.generate_result(info)
results.append(json)
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:
ret = self.detect_uninitialized(c)
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]
source += [f.source_mapping for f in functions]
info = [variable, " is never initialized. It is used in:\n"]
for f in functions:
info += ["\t- ", f, "\n"]
json = self.generate_json_result(info)
self.add_variable_to_json(variable, json)
self.add_functions_to_json(functions, json)
json = self.generate_result(info)
results.append(json)
return results

@ -101,14 +101,8 @@ Bob calls `func`. As a result, `owner` is override to 0.
self._detect_uninitialized(function, function.entry_point, [])
for(function, uninitialized_storage_variable) in self.results:
var_name = uninitialized_storage_variable.name
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)
info = [uninitialized_storage_variable, " is a storage variable never initialiazed\n"]
json = self.generate_result(info)
results.append(json)
return results

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

@ -1,6 +1,6 @@
import abc
from slither.utils import json_utils
from slither.utils import output
class IncorrectPrinterInitialization(Exception):
@ -33,38 +33,14 @@ class AbstractPrinter(metaclass=abc.ABCMeta):
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:
additional_fields = {}
d = json_utils.generate_json_result(info, additional_fields)
d['printer'] = self.ARGUMENT
d = output.Output(info, additional_fields)
d.data['printer'] = self.ARGUMENT
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
def output(self, filename):
"""TODO Documentation"""

@ -176,9 +176,9 @@ class PrinterCallGraph(AbstractPrinter):
results.append((filename, content))
self.info(info)
json = self.generate_json_result(info)
res = self.generate_output(info)
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'
self.info(txt)
json = self.generate_json_result(txt)
res = self.generate_output(txt)
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)
json = self.generate_json_result(info)
res = self.generate_output(info)
for filename, content in all_files:
self.add_file_to_json(filename, content, json)
return json
res.add_file(filename, content)
return res

@ -140,4 +140,6 @@ class Echidna(AbstractPrinter):
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))
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:
f.write(content)
json = self.generate_json_result(info)
self.add_file_to_json(filename, content, json)
res = self.generate_output(info)
res.add_file(filename, content)
return json
return res

@ -2,46 +2,51 @@
Module printing summary of the contract
"""
from slither.printers.abstract_printer import AbstractPrinter
from slither.utils import output
class ConstructorPrinter(AbstractPrinter):
WIKI = 'https://github.com/crytic/slither/wiki/Printer-documentation#constructor-calls'
ARGUMENT = 'constructor-calls'
HELP = 'Print the constructors executed'
def _get_soruce_code(self,cst):
src_mapping = cst.source_mapping
content= self.slither.source_code[src_mapping['filename_absolute']]
start = src_mapping['start']
end = src_mapping['start'] + src_mapping['length']
initial_space = src_mapping['starting_column']
return ' ' * initial_space + content[start:end]
WIKI = 'https://github.com/crytic/slither/wiki/Printer-documentation#constructor-calls'
ARGUMENT = 'constructor-calls'
HELP = 'Print the constructors executed'
def _get_soruce_code(self, cst):
src_mapping = cst.source_mapping
content = self.slither.source_code[src_mapping['filename_absolute']]
start = src_mapping['start']
end = src_mapping['start'] + src_mapping['length']
initial_space = src_mapping['starting_column']
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):
for contract in self.contracts:
stack_name = []
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
self.info(info)
res = output.Output(info)
return res

@ -3,6 +3,7 @@
"""
import collections
from slither.printers.abstract_printer import AbstractPrinter
from slither.utils import output
from slither.utils.colors import blue, green, magenta
@ -24,7 +25,7 @@ class ContractSummary(AbstractPrinter):
all_contracts = []
for c in self.contracts:
txt += blue("\n+ Contract %s\n" % c.name)
additional_fields = {"elements": []}
additional_fields = output.Output('')
# Order the function with
# contract_declarer -> list_functions
@ -47,15 +48,15 @@ class ContractSummary(AbstractPrinter):
if function.visibility not in ['external', 'public', 'internal', 'private']:
txt += " - {}  ({})\n".format(function, function.visibility)
self.add_function_to_json(function, additional_fields, additional_fields={"visibility":
function.visibility})
additional_fields.add(function, additional_fields={"visibility":
function.visibility})
all_contracts.append((c, additional_fields))
all_contracts.append((c, additional_fields.data))
self.info(txt)
json = self.generate_json_result(txt)
res = self.generate_output(txt)
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_tables.append((c.name, table))
json = self.generate_json_result(all_txt)
res = self.generate_output(all_txt)
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_txt += txt
json = self.generate_json_result(all_txt)
res = self.generate_output(all_txt)
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)
json = self.generate_json_result(txt)
res = self.generate_output(txt)
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
from slither.printers.abstract_printer import AbstractPrinter
from slither.utils import output
from slither.utils.code_complexity import compute_cyclomatic_complexity
from slither.utils.colors import green, red, yellow
from slither.utils.standard_libraries import is_standard_library
@ -205,6 +206,7 @@ class PrinterHumanSummary(AbstractPrinter):
'ercs': [],
}
lines_number = self._lines_number()
if lines_number:
total_lines, total_dep_lines = lines_number
@ -241,6 +243,7 @@ class PrinterHumanSummary(AbstractPrinter):
self.info(txt)
results_contract = output.Output('')
for contract in self.slither.contracts_derived:
optimization, info, low, medium, high = self._get_detectors_result()
contract_d = {'contract_name': contract.name,
@ -262,9 +265,11 @@ class PrinterHumanSummary(AbstractPrinter):
contract_d['erc20_can_mint'] = False
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

@ -40,8 +40,8 @@ class Modifiers(AbstractPrinter):
txt += "\n"+str(table)
self.info(txt)
json = self.generate_json_result(all_txt)
res = self.generate_output(all_txt)
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_txt += txt
json = self.generate_json_result(all_txt)
res = self.generate_output(all_txt)
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
class PrinterSlithIR(AbstractPrinter):
class PrinterSlithIR(AbstractPrinter):
ARGUMENT = 'slithir'
HELP = 'Print the slithIR representation of the functions'
@ -20,30 +20,32 @@ class PrinterSlithIR(AbstractPrinter):
txt = ""
for contract in self.contracts:
print('Contract {}'.format(contract.name))
txt += 'Contract {}'.format(contract.name)
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:
if node.expression:
print('\t\tExpression: {}'.format(node.expression))
print('\t\tIRs:')
txt += '\t\tExpression: {}'.format(node.expression)
txt += '\t\tIRs:'
for ir in node.irs:
print('\t\t\t{}'.format(ir))
txt += '\t\t\t{}'.format(ir)
elif node.irs:
print('\t\tIRs:')
txt += '\t\tIRs:'
for ir in node.irs:
print('\t\t\t{}'.format(ir))
txt += '\t\t\t{}'.format(ir)
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:
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:
print('\tModifier {}'.format(modifier.canonical_name))
txt += '\tModifier {}'.format(modifier.canonical_name)
for node in modifier.nodes:
print(node)
txt += str(node)
if node.expression:
print('\t\tExpression: {}'.format(node.expression))
print('\t\tIRs:')
txt += '\t\tExpression: {}'.format(node.expression)
txt += '\t\tIRs:'
for ir in node.irs:
print('\t\t\t{}'.format(ir))
txt += '\t\t\t{}'.format(ir)
self.info(txt)
res = self.generate_output(txt)
return res

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

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

@ -68,6 +68,8 @@ class Slither(SlitherSolc):
if kwargs.get('generate_patches', False):
self.generate_patches = True
self._markdown_root = kwargs.get('markdown_root', "")
self._detectors = []
self._printers = []
@ -163,7 +165,7 @@ class Slither(SlitherSolc):
: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):

@ -5,7 +5,7 @@ from collections import defaultdict
from slither import Slither
from crytic_compile import cryticparser
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.erc20 import check_erc20
@ -59,7 +59,7 @@ def parse_args():
def _log_error(err, args):
if args.json:
output_json(args.json, str(err), {"upgradeability-check": []})
output_to_json(args.json, str(err), {"upgradeability-check": []})
logger.error(err)
@ -92,7 +92,7 @@ def main():
return
if args.json:
output_json(args.json, None, {"upgradeability-check": ret})
output_to_json(args.json, None, {"upgradeability-check": ret})
if __name__ == '__main__':

@ -1,6 +1,6 @@
import logging
from slither.utils import json_utils
from slither.utils import output
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'
logger.info(txt)
lack_of_erc20_race_condition_protection = json_utils.generate_json_result(txt)
json_utils.add_contract_to_json(contract, lack_of_erc20_race_condition_protection)
ret["lack_of_erc20_race_condition_protection"].append(lack_of_erc20_race_condition_protection)
lack_of_erc20_race_condition_protection = output.Output(txt)
lack_of_erc20_race_condition_protection.add(contract)
ret["lack_of_erc20_race_condition_protection"].append(lack_of_erc20_race_condition_protection.data)
def check_erc20(contract, ret, explored=None):

@ -1,7 +1,7 @@
import logging
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
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']:
txt = f'[ ] {sig} is missing {"" if required else "(optional)"}'
logger.info(txt)
missing_func = json_utils.generate_json_result(txt, additional_fields={
missing_func = output.Output(txt, additional_fields={
"function": sig,
"required": required
})
json_utils.add_contract_to_json(contract, missing_func)
ret["missing_function"].append(missing_func)
missing_func.add(contract)
ret["missing_function"].append(missing_func.data)
return
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:
txt = f'[ ] {sig} is missing {"" if required else "(optional)"}'
logger.info(txt)
missing_func = json_utils.generate_json_result(txt, additional_fields={
missing_func = output.Output(txt, additional_fields={
"function": sig,
"required": required
})
json_utils.add_contract_to_json(contract, missing_func)
ret["missing_function"].append(missing_func)
missing_func.add(contract)
ret["missing_function"].append(missing_func.data)
return
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}'
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,
"actual_return_type": function_return_type
})
json_utils.add_function_to_json(function, incorrect_return)
ret["incorrect_return_type"].append(incorrect_return)
incorrect_return.add(function)
ret["incorrect_return_type"].append(incorrect_return.data)
elif not 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}'
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,
"actual_return_type": function_return_type
})
json_utils.add_function_to_json(function, incorrect_return)
ret["incorrect_return_type"].append(incorrect_return)
incorrect_return.add(function)
ret["incorrect_return_type"].append(incorrect_return.data)
if view:
if function_view:
@ -94,9 +94,9 @@ def _check_signature(erc_function, contract, ret):
txt = f'\t[ ] {sig} should be view'
logger.info(txt)
should_be_view = json_utils.generate_json_result(txt)
json_utils.add_function_to_json(function, should_be_view)
ret["should_be_view"].append(should_be_view)
should_be_view = output.Output(txt)
should_be_view.add(function)
ret["should_be_view"].append(should_be_view.data)
if 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}'
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
})
json_utils.add_function_to_json(function, missing_event_emmited)
ret["missing_event_emmited"].append(missing_event_emmited)
missing_event_emmited.add(function)
ret["missing_event_emmited"].append(missing_event_emmited.data)
else:
event_found = False
@ -127,11 +127,11 @@ def _check_signature(erc_function, contract, ret):
txt = f'\t[ ] Must emit be view {event_sig}'
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
})
json_utils.add_function_to_json(function, missing_event_emmited)
ret["missing_event_emmited"].append(missing_event_emmited)
missing_event_emmited.add(function)
ret["missing_event_emmited"].append(missing_event_emmited.data)
def _check_events(erc_event, contract, ret):
@ -146,12 +146,11 @@ def _check_events(erc_event, contract, ret):
txt = f'[ ] {sig} is missing'
logger.info(txt)
missing_event = json_utils.generate_json_result(txt, additional_fields={
missing_event = output.Output(txt, additional_fields={
"event": sig
})
json_utils.add_contract_to_json(contract, missing_event)
ret["missing_event"].append(missing_event)
missing_event.add(contract)
ret["missing_event"].append(missing_event.data)
return
txt = f'[✓] {sig} is present'
@ -166,11 +165,11 @@ def _check_events(erc_event, contract, ret):
txt = f'\t[ ] parameter {i} should be indexed'
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
})
json_utils.add_event_to_json(event, missing_event_index)
ret["missing_event_index"].append(missing_event_index)
missing_event_index.add_event(event)
ret["missing_event_index"].append(missing_event_index.data)
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.variables.variable import Variable
from slither.utils.colors import yellow, green, red
from slither.utils import json_utils
from slither.utils import output
logging.basicConfig(level=logging.WARNING)
logger = logging.getLogger('Slither.kspec')
def _refactor_type(type):
return {
'uint': 'uint256',
'int': 'int256'
}.get(type, type)
def _get_all_covered_kspec_functions(target):
# Create a set of our discovered functions which are covered
covered_functions = set()
@ -56,9 +58,10 @@ def _get_slither_functions(slither):
# TODO: integrate state variables
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}
return slither_functions
def _generate_output(kspec, message, color, generate_json):
info = ""
for function in kspec:
@ -67,12 +70,13 @@ def _generate_output(kspec, message, color, generate_json):
logger.info(color(info))
if generate_json:
json_kspec_present = json_utils.generate_json_result(info)
json_kspec_present = output.Output(info)
for function in kspec:
json_utils.add_function_to_json(function, json_kspec_present)
return json_kspec_present
json_kspec_present.add(function)
return json_kspec_present.data
return None
def _generate_output_unresolved(kspec, message, color, generate_json):
info = ""
for contract, function in kspec:
@ -81,8 +85,8 @@ def _generate_output_unresolved(kspec, message, color, generate_json):
logger.info(color(info))
if generate_json:
json_kspec_present = json_utils.generate_json_result(info, additional_fields={"signatures": kspec})
return json_kspec_present
json_kspec_present = output.Output(info, additional_fields={"signatures": kspec})
return json_kspec_present.data
return None
@ -95,7 +99,6 @@ def _run_coverage_analysis(args, slither, kspec_functions):
kspec_functions_resolved = kspec_functions & slither_functions_set
kspec_functions_unresolved = kspec_functions - kspec_functions_resolved
kspec_missing = []
kspec_present = []
@ -114,23 +117,24 @@ def _run_coverage_analysis(args, slither, kspec_functions):
red,
args.json)
json_kspec_missing_variables = _generate_output([f for f in kspec_missing if isinstance(f, Variable)],
"[ ] (Missing variable)",
yellow,
args.json)
"[ ] (Missing variable)",
yellow,
args.json)
json_kspec_unresolved = _generate_output_unresolved(kspec_functions_unresolved,
"[ ] (Unresolved)",
yellow,
args.json)
# Handle unresolved kspecs
if args.json:
json_utils.output_json(args.json, None, {
"functions_present": json_kspec_present,
"functions_missing": json_kspec_missing_functions,
"variables_missing": json_kspec_missing_variables,
"functions_unresolved": json_kspec_unresolved
})
output.output_to_json(args.json, None, {
"functions_present": json_kspec_present,
"functions_missing": json_kspec_missing_functions,
"variables_missing": json_kspec_missing_variables,
"functions_unresolved": json_kspec_unresolved
})
def run_analysis(args, slither, kspec):
# Get all of our kspec'd functions (tuple(contract_name, function_name)).
if ',' in kspec:
@ -143,4 +147,3 @@ def run_analysis(args, slither, kspec):
# Run coverage analysis
_run_coverage_analysis(args, slither, kspec_functions)

@ -7,7 +7,7 @@ from slither import Slither
from crytic_compile import cryticparser
from slither.exceptions import SlitherException
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_function_ids import compare_function_ids
@ -135,7 +135,7 @@ def main():
info = 'Contract {} not found in {}'.format(v1_name, v1.filename)
logger.error(red(info))
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
_checks_on_contract(v1_contract, json_results)
@ -153,7 +153,7 @@ def main():
info = 'Proxy {} not found in {}'.format(args.proxy_name, proxy.filename)
logger.error(red(info))
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
json_results['proxy-present'] = True
_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)
logger.error(red(info))
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
json_results['contract_v2-present'] = True
@ -183,12 +183,12 @@ def main():
_checks_on_contract_update(v1_contract, v2_contract, json_results)
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:
logger.error(str(e))
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
# endregion

@ -1,7 +1,7 @@
import logging
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
logger = logging.getLogger("Slither-check-upgradeability")
@ -74,9 +74,9 @@ def check_initialization(contract):
initializer_modifier_missing = True
info = f'{f.canonical_name} does not call the initializer modifier'
logger.info(red(info))
json_elem = json_utils.generate_json_result(info)
json_utils.add_function_to_json(f, json_elem)
results['missing-initializer-modifier'].append(json_elem)
res = Output(info)
res.add(f)
results['missing-initializer-modifier'].append(res.data)
if not initializer_modifier_missing:
logger.info(green('All the init functions have the initializer modifier'))
@ -103,10 +103,10 @@ def check_initialization(contract):
for f in missing_calls:
info = f'Missing call to {f.canonical_name} in {most_derived_init.canonical_name}'
logger.info(red(info))
json_elem = json_utils.generate_json_result(info)
json_utils.add_function_to_json(f, json_elem, {"is_most_derived_init_function": False})
json_utils.add_function_to_json(most_derived_init, json_elem, {"is_most_derived_init_function": True})
results['missing-calls'].append(json_elem)
res = Output(info)
res.add(f, {"is_most_derived_init_function": False})
res.add(most_derived_init, {"is_most_derived_init_function": True})
results['missing-calls'].append(res.data)
missing_call = True
if not missing_call:
logger.info(green('No missing call to an init function found'))
@ -117,9 +117,9 @@ def check_initialization(contract):
for f in double_calls:
info = f'{f.canonical_name} is called multiple times in {most_derived_init.full_name}'
logger.info(red(info))
json_elem = json_utils.generate_json_result(info)
json_utils.add_function_to_json(f, json_elem)
results['multiple-calls'].append(json_elem)
res = Output(info)
res.add(f)
results['multiple-calls'].append(res.data)
double_calls_found = True
if not double_calls_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'
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)
json_utils.add_function_to_json(most_derived_init, json_elem)
results['initialize_target'] = json_elem
res = Output(init_info)
res.add(most_derived_init)
results['initialize_target'] = res.data
if not error_found:
logger.info(green('No error found'))

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

@ -4,10 +4,9 @@
'''
import logging
from slither import Slither
from slither.core.declarations import Function
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.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})'
logger.info(red(info))
json_elem = json_utils.generate_json_result(info)
if isinstance(implem_function, Function):
json_utils.add_function_to_json(implem_function, json_elem)
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['function-id-collision'].append(json_elem)
res = Output(info)
res.add(implem_function)
res.add(proxy_function)
results['function-id-collision'].append(res.data)
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})'
logger.info(red(info))
json_elem = json_utils.generate_json_result(info)
json_elem = json_utils.generate_json_result(info)
if isinstance(implem_function, Function):
json_utils.add_function_to_json(implem_function, json_elem)
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)
res = Output(info)
res.add(implem_function)
res.add(proxy_function)
results['shadowing'].append(res.data)
if not error_found:
logger.info(green('No error found'))

@ -3,7 +3,7 @@
'''
import logging
from slither.utils import json_utils
from slither.utils.output import Output
from slither.utils.colors import red, green, yellow
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})'
logger.info(yellow(info))
json_elem = json_utils.generate_json_result(info)
json_utils.add_variable_to_json(variable1, json_elem)
results['missing_variables'].append(json_elem)
res = Output(info)
res.add(variable1)
results['missing_variables'].append(res.data)
error_found = True
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'
logger.info(red(info))
json_elem = json_utils.generate_json_result(info, additional_fields={'index': idx})
json_utils.add_variable_to_json(variable1, json_elem)
json_utils.add_variable_to_json(variable2, json_elem)
results['different-variables'].append(json_elem)
res = Output(info, additional_fields={'index': idx})
res.add(variable1)
res.add(variable2)
results['different-variables'].append(res.data)
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'
logger.info(yellow(info))
json_elem = json_utils.generate_json_result(info, additional_fields={'index': idx})
json_utils.add_variable_to_json(variable2, json_elem)
results['extra-variables'].append(json_elem)
res = Output(info, additional_fields={'index': idx})
res.add(variable2)
results['extra-variables'].append(res.data)
idx = idx + 1
if not error_found:

@ -1,6 +1,6 @@
import logging
from slither.utils import json_utils
from slither.utils.output import Output
from slither.utils.colors import red, yellow, green
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})'
logger.info(red(info))
json_elem = json_utils.generate_json_result(info)
json_utils.add_variable_to_json(state_v1, json_elem)
json_utils.add_variable_to_json(state_v2, json_elem)
results['were_constants'].append(json_elem)
res = Output(info)
res.add(state_v1)
res.add(state_v2)
results['were_constants'].append(res.data)
error_found = True
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})'
logger.info(red(info))
json_elem = json_utils.generate_json_result(info)
json_utils.add_variable_to_json(state_v1, json_elem)
json_utils.add_variable_to_json(state_v2, json_elem)
results['became_constants'].append(json_elem)
res = Output(info)
res.add(state_v1)
res.add(state_v2)
results['became_constants'].append(res.data)
error_found = True
else:
info = f'{state_v1.canonical_name} not found in {contract_v2.name}, not check was done'
logger.info(yellow(info))
json_elem = json_utils.generate_json_result(info)
json_utils.add_variable_to_json(state_v1, json_elem)
json_utils.add_contract_to_json(contract_v2, json_elem)
results['not_found_in_v2'].append(json_elem)
res = Output(info)
res.add(state_v1)
res.add(contract_v2)
results['not_found_in_v2'].append(res.data)
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": {
"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": [
{
"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",
"impact": "High",
"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",
"confidence": "Medium"
},
{
"elements": [
{
"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
Dangerous calls:
- 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:
- 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
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": {
"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": [
{
"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",
"impact": "High",
"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",
"confidence": "Medium"
},
{
"elements": [
{
"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
Dangerous calls:
- 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:
- 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
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": {
"detectors": [
{
"check": "backdoor",
"impact": "High",
"confidence": "High",
"description": "Backdoor function found in C.i_am_a_backdoor (tests/backdoor.sol#4-6)\n",
"elements": [
{
"type": "function",
@ -56,7 +52,12 @@
"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
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:tests/backdoor.sol analyzed (1 contracts), 1 result(s) found

@ -4,10 +4,6 @@
"results": {
"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": [
{
"type": "function",
@ -56,7 +52,12 @@
"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
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:tests/backdoor.sol analyzed (1 contracts), 1 result(s) found

@ -4,10 +4,6 @@
"results": {
"detectors": [
{
"check": "constable-states",
"impact": "Optimization",
"confidence": "High",
"description": "A.myFriendsAddress should be constant (tests/const_state_variables.sol#7)\n",
"elements": [
{
"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",
"impact": "Optimization",
"confidence": "High",
"description": "A.test should be constant (tests/const_state_variables.sol#10)\n",
"confidence": "High"
},
{
"elements": [
{
"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",
"impact": "Optimization",
"confidence": "High",
"description": "A.text2 should be constant (tests/const_state_variables.sol#14)\n",
"confidence": "High"
},
{
"elements": [
{
"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",
"impact": "Optimization",
"confidence": "High",
"description": "B.mySistersAddress should be constant (tests/const_state_variables.sol#26)\n",
"confidence": "High"
},
{
"elements": [
{
"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",
"impact": "Optimization",
"confidence": "High",
"description": "MyConc.should_be_constant should be constant (tests/const_state_variables.sol#42)\n",
"confidence": "High"
},
{
"elements": [
{
"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",
"impact": "Optimization",
"confidence": "High",
"description": "MyConc.should_be_constant_2 should be constant (tests/const_state_variables.sol#43)\n",
"confidence": "High"
},
{
"elements": [
{
"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.test should be constant (tests/const_state_variables.sol#10)
A.text2 should be constant (tests/const_state_variables.sol#14)
B.mySistersAddress should be constant (tests/const_state_variables.sol#26)
MyConc.should_be_constant should be constant (tests/const_state_variables.sol#42)
MyConc.should_be_constant_2 should be constant (tests/const_state_variables.sol#43)

A.myFriendsAddress (tests/const_state_variables.sol#7) should be constant
A.test (tests/const_state_variables.sol#10) should be constant
A.text2 (tests/const_state_variables.sol#14) should be constant
B.mySistersAddress (tests/const_state_variables.sol#26) should be constant
MyConc.should_be_constant (tests/const_state_variables.sol#42) should be constant
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
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": {
"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": [
{
"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": {
"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
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": {
"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": [
{
"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": {
"contains_assembly": false
}
},
{
},
"check": "constant-function",
"impact": "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",
"confidence": "Medium"
},
{
"elements": [
{
"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": {
"contains_assembly": false
}
},
{
},
"check": "constant-function",
"impact": "Medium",
"confidence": "Medium",
"description": "Constant.test_assembly_bug() (tests/constant.sol#22-24) is declared view but contains assembly code\n",
"confidence": "Medium"
},
{
"elements": [
{
"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": {
"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.a
- Constant.a (tests/constant.sol#3)
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
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": {
"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": [
{
"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",
"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",
"name": "bad_delegate_call",
"name": "bad_delegate_call2",
"source_mapping": {
"start": 101,
"length": 134,
"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": [
8,
9,
10,
11
18,
19,
20
],
"starting_column": 5,
"ending_column": 6
@ -158,17 +228,9 @@
"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",
"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)
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)
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": {
"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": [
{
"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",
"impact": "Informational",
"confidence": "High",
"description": "Deprecated standard detected @ tests/deprecated_calls.sol#7:\n\t- Usage of \"msg.gas\" should be replaced with \"gasleft()\"\n",
"confidence": "High"
},
{
"elements": [
{
"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",
"impact": "Informational",
"confidence": "High",
"description": "Deprecated standard detected @ tests/deprecated_calls.sol#9:\n\t- Usage of \"throw\" should be replaced with \"revert()\"\n",
"confidence": "High"
},
{
"elements": [
{
"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",
"impact": "Informational",
"confidence": "High",
"description": "Deprecated standard detected @ tests/deprecated_calls.sol#16:\n\t- Usage of \"sha3()\" should be replaced with \"keccak256()\"\n",
"confidence": "High"
},
{
"elements": [
{
"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",
"impact": "Informational",
"confidence": "High",
"description": "Deprecated standard detected @ tests/deprecated_calls.sol#19:\n\t- Usage of \"block.blockhash()\" should be replaced with \"blockhash()\"\n",
"confidence": "High"
},
{
"elements": [
{
"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",
"impact": "Informational",
"confidence": "High",
"description": "Deprecated standard detected @ tests/deprecated_calls.sol#22:\n\t- Usage of \"callcode\" should be replaced with \"delegatecall\"\n",
"confidence": "High"
},
{
"elements": [
{
"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",
"impact": "Informational",
"confidence": "High",
"description": "Deprecated standard detected @ tests/deprecated_calls.sol#25:\n\t- Usage of \"suicide()\" should be replaced with \"selfdestruct()\"\n",
"confidence": "High"
},
{
"elements": [
{
"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",
"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",
"confidence": "High"
},
{
"elements": [
{
"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()"
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()"
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()"
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()"
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()"
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"
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()"
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()"
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": {
"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": [
{
"type": "event",
@ -60,13 +56,14 @@
"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",
"impact": "Informational",
"confidence": "High",
"description": "ERC20 event IERC20Bad.Transfer (tests/erc20_indexed.sol#19) does not index parameter 'to'\n",
"confidence": "High"
},
{
"elements": [
{
"type": "event",
@ -119,13 +116,14 @@
"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",
"impact": "Informational",
"confidence": "High",
"description": "ERC20 event IERC20Bad.Approval (tests/erc20_indexed.sol#20) does not index parameter 'owner'\n",
"confidence": "High"
},
{
"elements": [
{
"type": "event",
@ -178,13 +176,14 @@
"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",
"impact": "Informational",
"confidence": "High",
"description": "ERC20 event IERC20Bad.Approval (tests/erc20_indexed.sol#20) does not index parameter 'spender'\n",
"confidence": "High"
},
{
"elements": [
{
"type": "event",
@ -237,7 +236,12 @@
"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 IERC20Bad.Transfer (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 IERC20Bad.Approval (tests/erc20_indexed.sol#20) does not index parameter 'spender'

ERC20 event IERC20BadTransfer(address,address,uint256) (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 to
ERC20 event IERC20BadApproval(address,address,uint256) (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 spender
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": {
"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": [
{
"type": "function",
@ -68,13 +64,14 @@
"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",
"impact": "Optimization",
"confidence": "High",
"description": "funcNotCalled2() should be declared external:\n\t- ContractWithFunctionNotCalled.funcNotCalled2() (tests/external_function.sol#17-19)\n",
"confidence": "High"
},
{
"elements": [
{
"type": "function",
@ -135,13 +132,14 @@
"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",
"impact": "Optimization",
"confidence": "High",
"description": "funcNotCalled() should be declared external:\n\t- ContractWithFunctionNotCalled.funcNotCalled() (tests/external_function.sol#21-23)\n",
"confidence": "High"
},
{
"elements": [
{
"type": "function",
@ -202,13 +200,14 @@
"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",
"impact": "Optimization",
"confidence": "High",
"description": "funcNotCalled() should be declared external:\n\t- ContractWithFunctionNotCalled2.funcNotCalled() (tests/external_function.sol#32-39)\n",
"confidence": "High"
},
{
"elements": [
{
"type": "function",
@ -265,13 +264,14 @@
"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",
"impact": "Optimization",
"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",
"confidence": "High"
},
{
"elements": [
{
"type": "function",
@ -324,7 +324,12 @@
"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:
- 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:
- 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:
- 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:
- 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:
- 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:
- 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:
- balance == 10000000000000000000
- balance == 10000000000000000000 (tests/incorrect_equality.sol#54)
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:
- balance == 10000000000000000000
- balance == 10000000000000000000 (tests/incorrect_equality.sol#68)
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:
- 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:
- 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
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": {
"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": [
{
"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",
"name": "transfer",
@ -55,14 +76,40 @@
"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",
"impact": "Medium",
"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",
"confidence": "High"
},
{
"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",
"name": "approve",
@ -109,14 +156,40 @@
"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",
"impact": "Medium",
"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",
"confidence": "High"
},
{
"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",
"name": "transferFrom",
@ -163,14 +236,40 @@
"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",
"impact": "Medium",
"confidence": "High",
"description": "Token (tests/incorrect_erc20_interface.sol#3-10) has incorrect ERC20 function interface: totalSupply() (tests/incorrect_erc20_interface.sol#7)\n",
"confidence": "High"
},
{
"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",
"name": "totalSupply",
@ -217,14 +316,40 @@
"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",
"impact": "Medium",
"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",
"confidence": "High"
},
{
"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",
"name": "balanceOf",
@ -271,14 +396,40 @@
"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",
"impact": "Medium",
"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",
"confidence": "High"
},
{
"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",
"name": "allowance",
@ -325,7 +476,12 @@
"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: 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: 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: allowance(address,address) (tests/incorrect_erc20_interface.sol#9)

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:Token.approve(address,uint256) (tests/incorrect_erc20_interface.sol#5)
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:Token.totalSupply() (tests/incorrect_erc20_interface.sol#7)
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:Token.allowance(address,address) (tests/incorrect_erc20_interface.sol#9)
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