|
|
|
@ -18,7 +18,7 @@ from slither.detectors.abstract_detector import (AbstractDetector, |
|
|
|
|
from slither.printers import all_printers |
|
|
|
|
from slither.printers.abstract_printer import AbstractPrinter |
|
|
|
|
from slither.slither import Slither |
|
|
|
|
from slither.utils.colors import red, set_colorization_enabled |
|
|
|
|
from slither.utils.colors import red, yellow, set_colorization_enabled |
|
|
|
|
from slither.utils.command_line import (output_detectors, |
|
|
|
|
output_detectors_json, output_printers, |
|
|
|
|
output_to_markdown, output_wiki) |
|
|
|
@ -43,7 +43,13 @@ def process(filename, args, detector_classes, printer_classes): |
|
|
|
|
ast = '--ast-compact-json' |
|
|
|
|
if args.legacy_ast: |
|
|
|
|
ast = '--ast-json' |
|
|
|
|
slither = Slither(filename, args.solc, args.disable_solc_warnings, args.solc_args, ast) |
|
|
|
|
slither = Slither(filename, |
|
|
|
|
solc=args.solc, |
|
|
|
|
disable_solc_warnings=args.disable_solc_warnings, |
|
|
|
|
solc_arguments=args.solc_args, |
|
|
|
|
ast_format=ast, |
|
|
|
|
filter_paths=parse_filter_paths(args), |
|
|
|
|
triage_mode=args.triage_mode) |
|
|
|
|
|
|
|
|
|
return _process(slither, detector_classes, printer_classes) |
|
|
|
|
|
|
|
|
@ -70,35 +76,39 @@ def _process(slither, detector_classes, printer_classes): |
|
|
|
|
return results, analyzed_contracts_count |
|
|
|
|
|
|
|
|
|
def process_truffle(dirname, args, detector_classes, printer_classes): |
|
|
|
|
cmd = ['truffle', 'compile'] |
|
|
|
|
if args.truffle_version: |
|
|
|
|
cmd = ['npx',args.truffle_version,'compile'] |
|
|
|
|
elif os.path.isfile('package.json'): |
|
|
|
|
with open('package.json') as f: |
|
|
|
|
package = json.load(f) |
|
|
|
|
if 'devDependencies' in package: |
|
|
|
|
if 'truffle' in package['devDependencies']: |
|
|
|
|
version = package['devDependencies']['truffle'] |
|
|
|
|
if version.startswith('^'): |
|
|
|
|
version = version[1:] |
|
|
|
|
truffle_version = 'truffle@{}'.format(version) |
|
|
|
|
cmd = ['npx', truffle_version,'compile'] |
|
|
|
|
logger.info("'{}' running (use --truffle-version truffle@x.x.x to use specific version)".format(' '.join(cmd))) |
|
|
|
|
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
|
|
|
|
|
|
|
|
|
stdout, stderr = process.communicate() |
|
|
|
|
stdout, stderr = stdout.decode(), stderr.decode() # convert bytestrings to unicode strings |
|
|
|
|
|
|
|
|
|
logger.info(stdout) |
|
|
|
|
|
|
|
|
|
if stderr: |
|
|
|
|
logger.error(stderr) |
|
|
|
|
if not args.ignore_truffle_compile: |
|
|
|
|
cmd = ['truffle', 'compile'] |
|
|
|
|
if args.truffle_version: |
|
|
|
|
cmd = ['npx',args.truffle_version,'compile'] |
|
|
|
|
elif os.path.isfile('package.json'): |
|
|
|
|
with open('package.json') as f: |
|
|
|
|
package = json.load(f) |
|
|
|
|
if 'devDependencies' in package: |
|
|
|
|
if 'truffle' in package['devDependencies']: |
|
|
|
|
version = package['devDependencies']['truffle'] |
|
|
|
|
if version.startswith('^'): |
|
|
|
|
version = version[1:] |
|
|
|
|
truffle_version = 'truffle@{}'.format(version) |
|
|
|
|
cmd = ['npx', truffle_version,'compile'] |
|
|
|
|
logger.info("'{}' running (use --truffle-version truffle@x.x.x to use specific version)".format(' '.join(cmd))) |
|
|
|
|
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
|
|
|
|
|
|
|
|
|
stdout, stderr = process.communicate() |
|
|
|
|
stdout, stderr = stdout.decode(), stderr.decode() # convert bytestrings to unicode strings |
|
|
|
|
|
|
|
|
|
logger.info(stdout) |
|
|
|
|
|
|
|
|
|
if stderr: |
|
|
|
|
logger.error(stderr) |
|
|
|
|
|
|
|
|
|
slither = Slither(dirname, |
|
|
|
|
args.solc, |
|
|
|
|
args.disable_solc_warnings, |
|
|
|
|
args.solc_args, |
|
|
|
|
is_truffle=True) |
|
|
|
|
solc=args.solc, |
|
|
|
|
disable_solc_warnings=args.disable_solc_warnings, |
|
|
|
|
solc_arguments=args.solc_args, |
|
|
|
|
is_truffle=True, |
|
|
|
|
filter_paths=parse_filter_paths(args), |
|
|
|
|
triage_mode=args.triage_mode) |
|
|
|
|
|
|
|
|
|
return _process(slither, detector_classes, printer_classes) |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -110,7 +120,13 @@ def process_files(filenames, args, detector_classes, printer_classes): |
|
|
|
|
contract_loaded = json.load(f) |
|
|
|
|
all_contracts.append(contract_loaded['ast']) |
|
|
|
|
|
|
|
|
|
slither = Slither(all_contracts, args.solc, args.disable_solc_warnings, args.solc_args) |
|
|
|
|
slither = Slither(all_contracts, |
|
|
|
|
solc=args.solc, |
|
|
|
|
disable_solc_warnings=args.disable_solc_warnings, |
|
|
|
|
solc_arguments=args.solc_args, |
|
|
|
|
filter_paths=parse_filter_paths(args), |
|
|
|
|
triage_mode=args.triage_mode) |
|
|
|
|
|
|
|
|
|
return _process(slither, detector_classes, printer_classes) |
|
|
|
|
|
|
|
|
|
# endregion |
|
|
|
@ -121,8 +137,11 @@ def process_files(filenames, args, detector_classes, printer_classes): |
|
|
|
|
################################################################################### |
|
|
|
|
|
|
|
|
|
def output_json(results, filename): |
|
|
|
|
with open(filename, 'w', encoding='utf8') as f: |
|
|
|
|
json.dump(results, f) |
|
|
|
|
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(results, f) |
|
|
|
|
|
|
|
|
|
# endregion |
|
|
|
|
################################################################################### |
|
|
|
@ -181,10 +200,11 @@ def choose_detectors(args, all_detector_classes): |
|
|
|
|
|
|
|
|
|
if args.detectors_to_run == 'all': |
|
|
|
|
detectors_to_run = all_detector_classes |
|
|
|
|
detectors_excluded = args.detectors_to_exclude.split(',') |
|
|
|
|
for d in detectors: |
|
|
|
|
if d in detectors_excluded: |
|
|
|
|
detectors_to_run.remove(detectors[d]) |
|
|
|
|
if args.detectors_to_exclude: |
|
|
|
|
detectors_excluded = args.detectors_to_exclude.split(',') |
|
|
|
|
for d in detectors: |
|
|
|
|
if d in detectors_excluded: |
|
|
|
|
detectors_to_run.remove(detectors[d]) |
|
|
|
|
else: |
|
|
|
|
for d in args.detectors_to_run.split(','): |
|
|
|
|
if d in detectors: |
|
|
|
@ -219,7 +239,7 @@ def choose_printers(args, all_printer_classes): |
|
|
|
|
printers_to_run = [] |
|
|
|
|
|
|
|
|
|
# disable default printer |
|
|
|
|
if args.printers_to_run == '': |
|
|
|
|
if args.printers_to_run is None: |
|
|
|
|
return [] |
|
|
|
|
|
|
|
|
|
printers = {p.ARGUMENT: p for p in all_printer_classes} |
|
|
|
@ -237,6 +257,31 @@ def choose_printers(args, all_printer_classes): |
|
|
|
|
################################################################################### |
|
|
|
|
################################################################################### |
|
|
|
|
|
|
|
|
|
def parse_filter_paths(args): |
|
|
|
|
if args.filter_paths: |
|
|
|
|
return args.filter_paths.split(',') |
|
|
|
|
return [] |
|
|
|
|
|
|
|
|
|
# Those are the flags shared by the command line and the config file |
|
|
|
|
defaults_flag_in_config = { |
|
|
|
|
'detectors_to_run': 'all', |
|
|
|
|
'printers_to_run': None, |
|
|
|
|
'detectors_to_exclude': None, |
|
|
|
|
'exclude_informational': False, |
|
|
|
|
'exclude_low': False, |
|
|
|
|
'exclude_medium': False, |
|
|
|
|
'exclude_high': False, |
|
|
|
|
'solc': 'solc', |
|
|
|
|
'solc_args': None, |
|
|
|
|
'disable_solc_warnings': False, |
|
|
|
|
'json': None, |
|
|
|
|
'truffle_version': None, |
|
|
|
|
'disable_color': False, |
|
|
|
|
'filter_paths': None, |
|
|
|
|
'ignore_truffle_compile': False, |
|
|
|
|
'legacy_ast': False |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
def parse_args(detector_classes, printer_classes): |
|
|
|
|
parser = argparse.ArgumentParser(description='Slither', |
|
|
|
|
usage="slither.py contract.sol [flag]") |
|
|
|
@ -260,7 +305,7 @@ def parse_args(detector_classes, printer_classes): |
|
|
|
|
', '.join(d.ARGUMENT for d in detector_classes)), |
|
|
|
|
action='store', |
|
|
|
|
dest='detectors_to_run', |
|
|
|
|
default='all') |
|
|
|
|
default=defaults_flag_in_config['detectors_to_run']) |
|
|
|
|
|
|
|
|
|
group_printer.add_argument('--print', |
|
|
|
|
help='Comma-separated list fo contract information printers, ' |
|
|
|
@ -268,7 +313,7 @@ def parse_args(detector_classes, printer_classes): |
|
|
|
|
', '.join(d.ARGUMENT for d in printer_classes)), |
|
|
|
|
action='store', |
|
|
|
|
dest='printers_to_run', |
|
|
|
|
default='') |
|
|
|
|
default=defaults_flag_in_config['printers_to_run']) |
|
|
|
|
|
|
|
|
|
group_detector.add_argument('--list-detectors', |
|
|
|
|
help='List available detectors', |
|
|
|
@ -282,48 +327,46 @@ def parse_args(detector_classes, printer_classes): |
|
|
|
|
nargs=0, |
|
|
|
|
default=False) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
group_detector.add_argument('--exclude', |
|
|
|
|
help='Comma-separated list of detectors that should be excluded', |
|
|
|
|
action='store', |
|
|
|
|
dest='detectors_to_exclude', |
|
|
|
|
default='') |
|
|
|
|
default=defaults_flag_in_config['detectors_to_exclude']) |
|
|
|
|
|
|
|
|
|
group_detector.add_argument('--exclude-informational', |
|
|
|
|
help='Exclude informational impact analyses', |
|
|
|
|
action='store_true', |
|
|
|
|
default=False) |
|
|
|
|
default=defaults_flag_in_config['exclude_informational']) |
|
|
|
|
|
|
|
|
|
group_detector.add_argument('--exclude-low', |
|
|
|
|
help='Exclude low impact analyses', |
|
|
|
|
action='store_true', |
|
|
|
|
default=False) |
|
|
|
|
default=defaults_flag_in_config['exclude_low']) |
|
|
|
|
|
|
|
|
|
group_detector.add_argument('--exclude-medium', |
|
|
|
|
help='Exclude medium impact analyses', |
|
|
|
|
action='store_true', |
|
|
|
|
default=False) |
|
|
|
|
default=defaults_flag_in_config['exclude_medium']) |
|
|
|
|
|
|
|
|
|
group_detector.add_argument('--exclude-high', |
|
|
|
|
help='Exclude high impact analyses', |
|
|
|
|
action='store_true', |
|
|
|
|
default=False) |
|
|
|
|
|
|
|
|
|
default=defaults_flag_in_config['exclude_high']) |
|
|
|
|
|
|
|
|
|
group_solc.add_argument('--solc', |
|
|
|
|
help='solc path', |
|
|
|
|
action='store', |
|
|
|
|
default='solc') |
|
|
|
|
default=defaults_flag_in_config['solc']) |
|
|
|
|
|
|
|
|
|
group_solc.add_argument('--solc-args', |
|
|
|
|
help='Add custom solc arguments. Example: --solc-args "--allow-path /tmp --evm-version byzantium".', |
|
|
|
|
action='store', |
|
|
|
|
default=None) |
|
|
|
|
default=defaults_flag_in_config['solc_args']) |
|
|
|
|
|
|
|
|
|
group_solc.add_argument('--disable-solc-warnings', |
|
|
|
|
help='Disable solc warnings', |
|
|
|
|
action='store_true', |
|
|
|
|
default=False) |
|
|
|
|
default=defaults_flag_in_config['disable_solc_warnings']) |
|
|
|
|
|
|
|
|
|
group_solc.add_argument('--solc-ast', |
|
|
|
|
help='Provide the ast solc file', |
|
|
|
@ -333,17 +376,40 @@ def parse_args(detector_classes, printer_classes): |
|
|
|
|
group_misc.add_argument('--json', |
|
|
|
|
help='Export results as JSON', |
|
|
|
|
action='store', |
|
|
|
|
default=None) |
|
|
|
|
default=defaults_flag_in_config['json']) |
|
|
|
|
group_misc.add_argument('--truffle-version', |
|
|
|
|
help='Use a local Truffle version (with npx)', |
|
|
|
|
action='store', |
|
|
|
|
default=False) |
|
|
|
|
default=defaults_flag_in_config['truffle_version']) |
|
|
|
|
|
|
|
|
|
group_misc.add_argument('--disable-color', |
|
|
|
|
help='Disable output colorization', |
|
|
|
|
action='store_true', |
|
|
|
|
default=False) |
|
|
|
|
default=defaults_flag_in_config['disable_color']) |
|
|
|
|
|
|
|
|
|
group_misc.add_argument('--filter-paths', |
|
|
|
|
help='Comma-separated list of paths for which results will be excluded', |
|
|
|
|
action='store', |
|
|
|
|
dest='filter_paths', |
|
|
|
|
default=defaults_flag_in_config['filter_paths']) |
|
|
|
|
|
|
|
|
|
group_misc.add_argument('--ignore-truffle-compile', |
|
|
|
|
help='Do not run truffle compile', |
|
|
|
|
action='store_true', |
|
|
|
|
dest='ignore_truffle_compile', |
|
|
|
|
default=defaults_flag_in_config['ignore_truffle_compile']) |
|
|
|
|
|
|
|
|
|
group_misc.add_argument('--triage-mode', |
|
|
|
|
help='Run triage mode (save results in slither.db.json)', |
|
|
|
|
action='store_true', |
|
|
|
|
dest='triage_mode', |
|
|
|
|
default=False) |
|
|
|
|
|
|
|
|
|
group_misc.add_argument('--config-file', |
|
|
|
|
help='Provide a config file (default: slither.config.json)', |
|
|
|
|
action='store', |
|
|
|
|
dest='config_file', |
|
|
|
|
default='slither.config.json') |
|
|
|
|
|
|
|
|
|
# debugger command |
|
|
|
|
parser.add_argument('--debug', |
|
|
|
@ -370,7 +436,7 @@ def parse_args(detector_classes, printer_classes): |
|
|
|
|
parser.add_argument('--legacy-ast', |
|
|
|
|
help=argparse.SUPPRESS, |
|
|
|
|
action='store_true', |
|
|
|
|
default=False) |
|
|
|
|
default=defaults_flag_in_config['legacy_ast']) |
|
|
|
|
|
|
|
|
|
# if the json is splitted in different files |
|
|
|
|
parser.add_argument('--splitted', |
|
|
|
@ -384,6 +450,19 @@ def parse_args(detector_classes, printer_classes): |
|
|
|
|
|
|
|
|
|
args = parser.parse_args() |
|
|
|
|
|
|
|
|
|
if os.path.isfile(args.config_file): |
|
|
|
|
try: |
|
|
|
|
with open(args.config_file) as f: |
|
|
|
|
config = json.load(f) |
|
|
|
|
for key, elem in config.items(): |
|
|
|
|
if key not in defaults_flag_in_config: |
|
|
|
|
logger.info(yellow('{} has an unknown key: {} : {}'.format(args.config_file, key, elem))) |
|
|
|
|
continue |
|
|
|
|
if getattr(args, key) == defaults_flag_in_config[key]: |
|
|
|
|
setattr(args, key, elem) |
|
|
|
|
except json.decoder.JSONDecodeError as e: |
|
|
|
|
logger.error(red('Impossible to read {}, please check the file {}'.format(args.config_file, e))) |
|
|
|
|
|
|
|
|
|
return args |
|
|
|
|
|
|
|
|
|
class ListDetectors(argparse.Action): |
|
|
|
|