Skip to content

Commit

Permalink
Create Json output for printers (fix #286)
Browse files Browse the repository at this point in the history
  • Loading branch information
montyly committed Oct 26, 2019
1 parent 8d32de0 commit 23ea41e
Show file tree
Hide file tree
Showing 20 changed files with 305 additions and 44 deletions.
55 changes: 33 additions & 22 deletions slither/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
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

Expand Down Expand Up @@ -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):
Expand All @@ -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):
Expand Down Expand Up @@ -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'])

Expand Down Expand Up @@ -548,21 +553,23 @@ def main_impl(all_detector_classes, all_printer_classes):
if not filenames:
filenames = globbed_filenames
number_contracts = 0
results = []
results_detectors = []
results_printers = []
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:
Expand All @@ -574,8 +581,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:
Expand All @@ -589,7 +600,7 @@ 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:
Expand Down Expand Up @@ -626,7 +637,7 @@ def main_impl(all_detector_classes, all_printer_classes):
if output_error:
sys.exit(-1)
else:
exit(results)
exit(len(results_detectors))


if __name__ == '__main__':
Expand Down
9 changes: 9 additions & 0 deletions slither/printers/abstract_printer.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import abc

from slither.utils import json_utils


class IncorrectPrinterInitialization(Exception):
pass
Expand Down Expand Up @@ -30,6 +32,13 @@ def info(self, info):
if self.logger:
self.logger.info(info)


def generate_json_result(self, info, additional_fields={}):
d = json_utils.generate_json_result(info, additional_fields)
d['printer'] = self.ARGUMENT

return d

@abc.abstractmethod
def output(self, filename):
"""TODO Documentation"""
Expand Down
25 changes: 20 additions & 5 deletions slither/printers/call/call_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
from slither.core.solidity_types.user_defined_type import UserDefinedType

# return unique id for contract to use as subgraph name
from slither.utils import json_utils


def _contract_subgraph(contract):
return f'cluster_{contract.id}_{contract.name}'

Expand Down Expand Up @@ -163,13 +166,25 @@ def output(self, filename):
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:
json_utils.add_file_to_json(filename, content, json)

return json

16 changes: 14 additions & 2 deletions slither/printers/functions/authorization.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from prettytable import PrettyTable
from slither.printers.abstract_printer import AbstractPrinter
from slither.core.declarations.function import Function
from slither.utils import json_utils


class PrinterWrittenVariablesAndAuthorization(AbstractPrinter):

Expand Down Expand Up @@ -33,12 +35,22 @@ def output(self, _filename):
_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:
json_utils.add_pretty_table_to_json(table, name, json)

return json
17 changes: 15 additions & 2 deletions slither/printers/functions/cfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

from slither.printers.abstract_printer import AbstractPrinter
from slither.core.declarations.function import Function
from slither.utils import json_utils


class CFG(AbstractPrinter):

Expand All @@ -18,9 +20,20 @@ def output(self, original_filename):
_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:
json_utils.add_file_to_json(filename, content, json)
return json
4 changes: 3 additions & 1 deletion slither/printers/guidance/echidna.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,6 @@ def output(self, filename):
'constants_used': cst_used,
'constants_used_in_binary': cst_used_in_binary}

print(json.dumps(d, indent=4))
self.info(json.dumps(d, indent=4))

return d
23 changes: 23 additions & 0 deletions slither/printers/inheritance/inheritance.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"""

from slither.printers.abstract_printer import AbstractPrinter
from slither.utils import json_utils
from slither.utils.colors import blue, green


Expand Down Expand Up @@ -35,24 +36,46 @@ def output(self, filename):

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
18 changes: 14 additions & 4 deletions slither/printers/inheritance/inheritance_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from slither.core.declarations.contract import Contract
from slither.core.solidity_types.user_defined_type import UserDefinedType
from slither.printers.abstract_printer import AbstractPrinter
from slither.utils import json_utils
from slither.utils.inheritance_analysis import (detect_c3_function_shadowing,
detect_state_variable_shadowing)

Expand Down Expand Up @@ -156,14 +157,23 @@ def output(self, filename):
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)
json_utils.add_file_to_json(filename, content, json)

return json
6 changes: 3 additions & 3 deletions slither/printers/summary/constructor_calls.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ def output(self,_filename):
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
Loading

0 comments on commit 23ea41e

Please sign in to comment.