Create utils.output.Output class to handle json output

Remove add*_json function
pull/362/head
Josselin 5 years ago
parent d77fcfe6b6
commit f70e89bf12
  1. 2
      plugin_example/slither_my_plugin/detectors/example.py
  2. 4
      slither/__main__.py
  3. 72
      slither/detectors/abstract_detector.py
  4. 8
      slither/detectors/attributes/const_functions.py
  5. 4
      slither/detectors/attributes/constant_pragma.py
  6. 2
      slither/detectors/attributes/incorrect_solc.py
  7. 2
      slither/detectors/attributes/locked_ether.py
  8. 2
      slither/detectors/erc/incorrect_erc20_interface.py
  9. 4
      slither/detectors/erc/incorrect_erc721_interface.py
  10. 8
      slither/detectors/erc/unindexed_event_parameters.py
  11. 4
      slither/detectors/examples/backdoor.py
  12. 4
      slither/detectors/functions/arbitrary_send.py
  13. 7
      slither/detectors/functions/complex_function.py
  14. 4
      slither/detectors/functions/external_function.py
  15. 4
      slither/detectors/functions/suicidal.py
  16. 60
      slither/detectors/naming_convention/naming_convention.py
  17. 4
      slither/detectors/operations/block_timestamp.py
  18. 4
      slither/detectors/operations/low_level_calls.py
  19. 4
      slither/detectors/operations/unused_return_values.py
  20. 4
      slither/detectors/operations/void_constructor.py
  21. 12
      slither/detectors/reentrancy/reentrancy_benign.py
  22. 12
      slither/detectors/reentrancy/reentrancy_eth.py
  23. 10
      slither/detectors/reentrancy/reentrancy_read_before_write.py
  24. 4
      slither/detectors/shadowing/abstract.py
  25. 4
      slither/detectors/shadowing/builtin_symbols.py
  26. 4
      slither/detectors/shadowing/local.py
  27. 5
      slither/detectors/shadowing/state.py
  28. 11
      slither/detectors/source/rtlo.py
  29. 4
      slither/detectors/statements/assembly.py
  30. 4
      slither/detectors/statements/calls_in_loop.py
  31. 4
      slither/detectors/statements/controlled_delegatecall.py
  32. 4
      slither/detectors/statements/deprecated_calls.py
  33. 4
      slither/detectors/statements/incorrect_strict_equality.py
  34. 4
      slither/detectors/statements/too_many_digits.py
  35. 5
      slither/detectors/statements/tx_origin.py
  36. 2
      slither/detectors/variables/possible_const_state_variables.py
  37. 2
      slither/detectors/variables/uninitialized_local_variables.py
  38. 2
      slither/detectors/variables/uninitialized_state_variables.py
  39. 2
      slither/detectors/variables/uninitialized_storage_variables.py
  40. 2
      slither/detectors/variables/unused_state_variables.py
  41. 32
      slither/printers/abstract_printer.py
  42. 6
      slither/printers/call/call_graph.py
  43. 6
      slither/printers/functions/authorization.py
  44. 6
      slither/printers/functions/cfg.py
  45. 4
      slither/printers/guidance/echidna.py
  46. 4
      slither/printers/inheritance/inheritance.py
  47. 6
      slither/printers/inheritance/inheritance_graph.py
  48. 83
      slither/printers/summary/constructor_calls.py
  49. 15
      slither/printers/summary/contract.py
  50. 6
      slither/printers/summary/data_depenency.py
  51. 6
      slither/printers/summary/function.py
  52. 6
      slither/printers/summary/function_ids.py
  53. 9
      slither/printers/summary/human_summary.py
  54. 6
      slither/printers/summary/modifier_calls.py
  55. 6
      slither/printers/summary/require_calls.py
  56. 32
      slither/printers/summary/slithir.py
  57. 22
      slither/printers/summary/slithir_ssa.py
  58. 7
      slither/printers/summary/variable_order.py
  59. 2
      slither/slither.py
  60. 6
      slither/tools/erc_conformance/__main__.py
  61. 8
      slither/tools/erc_conformance/erc/erc20.py
  62. 57
      slither/tools/erc_conformance/erc/ercs.py
  63. 43
      slither/tools/kspec_coverage/analysis.py
  64. 12
      slither/tools/upgradeability/__main__.py
  65. 28
      slither/tools/upgradeability/check_initialization.py
  66. 8
      slither/tools/upgradeability/check_variable_initialization.py
  67. 32
      slither/tools/upgradeability/compare_function_ids.py
  68. 22
      slither/tools/upgradeability/compare_variables_order.py
  69. 26
      slither/tools/upgradeability/constant_checks.py
  70. 452
      slither/utils/json_utils.py
  71. 454
      slither/utils/output.py

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

@ -19,7 +19,7 @@ from slither.detectors.abstract_detector import (AbstractDetector,
from slither.printers import all_printers from slither.printers import all_printers
from slither.printers.abstract_printer import AbstractPrinter from slither.printers.abstract_printer import AbstractPrinter
from slither.slither import Slither from slither.slither import Slither
from slither.utils.json_utils import output_json from slither.utils.output import output_to_json
from slither.utils.output_capture import StandardOutputCapture from slither.utils.output_capture import StandardOutputCapture
from slither.utils.colors import red, yellow, set_colorization_enabled from slither.utils.colors import red, yellow, set_colorization_enabled
from slither.utils.command_line import (output_detectors, output_results_to_markdown, from slither.utils.command_line import (output_detectors, output_results_to_markdown,
@ -638,7 +638,7 @@ def main_impl(all_detector_classes, all_printer_classes):
'stderr': StandardOutputCapture.get_stderr_output() 'stderr': StandardOutputCapture.get_stderr_output()
} }
StandardOutputCapture.disable() StandardOutputCapture.disable()
output_json(None if outputting_json_stdout else args.json, output_error, json_results) output_to_json(None if outputting_json_stdout else args.json, output_error, json_results)
# Exit with the appropriate status code # Exit with the appropriate status code
if output_error: if output_error:

@ -4,7 +4,7 @@ import re
from slither.utils.colors import green, yellow, red from slither.utils.colors import green, yellow, red
from slither.formatters.exceptions import FormatImpossible from slither.formatters.exceptions import FormatImpossible
from slither.formatters.utils.patches import apply_patch, create_diff from slither.formatters.utils.patches import apply_patch, create_diff
from slither.utils import json_utils from slither.utils.output import Output
class IncorrectDetectorInitialization(Exception): class IncorrectDetectorInitialization(Exception):
@ -103,10 +103,12 @@ class AbstractDetector(metaclass=abc.ABCMeta):
@abc.abstractmethod @abc.abstractmethod
def _detect(self): def _detect(self):
"""TODO Documentation""" """TODO Documentation"""
return return []
def detect(self): def detect(self):
all_results = self._detect() all_results = self._detect()
# Keep only dictionaries
all_results = [r.data for r in all_results]
results = [] results = []
# only keep valid result, and remove dupplicate # only keep valid result, and remove dupplicate
[results.append(r) for r in all_results if self.slither.valid_result(r) and r not in results] [results.append(r) for r in all_results if self.slither.valid_result(r) and r not in results]
@ -170,65 +172,17 @@ class AbstractDetector(metaclass=abc.ABCMeta):
def color(self): def color(self):
return classification_colors[self.IMPACT] return classification_colors[self.IMPACT]
def generate_json_result(self, info, additional_fields=None): def generate_result(self, info, additional_fields=None):
d = json_utils.generate_json_result(info, output = Output(info,
additional_fields, additional_fields,
standard_format=self.STANDARD_JSON, standard_format=self.STANDARD_JSON,
markdown_root=self.slither.markdown_root) markdown_root=self.slither.markdown_root)
d['check'] = self.ARGUMENT output.data['check'] = self.ARGUMENT
d['impact'] = classification_txt[self.IMPACT] output.data['impact'] = classification_txt[self.IMPACT]
d['confidence'] = classification_txt[self.CONFIDENCE] output.data['confidence'] = classification_txt[self.CONFIDENCE]
return d return output
@staticmethod
def add_variable_to_json(e, d, additional_fields=None):
json_utils.add_variable_to_json(e, d, additional_fields=additional_fields)
@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)
@staticmethod @staticmethod
def _format(slither, result): def _format(slither, result):

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

@ -36,9 +36,9 @@ class ConstantPragma(AbstractDetector):
for p in pragma: for p in pragma:
info += ["\t- ", p, "\n"] info += ["\t- ", p, "\n"]
json = self.generate_json_result(info) res = self.generate_result(info)
results.append(json) results.append(res)
return results return results

@ -102,7 +102,7 @@ Use Solidity 0.4.25 or 0.5.3. Consider using the latest version of Solidity for
for (reason, p) in disallowed_pragmas: for (reason, p) in disallowed_pragmas:
info = ["Pragma version", p, f" {reason}\n"] info = ["Pragma version", p, f" {reason}\n"]
json = self.generate_json_result(info) json = self.generate_result(info)
results.append(json) results.append(json)

@ -80,7 +80,7 @@ Every ether sent to `Locked` will be lost.'''
info += [f"\t - ", function, "\n"] info += [f"\t - ", function, "\n"]
info += "\tBut does not have a function to withdraw the ether\n" info += "\tBut does not have a function to withdraw the ether\n"
json = self.generate_json_result(info) json = self.generate_result(info)
results.append(json) results.append(json)

@ -88,7 +88,7 @@ contract Token{
if functions: if functions:
for function in functions: for function in functions:
info = [c, " has incorrect ERC20 function interface:", function, "\n"] info = [c, " has incorrect ERC20 function interface:", function, "\n"]
json = self.generate_json_result(info) json = self.generate_result(info)
results.append(json) results.append(json)

@ -87,8 +87,8 @@ contract Token{
if functions: if functions:
for function in functions: for function in functions:
info = [c, " has incorrect ERC721 function interface:", function, "\n"] info = [c, " has incorrect ERC721 function interface:", function, "\n"]
json = self.generate_json_result(info) res = self.generate_result(info)
results.append(json) results.append(res)
return results return results

@ -76,12 +76,10 @@ In this case, Transfer and Approval events should have the 'indexed' keyword on
info = ["ERC20 event ", event, f"does not index parameter {parameter}\n"] info = ["ERC20 event ", event, f"does not index parameter {parameter}\n"]
# Add the events to the JSON (note: we do not add the params/vars as they have no source mapping). # Add the events to the JSON (note: we do not add the params/vars as they have no source mapping).
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_event_to_json(event, json, { res.add(event, {"parameter_name": parameter.name})
"parameter_name": parameter.name results.append(res)
})
results.append(json)
return results return results

@ -29,8 +29,8 @@ class Backdoor(AbstractDetector):
info = ['Backdoor function found in ', f, '\n'] info = ['Backdoor function found in ', f, '\n']
# Add the result in result # Add the result in result
json = self.generate_json_result(info) res = self.generate_result(info)
results.append(json) results.append(res)
return results return results

@ -114,8 +114,8 @@ Bob calls `setDestination` and `withdraw`. As a result he withdraws the contract
for node in nodes: for node in nodes:
info += ['\t- ', node, '\n'] info += ['\t- ', node, '\n']
json = self.generate_json_result(info) res = self.generate_result(info)
results.append(json) results.append(res)
return results return results

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

@ -187,9 +187,9 @@ class ExternalFunction(AbstractDetector):
for other_function_definition in all_function_definitions: for other_function_definition in all_function_definitions:
info += [f"\t- ", other_function_definition, "\n"] info += [f"\t- ", other_function_definition, "\n"]
json = self.generate_json_result(info) res = self.generate_result(info)
results.append(json) results.append(res)
return results return results

@ -75,8 +75,8 @@ Bob calls `kill` and destructs the contract.'''
info = [func, " allows anyone to destruct the contract\n"] info = [func, " allows anyone to destruct the contract\n"]
json = self.generate_json_result(info) res = self.generate_result(info)
results.append(json) results.append(res)
return results return results

@ -62,34 +62,34 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
if not self.is_cap_words(contract.name): if not self.is_cap_words(contract.name):
info = ["Contract ", contract, " is not in CapWords\n"] info = ["Contract ", contract, " is not in CapWords\n"]
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_contract_to_json(contract, json, { res.add(contract, {
"target": "contract", "target": "contract",
"convention": "CapWords" "convention": "CapWords"
}) })
results.append(json) results.append(res)
for struct in contract.structures_declared: for struct in contract.structures_declared:
if not self.is_cap_words(struct.name): if not self.is_cap_words(struct.name):
info = ["Struct ", struct, " is not in CapWords\n"] info = ["Struct ", struct, " is not in CapWords\n"]
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_struct_to_json(struct, json, { res.add(struct, {
"target": "structure", "target": "structure",
"convention": "CapWords" "convention": "CapWords"
}) })
results.append(json) results.append(res)
for event in contract.events_declared: for event in contract.events_declared:
if not self.is_cap_words(event.name): if not self.is_cap_words(event.name):
info = ["Event ", event, " is not in CapWords\n"] info = ["Event ", event, " is not in CapWords\n"]
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_event_to_json(event, json, { res.add(event, {
"target": "event", "target": "event",
"convention": "CapWords" "convention": "CapWords"
}) })
results.append(json) results.append(res)
for func in contract.functions_declared: for func in contract.functions_declared:
if func.is_constructor: if func.is_constructor:
@ -101,12 +101,12 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
continue continue
info = ["Function ", func, " is not in mixedCase\n"] info = ["Function ", func, " is not in mixedCase\n"]
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_function_to_json(func, json, { res.add(func, {
"target": "function", "target": "function",
"convention": "mixedCase" "convention": "mixedCase"
}) })
results.append(json) results.append(res)
for argument in func.parameters: for argument in func.parameters:
# Ignore parameter names that are not specified i.e. empty strings # Ignore parameter names that are not specified i.e. empty strings
@ -119,24 +119,24 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
if not correct_naming: if not correct_naming:
info = ["Parameter ", argument, " is not in mixedCase\n"] info = ["Parameter ", argument, " is not in mixedCase\n"]
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_variable_to_json(argument, json, { res.add(argument, {
"target": "parameter", "target": "parameter",
"convention": "mixedCase" "convention": "mixedCase"
}) })
results.append(json) results.append(res)
for var in contract.state_variables_declared: for var in contract.state_variables_declared:
if self.should_avoid_name(var.name): if self.should_avoid_name(var.name):
if not self.is_upper_case_with_underscores(var.name): if not self.is_upper_case_with_underscores(var.name):
info = ["Variable ", var," used l, O, I, which should not be used\n"] info = ["Variable ", var," used l, O, I, which should not be used\n"]
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_variable_to_json(var, json, { res.add(var, {
"target": "variable", "target": "variable",
"convention": "l_O_I_should_not_be_used" "convention": "l_O_I_should_not_be_used"
}) })
results.append(json) results.append(res)
if var.is_constant is True: if var.is_constant is True:
# For ERC20 compatibility # For ERC20 compatibility
@ -146,12 +146,12 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
if not self.is_upper_case_with_underscores(var.name): if not self.is_upper_case_with_underscores(var.name):
info = ["Constant ", var," is not in UPPER_CASE_WITH_UNDERSCORES\n"] info = ["Constant ", var," is not in UPPER_CASE_WITH_UNDERSCORES\n"]
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_variable_to_json(var, json, { res.add(var, {
"target": "variable_constant", "target": "variable_constant",
"convention": "UPPER_CASE_WITH_UNDERSCORES" "convention": "UPPER_CASE_WITH_UNDERSCORES"
}) })
results.append(json) results.append(res)
else: else:
if var.visibility == 'private': if var.visibility == 'private':
@ -161,34 +161,34 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
if not correct_naming: if not correct_naming:
info = ["Variable ", var, " is not in mixedCase\n"] info = ["Variable ", var, " is not in mixedCase\n"]
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_variable_to_json(var, json, { res.add(var, {
"target": "variable", "target": "variable",
"convention": "mixedCase" "convention": "mixedCase"
}) })
results.append(json) results.append(res)
for enum in contract.enums_declared: for enum in contract.enums_declared:
if not self.is_cap_words(enum.name): if not self.is_cap_words(enum.name):
info = ["Enum ", enum, " is not in CapWords\n"] info = ["Enum ", enum, " is not in CapWords\n"]
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_enum_to_json(enum, json, { res.add(enum, {
"target": "enum", "target": "enum",
"convention": "CapWords" "convention": "CapWords"
}) })
results.append(json) results.append(res)
for modifier in contract.modifiers_declared: for modifier in contract.modifiers_declared:
if not self.is_mixed_case(modifier.name): if not self.is_mixed_case(modifier.name):
info = ["Modifier ", modifier, " is not in mixedCase\n"] info = ["Modifier ", modifier, " is not in mixedCase\n"]
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_function_to_json(modifier, json, { res.add(modifier, {
"target": "modifier", "target": "modifier",
"convention": "mixedCase" "convention": "mixedCase"
}) })
results.append(json) results.append(res)
return results return results

@ -75,8 +75,8 @@ class Timestamp(AbstractDetector):
for node in nodes: for node in nodes:
info += ['\t- ', node, '\n'] info += ['\t- ', node, '\n']
json = self.generate_json_result(info) res = self.generate_result(info)
results.append(json) results.append(res)
return results return results

@ -53,8 +53,8 @@ class LowLevelCalls(AbstractDetector):
for node in nodes: for node in nodes:
info += ['\t- ', node, '\n'] info += ['\t- ', node, '\n']
json = self.generate_json_result(info) res = self.generate_result(info)
results.append(json) results.append(res)
return results return results

@ -76,9 +76,9 @@ contract MyConc{
for node in unused_return: for node in unused_return:
info = [f, f" ignores return value by ", node, "\n"] info = [f, f" ignores return value by ", node, "\n"]
json = self.generate_json_result(info) res = self.generate_result(info)
results.append(json) results.append(res)
return results return results

@ -39,7 +39,7 @@ By reading B's constructor definition, the reader might assume that `A()` initia
info = ["Void constructor called in ", cst, ":\n"] info = ["Void constructor called in ", cst, ":\n"]
info += ["\t- ", node, "\n"] info += ["\t- ", node, "\n"]
json = self.generate_json_result(info) res = self.generate_result(info)
results.append(json) results.append(res)
return results return results

@ -95,14 +95,14 @@ Only report reentrancy that acts as a double call (see `reentrancy-eth`, `reentr
# Create our JSON result # Create our JSON result
json = self.generate_json_result(info) res = self.generate_result(info)
# Add the function with the re-entrancy first # Add the function with the re-entrancy first
self.add_function_to_json(func, json) res.add(func)
# Add all underlying calls in the function which are potentially problematic. # Add all underlying calls in the function which are potentially problematic.
for call_info in calls: for call_info in calls:
self.add_node_to_json(call_info, json, { res.add(call_info, {
"underlying_type": "external_calls" "underlying_type": "external_calls"
}) })
@ -111,18 +111,18 @@ Only report reentrancy that acts as a double call (see `reentrancy-eth`, `reentr
# If the calls are not the same ones that send eth, add the eth sending nodes. # If the calls are not the same ones that send eth, add the eth sending nodes.
if calls != send_eth: if calls != send_eth:
for call_info in send_eth: for call_info in send_eth:
self.add_node_to_json(call_info, json, { res.add(call_info, {
"underlying_type": "external_calls_sending_eth" "underlying_type": "external_calls_sending_eth"
}) })
# Add all variables written via nodes which write them. # Add all variables written via nodes which write them.
for (v, node) in varsWritten: for (v, node) in varsWritten:
self.add_node_to_json(node, json, { res.add(node, {
"underlying_type": "variables_written", "underlying_type": "variables_written",
"variable_name": v.name "variable_name": v.name
}) })
# Append our result # Append our result
results.append(json) results.append(res)
return results return results

@ -95,14 +95,14 @@ Bob uses the re-entrancy bug to call `withdrawBalance` two times, and withdraw m
info += ['\t- ', v, ' in ', node, '\n'] info += ['\t- ', v, ' in ', node, '\n']
# Create our JSON result # Create our JSON result
json = self.generate_json_result(info) res = self.generate_result(info)
# Add the function with the re-entrancy first # Add the function with the re-entrancy first
self.add_function_to_json(func, json) res.add(func)
# Add all underlying calls in the function which are potentially problematic. # Add all underlying calls in the function which are potentially problematic.
for call_info in calls: for call_info in calls:
self.add_node_to_json(call_info, json, { res.add(call_info, {
"underlying_type": "external_calls" "underlying_type": "external_calls"
}) })
@ -111,18 +111,18 @@ Bob uses the re-entrancy bug to call `withdrawBalance` two times, and withdraw m
# If the calls are not the same ones that send eth, add the eth sending nodes. # If the calls are not the same ones that send eth, add the eth sending nodes.
if calls != send_eth: if calls != send_eth:
for call_info in send_eth: for call_info in send_eth:
self.add_node_to_json(call_info, json, { res.add(call_info, {
"underlying_type": "external_calls_sending_eth" "underlying_type": "external_calls_sending_eth"
}) })
# Add all variables written via nodes which write them. # Add all variables written via nodes which write them.
for (v, node) in varsWritten: for (v, node) in varsWritten:
self.add_node_to_json(node, json, { res.add(node, {
"underlying_type": "variables_written", "underlying_type": "variables_written",
"variable_name": v.name "variable_name": v.name
}) })
# Append our result # Append our result
results.append(json) results.append(res)
return results return results

@ -88,25 +88,25 @@ Do not report reentrancies that involve ethers (see `reentrancy-eth`)'''
info += ['\t- ', v, ' in ', node, '\n'] info += ['\t- ', v, ' in ', node, '\n']
# Create our JSON result # Create our JSON result
json = self.generate_json_result(info) res = self.generate_result(info)
# Add the function with the re-entrancy first # Add the function with the re-entrancy first
self.add_function_to_json(func, json) res.add(func)
# Add all underlying calls in the function which are potentially problematic. # Add all underlying calls in the function which are potentially problematic.
for call_info in calls: for call_info in calls:
self.add_node_to_json(call_info, json, { res.add(call_info, {
"underlying_type": "external_calls" "underlying_type": "external_calls"
}) })
# Add all variables written via nodes which write them. # Add all variables written via nodes which write them.
for (v, node) in varsWritten: for (v, node) in varsWritten:
self.add_node_to_json(node, json, { res.add(node, {
"underlying_type": "variables_written", "underlying_type": "variables_written",
"variable_name": v.name "variable_name": v.name
}) })
# Append our result # Append our result
results.append(json) results.append(res)
return results return results

@ -69,8 +69,8 @@ contract DerivedContract is BaseContract{
for var in variables: for var in variables:
info += ["\t- ", var, "\n"] info += ["\t- ", var, "\n"]
json = self.generate_json_result(info) res = self.generate_result(info)
results.append(json) results.append(res)
return results return results

@ -127,7 +127,7 @@ contract Bug {
info = [shadow_object, f' ({shadow_type}) shadows built-in symbol"\n'] info = [shadow_object, f' ({shadow_type}) shadows built-in symbol"\n']
json = self.generate_json_result(info) res = self.generate_result(info)
results.append(json) results.append(res)
return results return results

@ -109,8 +109,8 @@ contract Bug {
info += ["\t- ", overshadowed_entry[1], f" ({overshadowed_entry[0]})\n"] info += ["\t- ", overshadowed_entry[1], f" ({overshadowed_entry[0]})\n"]
# Generate relevant JSON data for this shadowing definition. # Generate relevant JSON data for this shadowing definition.
json = self.generate_json_result(info) res = self.generate_result(info)
results.append(json) results.append(res)
return results return results

@ -80,9 +80,8 @@ contract DerivedContract is BaseContract{
for var in variables: for var in variables:
info += ["\t- ", var, "\n"] info += ["\t- ", var, "\n"]
json = self.generate_json_result(info) res = self.generate_result(info)
results.append(res)
results.append(json)
return results return results

@ -73,12 +73,11 @@ contract Token
# We have a patch, so pattern.find will return at least one result # We have a patch, so pattern.find will return at least one result
info += f"\t- {pattern.findall(source_encoded)[0]}\n" info += f"\t- {pattern.findall(source_encoded)[0]}\n"
json = self.generate_json_result(info) res = self.generate_result(info)
self.add_other_to_json("rtlo-character", res.add_other("rtlo-character",
(filename, idx, len(self.RTLO_CHARACTER_ENCODED)), (filename, idx, len(self.RTLO_CHARACTER_ENCODED)),
json, self.slither)
self.slither) results.append(res)
results.append(json)
# Advance the start index for the next iteration # Advance the start index for the next iteration
start_index = result_index + 1 start_index = result_index + 1

@ -56,7 +56,7 @@ class Assembly(AbstractDetector):
for node in nodes: for node in nodes:
info += ["\t- ", node, "\n"] info += ["\t- ", node, "\n"]
json = self.generate_json_result(info) res = self.generate_result(info)
results.append(json) results.append(res)
return results return results

@ -88,7 +88,7 @@ If one of the destinations has a fallback function which reverts, `bad` will alw
func = node.function func = node.function
info = [func, " has external calls inside a loop: ", node, "\n"] info = [func, " has external calls inside a loop: ", node, "\n"]
json = self.generate_json_result(info) res = self.generate_result(info)
results.append(json) results.append(res)
return results return results

@ -51,7 +51,7 @@ Bob calls `delegate` and delegates the execution to its malicious contract. As a
for node in nodes: for node in nodes:
node_info = func_info + ['\t- ', node,'\n'] node_info = func_info + ['\t- ', node,'\n']
json = self.generate_json_result(node_info) res = self.generate_result(node_info)
results.append(json) results.append(res)
return results return results

@ -157,7 +157,7 @@ contract ContractWithDeprecatedReferences {
for (dep_id, original_desc, recommended_disc) in deprecated_entries: for (dep_id, original_desc, recommended_disc) in deprecated_entries:
info += [f"\t- Usage of \"{original_desc}\" should be replaced with \"{recommended_disc}\"\n"] info += [f"\t- Usage of \"{original_desc}\" should be replaced with \"{recommended_disc}\"\n"]
json = self.generate_json_result(info) res = self.generate_result(info)
results.append(json) results.append(res)
return results return results

@ -120,7 +120,7 @@ contract Crowdsale{
for node in nodes: for node in nodes:
node_info = func_info + [f"\t- ", node, "\n"] node_info = func_info + [f"\t- ", node, "\n"]
json = self.generate_json_result(node_info) res = self.generate_result(node_info)
results.append(json) results.append(res)
return results return results

@ -69,7 +69,7 @@ Use:
node_info = func_info + ['\n\t- ', node,'\n'] node_info = func_info + ['\n\t- ', node,'\n']
# Add the result in result # Add the result in result
json = self.generate_json_result(node_info) res = self.generate_result(node_info)
results.append(json) results.append(res)
return results return results

@ -69,8 +69,7 @@ Bob is the owner of `TxOrigin`. Bob calls Eve's contract. Eve's contract calls `
for node in nodes: for node in nodes:
info = [func, " uses tx.origin for authorization: ", node, "\n"] info = [func, " uses tx.origin for authorization: ", node, "\n"]
json = self.generate_json_result(info) res = self.generate_result(info)
results.append(res)
results.append(json)
return results return results

@ -89,7 +89,7 @@ class ConstCandidateStateVars(AbstractDetector):
# Create a result for each finding # Create a result for each finding
for v in constable_variables: for v in constable_variables:
info = [v, " should be constant\n"] info = [v, " should be constant\n"]
json = self.generate_json_result(info) json = self.generate_result(info)
results.append(json) results.append(json)
return results return results

@ -99,7 +99,7 @@ Bob calls `transfer`. As a result, the ethers are sent to the address 0x0 and ar
for(function, uninitialized_local_variable) in all_results: for(function, uninitialized_local_variable) in all_results:
info = [uninitialized_local_variable, " is a local variable never initialiazed\n"] info = [uninitialized_local_variable, " is a local variable never initialiazed\n"]
json = self.generate_json_result(info) json = self.generate_result(info)
results.append(json) results.append(json)
return results return results

@ -93,7 +93,7 @@ Initialize all the variables. If a variable is meant to be initialized to zero,
for f in functions: for f in functions:
info += ["\t- ", f, "\n"] info += ["\t- ", f, "\n"]
json = self.generate_json_result(info) json = self.generate_result(info)
results.append(json) results.append(json)
return results return results

@ -102,7 +102,7 @@ Bob calls `func`. As a result, `owner` is override to 0.
for(function, uninitialized_storage_variable) in self.results: for(function, uninitialized_storage_variable) in self.results:
info = [uninitialized_storage_variable, " is a storage variable never initialiazed\n"] info = [uninitialized_storage_variable, " is a storage variable never initialiazed\n"]
json = self.generate_json_result(info) json = self.generate_result(info)
results.append(json) results.append(json)
return results return results

@ -61,7 +61,7 @@ class UnusedStateVars(AbstractDetector):
if unusedVars: if unusedVars:
for var in unusedVars: for var in unusedVars:
info = [var, " is never used in ", c, "\n"] info = [var, " is never used in ", c, "\n"]
json = self.generate_json_result(info) json = self.generate_result(info)
results.append(json) results.append(json)
return results return results

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -165,7 +165,7 @@ class Slither(SlitherSolc):
:return: List of registered printers outputs. :return: List of registered printers outputs.
""" """
return [p.output(self.filename) for p in self._printers] return [p.output(self.filename).data for p in self._printers]
def _check_common_things(self, thing_name, cls, base_cls, instances_list): def _check_common_things(self, thing_name, cls, base_cls, instances_list):

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

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

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

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

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

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

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

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

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

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

@ -1,452 +0,0 @@
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_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')
def generate_json_result(info, additional_fields=None, markdown_root='', standard_format=False):
if additional_fields is None:
additional_fields = {}
d = OrderedDict()
d['elements'] = []
d['description'] = ''.join(_convert_to_description(d) for d in info)
d['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:
if isinstance(add, Variable):
add_variable_to_json(add, d)
elif isinstance(add, Contract):
add_contract_to_json(add, d)
elif isinstance(add, Function):
add_function_to_json(add, d)
elif isinstance(add, Enum):
add_enum_to_json(add, d)
elif isinstance(add, Event):
add_event_to_json(add, d)
elif isinstance(add, Structure):
add_struct_to_json(add, d)
elif isinstance(add, Pragma):
add_pragma_to_json(add, d)
elif isinstance(add, Node):
add_node_to_json(add, d)
else:
raise SlitherError(f'Impossible to add {type(add)} to the json')
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)
Loading…
Cancel
Save