Merge pull request #356 from crytic/dev-printer-to-json

Create Json output for printers
pull/364/head
Feist Josselin 5 years ago committed by GitHub
commit 5108a9eb32
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 61
      slither/__main__.py
  2. 35
      slither/printers/abstract_printer.py
  3. 29
      slither/printers/call/call_graph.py
  4. 15
      slither/printers/functions/authorization.py
  5. 17
      slither/printers/functions/cfg.py
  6. 4
      slither/printers/guidance/echidna.py
  7. 22
      slither/printers/inheritance/inheritance.py
  8. 17
      slither/printers/inheritance/inheritance_graph.py
  9. 6
      slither/printers/summary/constructor_calls.py
  10. 46
      slither/printers/summary/contract.py
  11. 12
      slither/printers/summary/data_depenency.py
  12. 13
      slither/printers/summary/function.py
  13. 10
      slither/printers/summary/function_ids.py
  14. 42
      slither/printers/summary/human_summary.py
  15. 10
      slither/printers/summary/modifier_calls.py
  16. 10
      slither/printers/summary/require_calls.py
  17. 1
      slither/printers/summary/slithir.py
  18. 2
      slither/printers/summary/slithir_ssa.py
  19. 12
      slither/printers/summary/variable_order.py
  20. 4
      slither/utils/command_line.py
  21. 44
      slither/utils/json_utils.py

@ -25,7 +25,7 @@ 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,
output_detectors_json, output_printers, output_printers_json, output_detectors_json, output_printers, output_printers_json,
output_to_markdown, output_wiki, defaults_flag_in_config, output_to_markdown, output_wiki, defaults_flag_in_config,
read_config_file, JSON_OUTPUT_TYPES) read_config_file, JSON_OUTPUT_TYPES, DEFAULT_JSON_OUTPUT_TYPES)
from crytic_compile import compile_all, is_supported from crytic_compile import compile_all, is_supported
from slither.exceptions import SlitherException from slither.exceptions import SlitherException
@ -59,14 +59,16 @@ def process_single(target, args, detector_classes, printer_classes):
def process_all(target, args, detector_classes, printer_classes): def process_all(target, args, detector_classes, printer_classes):
compilations = compile_all(target, **vars(args)) compilations = compile_all(target, **vars(args))
slither_instances = [] slither_instances = []
results = [] results_detectors = []
results_printers = []
analyzed_contracts_count = 0 analyzed_contracts_count = 0
for compilation in compilations: for compilation in compilations:
(slither, current_results, current_analyzed_count) = process_single(compilation, args, detector_classes, printer_classes) (slither, current_results_detectors, current_results_printers, current_analyzed_count) = process_single(compilation, args, detector_classes, printer_classes)
results.extend(current_results) results_detectors.extend(current_results_detectors)
results_printers.extend(current_results_printers)
slither_instances.append(slither) slither_instances.append(slither)
analyzed_contracts_count += current_analyzed_count analyzed_contracts_count += current_analyzed_count
return slither_instances, results, analyzed_contracts_count return slither_instances, results_detectors, results_printers, analyzed_contracts_count
def _process(slither, detector_classes, printer_classes): def _process(slither, detector_classes, printer_classes):
@ -78,18 +80,21 @@ def _process(slither, detector_classes, printer_classes):
analyzed_contracts_count = len(slither.contracts) analyzed_contracts_count = len(slither.contracts)
results = [] results_detectors = []
results_printers = []
if not printer_classes: if not printer_classes:
detector_results = slither.run_detectors() detector_results = slither.run_detectors()
detector_results = [x for x in detector_results if x] # remove empty results detector_results = [x for x in detector_results if x] # remove empty results
detector_results = [item for sublist in detector_results for item in sublist] # flatten detector_results = [item for sublist in detector_results for item in sublist] # flatten
results_detectors.extend(detector_results)
results.extend(detector_results) else:
printer_results = slither.run_printers()
slither.run_printers() # Currently printers does not return results printer_results = [x for x in printer_results if x] # remove empty results
results_printers.extend(printer_results)
return slither, results, analyzed_contracts_count return slither, results_detectors, results_printers, analyzed_contracts_count
def process_from_asts(filenames, args, detector_classes, printer_classes): def process_from_asts(filenames, args, detector_classes, printer_classes):
@ -320,9 +325,9 @@ def parse_args(detector_classes, printer_classes):
default=defaults_flag_in_config['json']) default=defaults_flag_in_config['json'])
group_misc.add_argument('--json-types', group_misc.add_argument('--json-types',
help='Comma-separated list of result types to output to JSON, defaults to all, ' help=f'Comma-separated list of result types to output to JSON, defaults to ' +\
'available types: {}'.format( f'{",".join(output_type for output_type in DEFAULT_JSON_OUTPUT_TYPES)}. ' +\
', '.join(output_type for output_type in JSON_OUTPUT_TYPES)), f'Available types: {",".join(output_type for output_type in JSON_OUTPUT_TYPES)}',
action='store', action='store',
default=defaults_flag_in_config['json-types']) default=defaults_flag_in_config['json-types'])
@ -538,6 +543,8 @@ def main_impl(all_detector_classes, all_printer_classes):
crytic_compile_error.propagate = False crytic_compile_error.propagate = False
crytic_compile_error.setLevel(logging.INFO) crytic_compile_error.setLevel(logging.INFO)
results_detectors = []
results_printers = []
try: try:
filename = args.filename filename = args.filename
@ -548,21 +555,22 @@ def main_impl(all_detector_classes, all_printer_classes):
if not filenames: if not filenames:
filenames = globbed_filenames filenames = globbed_filenames
number_contracts = 0 number_contracts = 0
results = []
slither_instances = [] slither_instances = []
if args.splitted: if args.splitted:
(slither_instance, results, number_contracts) = process_from_asts(filenames, args, detector_classes, printer_classes) (slither_instance, results_detectors, results_printers, number_contracts) = process_from_asts(filenames, args, detector_classes, printer_classes)
slither_instances.append(slither_instance) slither_instances.append(slither_instance)
else: else:
for filename in filenames: for filename in filenames:
(slither_instance, results_tmp, number_contracts_tmp) = process_single(filename, args, detector_classes, printer_classes) (slither_instance, results_detectors_tmp, results_printers_tmp, number_contracts_tmp) = process_single(filename, args, detector_classes, printer_classes)
number_contracts += number_contracts_tmp number_contracts += number_contracts_tmp
results += results_tmp results_detectors += results_detectors_tmp
results_printers += results_printers_tmp
slither_instances.append(slither_instance) slither_instances.append(slither_instance)
# Rely on CryticCompile to discern the underlying type of compilations. # Rely on CryticCompile to discern the underlying type of compilations.
else: else:
(slither_instances, results, number_contracts) = process_all(filename, args, detector_classes, printer_classes) (slither_instances, results_detectors, results_printers, number_contracts) = process_all(filename, args, detector_classes, printer_classes)
# Determine if we are outputting JSON # Determine if we are outputting JSON
if outputting_json: if outputting_json:
@ -574,8 +582,12 @@ def main_impl(all_detector_classes, all_printer_classes):
json_results['compilations'] = compilation_results json_results['compilations'] = compilation_results
# Add our detector results to JSON if desired. # Add our detector results to JSON if desired.
if results and 'detectors' in args.json_types: if results_detectors and 'detectors' in args.json_types:
json_results['detectors'] = results json_results['detectors'] = results_detectors
# Add our printer results to JSON if desired.
if results_printers and 'printers' in args.json_types:
json_results['printers'] = results_printers
# Add our detector types to JSON # Add our detector types to JSON
if 'list-detectors' in args.json_types: if 'list-detectors' in args.json_types:
@ -589,15 +601,16 @@ def main_impl(all_detector_classes, all_printer_classes):
# Output our results to markdown if we wish to compile a checklist. # Output our results to markdown if we wish to compile a checklist.
if args.checklist: if args.checklist:
output_results_to_markdown(results) output_results_to_markdown(results_detectors)
# Dont print the number of result for printers # Dont print the number of result for printers
if number_contracts == 0: if number_contracts == 0:
logger.warn(red('No contract was analyzed')) logger.warning(red('No contract was analyzed'))
if printer_classes: if printer_classes:
logger.info('%s analyzed (%d contracts)', filename, number_contracts) logger.info('%s analyzed (%d contracts)', filename, number_contracts)
else: else:
logger.info('%s analyzed (%d contracts with %d detectors), %d result(s) found', filename, number_contracts, len(detector_classes), len(results)) logger.info('%s analyzed (%d contracts with %d detectors), %d result(s) found', filename,
number_contracts, len(detector_classes), len(results_detectors))
if args.ignore_return_value: if args.ignore_return_value:
return return
@ -626,7 +639,7 @@ def main_impl(all_detector_classes, all_printer_classes):
if output_error: if output_error:
sys.exit(-1) sys.exit(-1)
else: else:
exit(results) exit(results_detectors)
if __name__ == '__main__': if __name__ == '__main__':

@ -1,5 +1,7 @@
import abc import abc
from slither.utils import json_utils
class IncorrectPrinterInitialization(Exception): class IncorrectPrinterInitialization(Exception):
pass pass
@ -30,6 +32,39 @@ class AbstractPrinter(metaclass=abc.ABCMeta):
if self.logger: if self.logger:
self.logger.info(info) self.logger.info(info)
def generate_json_result(self, info, additional_fields=None):
if additional_fields is None:
additional_fields = {}
d = json_utils.generate_json_result(info, additional_fields)
d['printer'] = self.ARGUMENT
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"""

@ -9,13 +9,10 @@ from collections import defaultdict
from slither.printers.abstract_printer import AbstractPrinter from slither.printers.abstract_printer import AbstractPrinter
from slither.core.declarations.solidity_variables import SolidityFunction from slither.core.declarations.solidity_variables import SolidityFunction
from slither.core.declarations.function import Function from slither.core.declarations.function import Function
from slither.core.declarations.contract import Contract
from slither.core.expressions.member_access import MemberAccess
from slither.core.expressions.identifier import Identifier
from slither.core.variables.variable import Variable from slither.core.variables.variable import Variable
from slither.core.solidity_types.user_defined_type import UserDefinedType
# return unique id for contract to use as subgraph name
def _contract_subgraph(contract): def _contract_subgraph(contract):
return f'cluster_{contract.id}_{contract.name}' return f'cluster_{contract.id}_{contract.name}'
@ -163,13 +160,25 @@ class PrinterCallGraph(AbstractPrinter):
if filename == ".dot": if filename == ".dot":
filename = "all_contracts.dot" filename = "all_contracts.dot"
info = ''
results = []
with open(filename, 'w', encoding='utf8') as f: with open(filename, 'w', encoding='utf8') as f:
self.info(f'Call Graph: {filename}') info += f'Call Graph: {filename}'
f.write('\n'.join(['strict digraph {'] + [self._process_functions(self.slither.functions)] + ['}'])) content = '\n'.join(['strict digraph {'] + [self._process_functions(self.slither.functions)] + ['}'])
f.write(content)
results.append((filename, content))
for derived_contract in self.slither.contracts_derived: for derived_contract in self.slither.contracts_derived:
with open(f'{derived_contract.name}.dot', 'w', encoding='utf8') as f: with open(f'{derived_contract.name}.dot', 'w', encoding='utf8') as f:
self.info(f'Call Graph: {derived_contract.name}.dot') info += f'Call Graph: {derived_contract.name}.dot'
f.write('\n'.join(['strict digraph {'] + [self._process_functions(derived_contract.functions)] + ['}'])) content = '\n'.join(['strict digraph {'] + [self._process_functions(derived_contract.functions)] + ['}'])
f.write(content)
results.append((filename, content))
self.info(info)
json = self.generate_json_result(info)
for filename, content in results:
self.add_file_to_json(filename, content, json)
return json

@ -6,6 +6,7 @@ from prettytable import PrettyTable
from slither.printers.abstract_printer import AbstractPrinter from slither.printers.abstract_printer import AbstractPrinter
from slither.core.declarations.function import Function from slither.core.declarations.function import Function
class PrinterWrittenVariablesAndAuthorization(AbstractPrinter): class PrinterWrittenVariablesAndAuthorization(AbstractPrinter):
ARGUMENT = 'vars-and-auth' ARGUMENT = 'vars-and-auth'
@ -33,12 +34,22 @@ class PrinterWrittenVariablesAndAuthorization(AbstractPrinter):
_filename(string) _filename(string)
""" """
txt = ''
all_tables = []
for contract in self.contracts: for contract in self.contracts:
txt = "\nContract %s\n"%contract.name txt += "\nContract %s\n"%contract.name
table = PrettyTable(["Function", "State variables written", "Conditions on msg.sender"]) table = PrettyTable(["Function", "State variables written", "Conditions on msg.sender"])
for function in contract.functions: for function in contract.functions:
state_variables_written = [v.name for v in function.all_state_variables_written()] state_variables_written = [v.name for v in function.all_state_variables_written()]
msg_sender_condition = self.get_msg_sender_checks(function) msg_sender_condition = self.get_msg_sender_checks(function)
table.add_row([function.name, str(state_variables_written), str(msg_sender_condition)]) table.add_row([function.name, str(state_variables_written), str(msg_sender_condition)])
self.info(txt + str(table)) all_tables.append((contract.name, table))
txt += str(table) + '\n'
self.info(txt)
json = self.generate_json_result(txt)
for name, table in all_tables:
self.add_pretty_table_to_json(table, name, json)
return json

@ -2,7 +2,7 @@
""" """
from slither.printers.abstract_printer import AbstractPrinter from slither.printers.abstract_printer import AbstractPrinter
from slither.core.declarations.function import Function
class CFG(AbstractPrinter): class CFG(AbstractPrinter):
@ -18,9 +18,20 @@ class CFG(AbstractPrinter):
_filename(string) _filename(string)
""" """
info = ''
all_files = []
for contract in self.contracts: for contract in self.contracts:
for function in contract.functions + contract.modifiers: for function in contract.functions + contract.modifiers:
filename = "{}-{}-{}.dot".format(original_filename, contract.name, function.full_name) filename = "{}-{}-{}.dot".format(original_filename, contract.name, function.full_name)
self.info('Export {}'.format(filename)) info += 'Export {}'.format(filename)
function.slithir_cfg_to_dot(filename) content = function.slithir_cfg_to_dot(filename)
with open(filename, 'w', encoding='utf8') as f:
f.write(content)
all_files.append((filename, content))
self.info(info)
json = self.generate_json_result(info)
for filename, content in all_files:
self.add_file_to_json(filename, content, json)
return json

@ -138,4 +138,6 @@ class Echidna(AbstractPrinter):
'constants_used': cst_used, 'constants_used': cst_used,
'constants_used_in_binary': cst_used_in_binary} 'constants_used_in_binary': cst_used_in_binary}
print(json.dumps(d, indent=4)) self.info(json.dumps(d, indent=4))
return d

@ -35,24 +35,46 @@ class PrinterInheritance(AbstractPrinter):
info += blue('Child_Contract -> ') + green('Immediate_Base_Contracts') info += blue('Child_Contract -> ') + green('Immediate_Base_Contracts')
info += green(' [Not_Immediate_Base_Contracts]') info += green(' [Not_Immediate_Base_Contracts]')
result = {}
result['child_to_base'] = {}
for child in self.contracts: for child in self.contracts:
info += blue(f'\n+ {child.name}') info += blue(f'\n+ {child.name}')
result['child_to_base'][child.name] = {'immediate': [],
'not_immediate': []}
if child.inheritance: if child.inheritance:
immediate = child.immediate_inheritance immediate = child.immediate_inheritance
not_immediate = [i for i in child.inheritance if i not in immediate] not_immediate = [i for i in child.inheritance if i not in immediate]
info += ' -> ' + green(", ".join(map(str, immediate))) info += ' -> ' + green(", ".join(map(str, immediate)))
result['child_to_base'][child.name]['immediate'] = list(map(str, immediate))
if not_immediate: if not_immediate:
info += ", ["+ green(", ".join(map(str, not_immediate))) + "]" info += ", ["+ green(", ".join(map(str, not_immediate))) + "]"
result['child_to_base'][child.name]['not_immediate'] = list(map(str, not_immediate))
info += green('\n\nBase_Contract -> ') + blue('Immediate_Child_Contracts') info += green('\n\nBase_Contract -> ') + blue('Immediate_Child_Contracts')
info += blue(' [Not_Immediate_Child_Contracts]') info += blue(' [Not_Immediate_Child_Contracts]')
result['base_to_child'] = {}
for base in self.contracts: for base in self.contracts:
info += green(f'\n+ {base.name}') info += green(f'\n+ {base.name}')
children = list(self._get_child_contracts(base)) children = list(self._get_child_contracts(base))
result['base_to_child'][base.name] = {'immediate': [],
'not_immediate': []}
if children: if children:
immediate = [child for child in children if base in child.immediate_inheritance] immediate = [child for child in children if base in child.immediate_inheritance]
not_immediate = [child for child in children if not child in immediate] not_immediate = [child for child in children if not child in immediate]
info += ' -> ' + blue(", ".join(map(str, immediate))) info += ' -> ' + blue(", ".join(map(str, immediate)))
result['base_to_child'][base.name]['immediate'] = list(map(str, immediate))
if not_immediate: if not_immediate:
info += ', [' + blue(", ".join(map(str, not_immediate))) + ']' info += ', [' + blue(", ".join(map(str, not_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)
return json

@ -156,14 +156,23 @@ class PrinterInheritanceGraph(AbstractPrinter):
Args: Args:
filename(string) filename(string)
""" """
if filename == '': if filename == '':
filename = 'contracts.dot' filename = 'contracts.dot'
if not filename.endswith('.dot'): if not filename.endswith('.dot'):
filename += ".dot" filename += ".dot"
info = 'Inheritance Graph: ' + filename info = 'Inheritance Graph: ' + filename
self.info(info) self.info(info)
with open(filename, 'w', encoding='utf8') as f:
f.write('digraph "" {\n') content = 'digraph "" {\n'
for c in self.contracts: for c in self.contracts:
f.write(self._summary(c)) content += self._summary(c) + '\n'
f.write('}') content += '}'
with open(filename, 'w', encoding='utf8') as f:
f.write(content)
json = self.generate_json_result(info)
self.add_file_to_json(filename, content, json)
return json

@ -35,13 +35,13 @@ class ConstructorPrinter(AbstractPrinter):
stack_definition.append(self._get_soruce_code(cst)) stack_definition.append(self._get_soruce_code(cst))
if len(stack_name)>0: if len(stack_name)>0:
print(" ",stack_name[len(stack_name)-1], sep=' ', end='', flush=True) print(" ",stack_name[len(stack_name)-1], sep=' ', end='', flush=True)
count = len(stack_name)-2; count = len(stack_name)-2
while count>=0: while count>=0:
print("-->",stack_name[count], sep=' ', end='', flush=True) print("-->",stack_name[count], sep=' ', end='', flush=True)
count= count-1; count= count-1
print("\n Constructor Definitions:") print("\n Constructor Definitions:")
count = len(stack_definition)-1 count = len(stack_definition)-1
while count>=0: while count>=0:
print("\n Contract name:", stack_name[count]) print("\n Contract name:", stack_name[count])
print ("\n", stack_definition[count]) print ("\n", stack_definition[count])
count = count-1; count = count-1

@ -5,8 +5,8 @@ import collections
from slither.printers.abstract_printer import AbstractPrinter from slither.printers.abstract_printer import AbstractPrinter
from slither.utils.colors import blue, green, magenta from slither.utils.colors import blue, green, magenta
class ContractSummary(AbstractPrinter):
class ContractSummary(AbstractPrinter):
ARGUMENT = 'contract-summary' ARGUMENT = 'contract-summary'
HELP = 'Print a summary of the contracts' HELP = 'Print a summary of the contracts'
@ -20,28 +20,42 @@ class ContractSummary(AbstractPrinter):
""" """
txt = "" txt = ""
all_contracts = []
for c in self.contracts: for c in self.contracts:
(name, _inheritance, _var, func_summaries, _modif_summaries) = c.get_summary(False) txt += blue("\n+ Contract %s\n" % c.name)
txt += blue("\n+ Contract %s\n"%name) additional_fields = {"elements": []}
# (c_name, f_name, visi, _, _, _, _, _) in func_summaries
public = [(elem[0], (elem[1], elem[2]) ) for elem in func_summaries]
# Order the function with
# contract_declarer -> list_functions
public = [(f.contract_declarer.name, f) for f in c.functions if (not f.is_shadowed)]
collect = collections.defaultdict(list) collect = collections.defaultdict(list)
for a,b in public: for a, b in public:
collect[a].append(b) collect[a].append(b)
public = list(collect.items()) public = list(collect.items())
for contract, functions in public: for contract, functions in public:
txt += blue(" - From {}\n".format(contract)) txt += blue(" - From {}\n".format(contract))
functions = sorted(functions)
for (function, visi) in functions: functions = sorted(functions, key=lambda f: f.full_name)
if visi in ['external', 'public']:
txt += green(" - {} ({})\n".format(function, visi)) for function in functions:
for (function, visi) in functions: if function.visibility in ['external', 'public']:
if visi in ['internal', 'private']: txt += green(" - {} ({})\n".format(function, function.visibility))
txt += magenta(" - {} ({})\n".format(function, visi)) if function.visibility in ['internal', 'private']:
for (function, visi) in functions: txt += magenta(" - {} ({})\n".format(function, function.visibility))
if visi not in ['external', 'public', 'internal', 'private']: if function.visibility not in ['external', 'public', 'internal', 'private']:
txt += " - {}  ({})\n".format(function, visi) txt += " - {}  ({})\n".format(function, function.visibility)
self.add_function_to_json(function, additional_fields, additional_fields={"visibility":
function.visibility})
all_contracts.append((c, additional_fields))
self.info(txt) self.info(txt)
json = self.generate_json_result(txt)
for contract, additional_fields in all_contracts:
self.add_contract_to_json(contract, json, additional_fields=additional_fields)
return json

@ -25,6 +25,9 @@ class DataDependency(AbstractPrinter):
_filename(string) _filename(string)
""" """
all_tables = []
all_txt = ''
txt = '' txt = ''
for c in self.contracts: for c in self.contracts:
txt += "\nContract %s\n"%c.name txt += "\nContract %s\n"%c.name
@ -44,3 +47,12 @@ class DataDependency(AbstractPrinter):
table.add_row([v.canonical_name, _get(v, f)]) table.add_row([v.canonical_name, _get(v, f)])
txt += str(table) txt += str(table)
self.info(txt) self.info(txt)
all_txt += txt
all_tables.append((c.name, table))
json = self.generate_json_result(all_txt)
for name, table in all_tables:
self.add_pretty_table_to_json(table, name, json)
return json

@ -5,6 +5,7 @@
from prettytable import PrettyTable from prettytable import PrettyTable
from slither.printers.abstract_printer import AbstractPrinter from slither.printers.abstract_printer import AbstractPrinter
class FunctionSummary(AbstractPrinter): class FunctionSummary(AbstractPrinter):
ARGUMENT = 'function-summary' ARGUMENT = 'function-summary'
@ -28,6 +29,9 @@ class FunctionSummary(AbstractPrinter):
_filename(string) _filename(string)
""" """
all_tables = []
all_txt = ''
for c in self.contracts: for c in self.contracts:
(name, inheritance, var, func_summaries, modif_summaries) = c.get_summary() (name, inheritance, var, func_summaries, modif_summaries) = c.get_summary()
txt = "\nContract %s"%name txt = "\nContract %s"%name
@ -62,3 +66,12 @@ class FunctionSummary(AbstractPrinter):
txt += "\n\n"+str(table) txt += "\n\n"+str(table)
txt += "\n" txt += "\n"
self.info(txt) self.info(txt)
all_tables.append((name, table))
all_txt += txt
json = self.generate_json_result(all_txt)
for name, table in all_tables:
self.add_pretty_table_to_json(table, name, json)
return json

@ -1,11 +1,9 @@
""" """
Module printing summary of the contract Module printing summary of the contract
""" """
import collections
from prettytable import PrettyTable from prettytable import PrettyTable
from slither.printers.abstract_printer import AbstractPrinter from slither.printers.abstract_printer import AbstractPrinter
from slither.utils.colors import blue, green, magenta
from slither.utils.function import get_function_id from slither.utils.function import get_function_id
class FunctionIds(AbstractPrinter): class FunctionIds(AbstractPrinter):
@ -23,6 +21,7 @@ class FunctionIds(AbstractPrinter):
""" """
txt = '' txt = ''
all_tables = []
for contract in self.slither.contracts_derived: for contract in self.slither.contracts_derived:
txt += '\n{}:\n'.format(contract.name) txt += '\n{}:\n'.format(contract.name)
table = PrettyTable(['Name', 'ID']) table = PrettyTable(['Name', 'ID'])
@ -34,5 +33,12 @@ class FunctionIds(AbstractPrinter):
sig = variable.function_name sig = variable.function_name
table.add_row([sig, hex(get_function_id(sig))]) table.add_row([sig, hex(get_function_id(sig))])
txt += str(table) + '\n' txt += str(table) + '\n'
all_tables.append((contract.name, table))
self.info(txt) self.info(txt)
json = self.generate_json_result(txt)
for name, table in all_tables:
self.add_pretty_table_to_json(table, name, json)
return json

@ -195,10 +195,22 @@ class PrinterHumanSummary(AbstractPrinter):
txt = "\n" txt = "\n"
txt += self._compilation_type() txt += self._compilation_type()
results = {
'contracts': {
"elements": []
},
'number_lines': 0,
'number_lines_in_dependencies': 0,
'standard_libraries': [],
'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
txt += f'Number of lines: {total_lines} (+ {total_dep_lines} in dependencies)\n' txt += f'Number of lines: {total_lines} (+ {total_dep_lines} in dependencies)\n'
results['number_lines'] = total_lines
results['number_lines__dependencies'] = total_dep_lines
number_contracts, number_contracts_deps = self._number_contracts() number_contracts, number_contracts_deps = self._number_contracts()
txt += f'Number of contracts: {number_contracts} (+ {number_contracts_deps} in dependencies) \n\n' txt += f'Number of contracts: {number_contracts} (+ {number_contracts_deps} in dependencies) \n\n'
@ -208,10 +220,12 @@ class PrinterHumanSummary(AbstractPrinter):
libs = self._standard_libraries() libs = self._standard_libraries()
if libs: if libs:
txt += f'\nUse: {", ".join(libs)}\n' txt += f'\nUse: {", ".join(libs)}\n'
results['standard_libraries'] = [str(l) for l in libs]
ercs = self._ercs() ercs = self._ercs()
if ercs: if ercs:
txt += f'ERCs: {", ".join(ercs)}\n' txt += f'ERCs: {", ".join(ercs)}\n'
results['ercs'] = [str(e) for e in ercs]
for contract in self.slither.contracts_derived: for contract in self.slither.contracts_derived:
txt += "\nContract {}\n".format(contract.name) txt += "\nContract {}\n".format(contract.name)
@ -226,3 +240,31 @@ class PrinterHumanSummary(AbstractPrinter):
txt += self.get_summary_erc20(contract) txt += self.get_summary_erc20(contract)
self.info(txt) self.info(txt)
for contract in self.slither.contracts_derived:
optimization, info, low, medium, high = self._get_detectors_result()
contract_d = {'contract_name': contract.name,
'is_complex_code': self._is_complex_code(contract),
'optimization_issues': optimization,
'informational_issues': info,
'low_issues': low,
'medium_issues': medium,
'high_issues': high,
'is_erc20': contract.is_erc20(),
'number_functions': self._number_functions(contract)}
if contract_d['is_erc20']:
pause, mint_limited, race_condition_mitigated = self._get_summary_erc20(contract)
contract_d['erc20_pause'] = pause
if mint_limited is not None:
contract_d['erc20_can_mint'] = True
contract_d['erc20_mint_limited'] = mint_limited
else:
contract_d['erc20_can_mint'] = False
contract_d['erc20_race_condition_mitigated'] = race_condition_mitigated
self.add_contract_to_json(contract, results['contracts'], additional_fields=contract_d)
json = self.generate_json_result(txt, additional_fields=results)
return json

@ -6,6 +6,7 @@ from prettytable import PrettyTable
from slither.core.declarations import Function from slither.core.declarations import Function
from slither.printers.abstract_printer import AbstractPrinter from slither.printers.abstract_printer import AbstractPrinter
class Modifiers(AbstractPrinter): class Modifiers(AbstractPrinter):
ARGUMENT = 'modifiers' ARGUMENT = 'modifiers'
@ -20,6 +21,9 @@ class Modifiers(AbstractPrinter):
_filename(string) _filename(string)
""" """
all_txt = ''
all_tables = []
for contract in self.slither.contracts_derived: for contract in self.slither.contracts_derived:
txt = "\nContract %s"%contract.name txt = "\nContract %s"%contract.name
table = PrettyTable(["Function", table = PrettyTable(["Function",
@ -35,3 +39,9 @@ class Modifiers(AbstractPrinter):
table.add_row([function.name, [m.name for m in set(modifiers)]]) table.add_row([function.name, [m.name for m in set(modifiers)]])
txt += "\n"+str(table) txt += "\n"+str(table)
self.info(txt) self.info(txt)
json = self.generate_json_result(all_txt)
for name, table in all_tables:
self.add_pretty_table_to_json(table, name, json)
return json

@ -29,6 +29,8 @@ class RequireOrAssert(AbstractPrinter):
_filename(string) _filename(string)
""" """
all_tables = []
all_txt = ''
for contract in self.slither.contracts_derived: for contract in self.slither.contracts_derived:
txt = "\nContract %s"%contract.name txt = "\nContract %s"%contract.name
table = PrettyTable(["Function", table = PrettyTable(["Function",
@ -40,3 +42,11 @@ class RequireOrAssert(AbstractPrinter):
table.add_row([function.name, self._convert([str(m.expression) for m in set(require)])]) table.add_row([function.name, self._convert([str(m.expression) for m in set(require)])])
txt += "\n"+str(table) txt += "\n"+str(table)
self.info(txt) self.info(txt)
all_tables.append((contract.name, table))
all_txt += txt
json = self.generate_json_result(all_txt)
for name, table in all_tables:
self.add_pretty_table_to_json(table, name, json)
return json

@ -3,7 +3,6 @@
""" """
from slither.printers.abstract_printer import AbstractPrinter from slither.printers.abstract_printer import AbstractPrinter
from slither.utils.colors import blue, green, magenta
class PrinterSlithIR(AbstractPrinter): class PrinterSlithIR(AbstractPrinter):

@ -3,7 +3,7 @@
""" """
from slither.printers.abstract_printer import AbstractPrinter from slither.printers.abstract_printer import AbstractPrinter
from slither.utils.colors import blue, green, magenta
class PrinterSlithIRSSA(AbstractPrinter): class PrinterSlithIRSSA(AbstractPrinter):

@ -5,6 +5,7 @@
from prettytable import PrettyTable from prettytable import PrettyTable
from slither.printers.abstract_printer import AbstractPrinter from slither.printers.abstract_printer import AbstractPrinter
class VariableOrder(AbstractPrinter): class VariableOrder(AbstractPrinter):
ARGUMENT = 'variable-order' ARGUMENT = 'variable-order'
@ -20,12 +21,23 @@ class VariableOrder(AbstractPrinter):
""" """
txt = '' txt = ''
all_tables = []
for contract in self.slither.contracts_derived: for contract in self.slither.contracts_derived:
txt += '\n{}:\n'.format(contract.name) txt += '\n{}:\n'.format(contract.name)
table = PrettyTable(['Name', 'Type']) table = PrettyTable(['Name', 'Type'])
for variable in contract.state_variables_ordered: for variable in contract.state_variables_ordered:
if not variable.is_constant: if not variable.is_constant:
table.add_row([variable.canonical_name, str(variable.type)]) table.add_row([variable.canonical_name, str(variable.type)])
all_tables.append((contract.name, table))
txt += str(table) + '\n' txt += str(table) + '\n'
self.info(txt) self.info(txt)
json = self.generate_json_result(txt)
for name, table in all_tables:
self.add_pretty_table_to_json(table, name, json)
return json

@ -12,8 +12,8 @@ from .colors import yellow, red
logger = logging.getLogger("Slither") logger = logging.getLogger("Slither")
DEFAULT_JSON_OUTPUT_TYPES = ["detectors"] DEFAULT_JSON_OUTPUT_TYPES = ["detectors", "printers"]
JSON_OUTPUT_TYPES = ["compilations", "console", "detectors", "list-detectors", "list-printers"] JSON_OUTPUT_TYPES = ["compilations", "console", "detectors", "printers", "list-detectors", "list-printers"]
# Those are the flags shared by the command line and the config file # Those are the flags shared by the command line and the config file

@ -297,6 +297,50 @@ def add_pragma_to_json(pragma, d, additional_fields=None):
d['elements'].append(element) 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 # endregion
################################################################################### ###################################################################################
################################################################################### ###################################################################################

Loading…
Cancel
Save