diff --git a/slither/__main__.py b/slither/__main__.py index a2b3a395b..5f9a68158 100644 --- a/slither/__main__.py +++ b/slither/__main__.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, output_detectors_json, output_printers, output_printers_json, 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 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): compilations = compile_all(target, **vars(args)) slither_instances = [] - results = [] + results_detectors = [] + results_printers = [] analyzed_contracts_count = 0 for compilation in compilations: - (slither, current_results, current_analyzed_count) = process_single(compilation, args, detector_classes, printer_classes) - results.extend(current_results) + (slither, current_results_detectors, current_results_printers, current_analyzed_count) = process_single(compilation, args, detector_classes, printer_classes) + results_detectors.extend(current_results_detectors) + results_printers.extend(current_results_printers) slither_instances.append(slither) 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): @@ -78,18 +80,21 @@ def _process(slither, detector_classes, printer_classes): analyzed_contracts_count = len(slither.contracts) - results = [] + results_detectors = [] + results_printers = [] if not printer_classes: detector_results = slither.run_detectors() 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 + results_detectors.extend(detector_results) - results.extend(detector_results) - - slither.run_printers() # Currently printers does not return results + else: + printer_results = slither.run_printers() + 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): @@ -320,9 +325,9 @@ def parse_args(detector_classes, printer_classes): default=defaults_flag_in_config['json']) group_misc.add_argument('--json-types', - help='Comma-separated list of result types to output to JSON, defaults to all, ' - 'available types: {}'.format( - ', '.join(output_type for output_type in JSON_OUTPUT_TYPES)), + help=f'Comma-separated list of result types to output to JSON, defaults to ' +\ + f'{",".join(output_type for output_type in DEFAULT_JSON_OUTPUT_TYPES)}. ' +\ + f'Available types: {",".join(output_type for output_type in JSON_OUTPUT_TYPES)}', action='store', 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.setLevel(logging.INFO) + results_detectors = [] + results_printers = [] try: filename = args.filename @@ -548,21 +555,22 @@ def main_impl(all_detector_classes, all_printer_classes): if not filenames: filenames = globbed_filenames number_contracts = 0 - results = [] + slither_instances = [] 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) else: 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 - results += results_tmp + results_detectors += results_detectors_tmp + results_printers += results_printers_tmp slither_instances.append(slither_instance) # Rely on CryticCompile to discern the underlying type of compilations. 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 if outputting_json: @@ -574,8 +582,12 @@ def main_impl(all_detector_classes, all_printer_classes): json_results['compilations'] = compilation_results # Add our detector results to JSON if desired. - if results and 'detectors' in args.json_types: - json_results['detectors'] = results + if results_detectors and 'detectors' in args.json_types: + 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 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. if args.checklist: - output_results_to_markdown(results) + output_results_to_markdown(results_detectors) # Dont print the number of result for printers if number_contracts == 0: - logger.warn(red('No contract was analyzed')) + logger.warning(red('No contract was analyzed')) if printer_classes: logger.info('%s analyzed (%d contracts)', filename, number_contracts) 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: return @@ -626,7 +639,7 @@ def main_impl(all_detector_classes, all_printer_classes): if output_error: sys.exit(-1) else: - exit(results) + exit(results_detectors) if __name__ == '__main__': diff --git a/slither/printers/abstract_printer.py b/slither/printers/abstract_printer.py index 4926c1de2..3c7331138 100644 --- a/slither/printers/abstract_printer.py +++ b/slither/printers/abstract_printer.py @@ -1,5 +1,7 @@ import abc +from slither.utils import json_utils + class IncorrectPrinterInitialization(Exception): pass @@ -30,6 +32,39 @@ class AbstractPrinter(metaclass=abc.ABCMeta): if self.logger: 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 def output(self, filename): """TODO Documentation""" diff --git a/slither/printers/call/call_graph.py b/slither/printers/call/call_graph.py index 4c9b9e385..2f224ece0 100644 --- a/slither/printers/call/call_graph.py +++ b/slither/printers/call/call_graph.py @@ -9,13 +9,10 @@ from collections import defaultdict from slither.printers.abstract_printer import AbstractPrinter from slither.core.declarations.solidity_variables import SolidityFunction 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.solidity_types.user_defined_type import UserDefinedType -# return unique id for contract to use as subgraph name + + def _contract_subgraph(contract): return f'cluster_{contract.id}_{contract.name}' @@ -163,13 +160,25 @@ class PrinterCallGraph(AbstractPrinter): if filename == ".dot": filename = "all_contracts.dot" + info = '' + results = [] with open(filename, 'w', encoding='utf8') as f: - self.info(f'Call Graph: {filename}') - f.write('\n'.join(['strict digraph {'] + [self._process_functions(self.slither.functions)] + ['}'])) - + info += f'Call Graph: {filename}' + 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: with open(f'{derived_contract.name}.dot', 'w', encoding='utf8') as f: - self.info(f'Call Graph: {derived_contract.name}.dot') - f.write('\n'.join(['strict digraph {'] + [self._process_functions(derived_contract.functions)] + ['}'])) + info += f'Call Graph: {derived_contract.name}.dot' + 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 diff --git a/slither/printers/functions/authorization.py b/slither/printers/functions/authorization.py index 00af8f1a6..72fc532b3 100644 --- a/slither/printers/functions/authorization.py +++ b/slither/printers/functions/authorization.py @@ -6,6 +6,7 @@ from prettytable import PrettyTable from slither.printers.abstract_printer import AbstractPrinter from slither.core.declarations.function import Function + class PrinterWrittenVariablesAndAuthorization(AbstractPrinter): ARGUMENT = 'vars-and-auth' @@ -33,12 +34,22 @@ class PrinterWrittenVariablesAndAuthorization(AbstractPrinter): _filename(string) """ + txt = '' + all_tables = [] 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"]) for function in contract.functions: state_variables_written = [v.name for v in function.all_state_variables_written()] msg_sender_condition = self.get_msg_sender_checks(function) 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 \ No newline at end of file diff --git a/slither/printers/functions/cfg.py b/slither/printers/functions/cfg.py index 06be72636..191d63b5c 100644 --- a/slither/printers/functions/cfg.py +++ b/slither/printers/functions/cfg.py @@ -2,7 +2,7 @@ """ from slither.printers.abstract_printer import AbstractPrinter -from slither.core.declarations.function import Function + class CFG(AbstractPrinter): @@ -18,9 +18,20 @@ class CFG(AbstractPrinter): _filename(string) """ + info = '' + all_files = [] for contract in self.contracts: for function in contract.functions + contract.modifiers: filename = "{}-{}-{}.dot".format(original_filename, contract.name, function.full_name) - self.info('Export {}'.format(filename)) - function.slithir_cfg_to_dot(filename) + info += 'Export {}'.format(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 \ No newline at end of file diff --git a/slither/printers/guidance/echidna.py b/slither/printers/guidance/echidna.py index 288a4d1f5..f2034aa17 100644 --- a/slither/printers/guidance/echidna.py +++ b/slither/printers/guidance/echidna.py @@ -138,4 +138,6 @@ class Echidna(AbstractPrinter): 'constants_used': cst_used, 'constants_used_in_binary': cst_used_in_binary} - print(json.dumps(d, indent=4)) \ No newline at end of file + self.info(json.dumps(d, indent=4)) + + return d \ No newline at end of file diff --git a/slither/printers/inheritance/inheritance.py b/slither/printers/inheritance/inheritance.py index c80fa7cb2..f3b970dfc 100644 --- a/slither/printers/inheritance/inheritance.py +++ b/slither/printers/inheritance/inheritance.py @@ -35,24 +35,46 @@ class PrinterInheritance(AbstractPrinter): info += blue('Child_Contract -> ') + green('Immediate_Base_Contracts') info += green(' [Not_Immediate_Base_Contracts]') + + result = {} + result['child_to_base'] = {} + for child in self.contracts: info += blue(f'\n+ {child.name}') + result['child_to_base'][child.name] = {'immediate': [], + 'not_immediate': []} if child.inheritance: + immediate = child.immediate_inheritance not_immediate = [i for i in child.inheritance if i not in immediate] + info += ' -> ' + green(", ".join(map(str, immediate))) + result['child_to_base'][child.name]['immediate'] = list(map(str, immediate)) if 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 += blue(' [Not_Immediate_Child_Contracts]') + + result['base_to_child'] = {} for base in self.contracts: info += green(f'\n+ {base.name}') children = list(self._get_child_contracts(base)) + + result['base_to_child'][base.name] = {'immediate': [], + 'not_immediate': []} if children: immediate = [child for child in children if base in child.immediate_inheritance] not_immediate = [child for child in children if not child in immediate] + info += ' -> ' + blue(", ".join(map(str, immediate))) + result['base_to_child'][base.name]['immediate'] = list(map(str, immediate)) if not_immediate: info += ', [' + blue(", ".join(map(str, not_immediate))) + ']' + result['base_to_child'][base.name]['not_immediate'] = list(map(str, immediate)) self.info(info) + + json = self.generate_json_result(info, additional_fields=result) + + return json diff --git a/slither/printers/inheritance/inheritance_graph.py b/slither/printers/inheritance/inheritance_graph.py index 4dbed9e0b..52f4f32e2 100644 --- a/slither/printers/inheritance/inheritance_graph.py +++ b/slither/printers/inheritance/inheritance_graph.py @@ -156,14 +156,23 @@ class PrinterInheritanceGraph(AbstractPrinter): Args: filename(string) """ + if filename == '': filename = 'contracts.dot' if not filename.endswith('.dot'): filename += ".dot" info = 'Inheritance Graph: ' + filename self.info(info) + + content = 'digraph "" {\n' + for c in self.contracts: + content += self._summary(c) + '\n' + content += '}' + with open(filename, 'w', encoding='utf8') as f: - f.write('digraph "" {\n') - for c in self.contracts: - f.write(self._summary(c)) - f.write('}') + f.write(content) + + json = self.generate_json_result(info) + self.add_file_to_json(filename, content, json) + + return json \ No newline at end of file diff --git a/slither/printers/summary/constructor_calls.py b/slither/printers/summary/constructor_calls.py index 66e64d626..d0edb93be 100644 --- a/slither/printers/summary/constructor_calls.py +++ b/slither/printers/summary/constructor_calls.py @@ -35,13 +35,13 @@ class ConstructorPrinter(AbstractPrinter): 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; + count = len(stack_name)-2 while count>=0: print("-->",stack_name[count], sep=' ', end='', flush=True) - count= count-1; + 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; + count = count-1 diff --git a/slither/printers/summary/contract.py b/slither/printers/summary/contract.py index 63a13fb95..df7742281 100644 --- a/slither/printers/summary/contract.py +++ b/slither/printers/summary/contract.py @@ -5,8 +5,8 @@ import collections from slither.printers.abstract_printer import AbstractPrinter from slither.utils.colors import blue, green, magenta -class ContractSummary(AbstractPrinter): +class ContractSummary(AbstractPrinter): ARGUMENT = 'contract-summary' HELP = 'Print a summary of the contracts' @@ -20,28 +20,42 @@ class ContractSummary(AbstractPrinter): """ txt = "" + + all_contracts = [] for c in self.contracts: - (name, _inheritance, _var, func_summaries, _modif_summaries) = c.get_summary(False) - txt += blue("\n+ Contract %s\n"%name) - # (c_name, f_name, visi, _, _, _, _, _) in func_summaries - public = [(elem[0], (elem[1], elem[2]) ) for elem in func_summaries] + txt += blue("\n+ Contract %s\n" % c.name) + additional_fields = {"elements": []} + # 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) - for a,b in public: + for a, b in public: collect[a].append(b) public = list(collect.items()) for contract, functions in public: txt += blue(" - From {}\n".format(contract)) - functions = sorted(functions) - for (function, visi) in functions: - if visi in ['external', 'public']: - txt += green(" - {} ({})\n".format(function, visi)) - for (function, visi) in functions: - if visi in ['internal', 'private']: - txt += magenta(" - {} ({})\n".format(function, visi)) - for (function, visi) in functions: - if visi not in ['external', 'public', 'internal', 'private']: - txt += " - {}  ({})\n".format(function, visi) + + functions = sorted(functions, key=lambda f: f.full_name) + + for function in functions: + if function.visibility in ['external', 'public']: + txt += green(" - {} ({})\n".format(function, function.visibility)) + if function.visibility in ['internal', 'private']: + txt += magenta(" - {} ({})\n".format(function, function.visibility)) + if function.visibility not in ['external', 'public', 'internal', 'private']: + txt += " - {}  ({})\n".format(function, function.visibility) + + self.add_function_to_json(function, additional_fields, additional_fields={"visibility": + function.visibility}) + + all_contracts.append((c, additional_fields)) 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 diff --git a/slither/printers/summary/data_depenency.py b/slither/printers/summary/data_depenency.py index c8fb38277..cbd36fcdb 100644 --- a/slither/printers/summary/data_depenency.py +++ b/slither/printers/summary/data_depenency.py @@ -25,6 +25,9 @@ class DataDependency(AbstractPrinter): _filename(string) """ + all_tables = [] + all_txt = '' + txt = '' for c in self.contracts: txt += "\nContract %s\n"%c.name @@ -44,3 +47,12 @@ class DataDependency(AbstractPrinter): table.add_row([v.canonical_name, _get(v, f)]) txt += str(table) 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 diff --git a/slither/printers/summary/function.py b/slither/printers/summary/function.py index 13650ba74..e9a71a965 100644 --- a/slither/printers/summary/function.py +++ b/slither/printers/summary/function.py @@ -5,6 +5,7 @@ from prettytable import PrettyTable from slither.printers.abstract_printer import AbstractPrinter + class FunctionSummary(AbstractPrinter): ARGUMENT = 'function-summary' @@ -28,6 +29,9 @@ class FunctionSummary(AbstractPrinter): _filename(string) """ + all_tables = [] + all_txt = '' + for c in self.contracts: (name, inheritance, var, func_summaries, modif_summaries) = c.get_summary() txt = "\nContract %s"%name @@ -62,3 +66,12 @@ class FunctionSummary(AbstractPrinter): txt += "\n\n"+str(table) txt += "\n" 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 diff --git a/slither/printers/summary/function_ids.py b/slither/printers/summary/function_ids.py index 7d6539bf1..5aee6529c 100644 --- a/slither/printers/summary/function_ids.py +++ b/slither/printers/summary/function_ids.py @@ -1,11 +1,9 @@ """ Module printing summary of the contract """ -import collections from prettytable import PrettyTable from slither.printers.abstract_printer import AbstractPrinter -from slither.utils.colors import blue, green, magenta from slither.utils.function import get_function_id class FunctionIds(AbstractPrinter): @@ -23,6 +21,7 @@ class FunctionIds(AbstractPrinter): """ txt = '' + all_tables = [] for contract in self.slither.contracts_derived: txt += '\n{}:\n'.format(contract.name) table = PrettyTable(['Name', 'ID']) @@ -34,5 +33,12 @@ class FunctionIds(AbstractPrinter): sig = variable.function_name table.add_row([sig, hex(get_function_id(sig))]) txt += str(table) + '\n' + all_tables.append((contract.name, table)) 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 \ No newline at end of file diff --git a/slither/printers/summary/human_summary.py b/slither/printers/summary/human_summary.py index d83fbb759..156e353fa 100644 --- a/slither/printers/summary/human_summary.py +++ b/slither/printers/summary/human_summary.py @@ -195,10 +195,22 @@ class PrinterHumanSummary(AbstractPrinter): txt = "\n" txt += self._compilation_type() + results = { + 'contracts': { + "elements": [] + }, + 'number_lines': 0, + 'number_lines_in_dependencies': 0, + 'standard_libraries': [], + 'ercs': [], + } + lines_number = self._lines_number() if lines_number: total_lines, total_dep_lines = lines_number 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() 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() if libs: txt += f'\nUse: {", ".join(libs)}\n' + results['standard_libraries'] = [str(l) for l in libs] ercs = self._ercs() if ercs: txt += f'ERCs: {", ".join(ercs)}\n' + results['ercs'] = [str(e) for e in ercs] for contract in self.slither.contracts_derived: txt += "\nContract {}\n".format(contract.name) @@ -226,3 +240,31 @@ class PrinterHumanSummary(AbstractPrinter): txt += self.get_summary_erc20(contract) 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 + diff --git a/slither/printers/summary/modifier_calls.py b/slither/printers/summary/modifier_calls.py index b76736937..fbc62c90d 100644 --- a/slither/printers/summary/modifier_calls.py +++ b/slither/printers/summary/modifier_calls.py @@ -6,6 +6,7 @@ from prettytable import PrettyTable from slither.core.declarations import Function from slither.printers.abstract_printer import AbstractPrinter + class Modifiers(AbstractPrinter): ARGUMENT = 'modifiers' @@ -20,6 +21,9 @@ class Modifiers(AbstractPrinter): _filename(string) """ + all_txt = '' + all_tables = [] + for contract in self.slither.contracts_derived: txt = "\nContract %s"%contract.name table = PrettyTable(["Function", @@ -35,3 +39,9 @@ class Modifiers(AbstractPrinter): table.add_row([function.name, [m.name for m in set(modifiers)]]) txt += "\n"+str(table) 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 \ No newline at end of file diff --git a/slither/printers/summary/require_calls.py b/slither/printers/summary/require_calls.py index 34149b07c..51c67284d 100644 --- a/slither/printers/summary/require_calls.py +++ b/slither/printers/summary/require_calls.py @@ -29,6 +29,8 @@ class RequireOrAssert(AbstractPrinter): _filename(string) """ + all_tables = [] + all_txt = '' for contract in self.slither.contracts_derived: txt = "\nContract %s"%contract.name 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)])]) txt += "\n"+str(table) 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 diff --git a/slither/printers/summary/slithir.py b/slither/printers/summary/slithir.py index 70696c6fa..3466d0212 100644 --- a/slither/printers/summary/slithir.py +++ b/slither/printers/summary/slithir.py @@ -3,7 +3,6 @@ """ from slither.printers.abstract_printer import AbstractPrinter -from slither.utils.colors import blue, green, magenta class PrinterSlithIR(AbstractPrinter): diff --git a/slither/printers/summary/slithir_ssa.py b/slither/printers/summary/slithir_ssa.py index c97a291fa..37acd457e 100644 --- a/slither/printers/summary/slithir_ssa.py +++ b/slither/printers/summary/slithir_ssa.py @@ -3,7 +3,7 @@ """ from slither.printers.abstract_printer import AbstractPrinter -from slither.utils.colors import blue, green, magenta + class PrinterSlithIRSSA(AbstractPrinter): diff --git a/slither/printers/summary/variable_order.py b/slither/printers/summary/variable_order.py index 48d96356b..ffee1f940 100644 --- a/slither/printers/summary/variable_order.py +++ b/slither/printers/summary/variable_order.py @@ -5,6 +5,7 @@ from prettytable import PrettyTable from slither.printers.abstract_printer import AbstractPrinter + class VariableOrder(AbstractPrinter): ARGUMENT = 'variable-order' @@ -20,12 +21,23 @@ class VariableOrder(AbstractPrinter): """ txt = '' + + all_tables = [] + for contract in self.slither.contracts_derived: txt += '\n{}:\n'.format(contract.name) table = PrettyTable(['Name', 'Type']) for variable in contract.state_variables_ordered: if not variable.is_constant: table.add_row([variable.canonical_name, str(variable.type)]) + + 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 \ No newline at end of file diff --git a/slither/utils/command_line.py b/slither/utils/command_line.py index ee9b70bbc..2ead86223 100644 --- a/slither/utils/command_line.py +++ b/slither/utils/command_line.py @@ -12,8 +12,8 @@ from .colors import yellow, red logger = logging.getLogger("Slither") -DEFAULT_JSON_OUTPUT_TYPES = ["detectors"] -JSON_OUTPUT_TYPES = ["compilations", "console", "detectors", "list-detectors", "list-printers"] +DEFAULT_JSON_OUTPUT_TYPES = ["detectors", "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 diff --git a/slither/utils/json_utils.py b/slither/utils/json_utils.py index b748a7a09..8aecc8230 100644 --- a/slither/utils/json_utils.py +++ b/slither/utils/json_utils.py @@ -297,6 +297,50 @@ def add_pragma_to_json(pragma, d, additional_fields=None): 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 ################################################################################### ###################################################################################