Refactor Slither entry point: a Slither object will now detect by itself

if the directory is a truffle or embark one. The advantage is to simplify:
- external scripts and utils (ex: possible-paths/check-upgradability support embark natively)
- __main__.pyn
Changes:
        - Remove is_truffle/is_embark flag
        - Move truffle compile to Sliter._init_from_truffle
        - Refactor help, there are two new option groups: truffle and embark
        - Rename --ignore-truffle-compile to truffle-ignore-compile
pull/201/head
Josselin 6 years ago
parent e531c3ace3
commit ee1b4c251d
  1. 123
      slither/__main__.py
  2. 76
      slither/slither.py
  3. 2
      utils/possible_paths/__main__.py
  4. 6
      utils/upgradability/__main__.py

@ -6,7 +6,6 @@ import inspect
import json import json
import logging import logging
import os import os
import platform
import subprocess import subprocess
import sys import sys
import traceback import traceback
@ -49,6 +48,10 @@ def process(filename, args, detector_classes, printer_classes):
disable_solc_warnings=args.disable_solc_warnings, disable_solc_warnings=args.disable_solc_warnings,
solc_arguments=args.solc_args, solc_arguments=args.solc_args,
ast_format=ast, ast_format=ast,
truffle_build_directory=args.truffle_build_directory,
truffle_ignore_compile=args.truffle_ignore_compile,
truffle_version=args.truffle_version,
embark_overwrite_config=args.embark_overwrite_config,
filter_paths=parse_filter_paths(args), filter_paths=parse_filter_paths(args),
triage_mode=args.triage_mode) triage_mode=args.triage_mode)
@ -76,62 +79,6 @@ def _process(slither, detector_classes, printer_classes):
return results, analyzed_contracts_count return results, analyzed_contracts_count
def process_truffle(dirname, args, detector_classes, printer_classes):
# Truffle on windows has naming conflicts where it will invoke truffle.js directly instead
# of truffle.cmd (unless in powershell or git bash). The cleanest solution is to explicitly call
# truffle.cmd. Reference:
# https://truffleframework.com/docs/truffle/reference/configuration#resolving-naming-conflicts-on-windows
if not args.ignore_truffle_compile:
truffle_base_command = "truffle" if platform.system() != 'Windows' else "truffle.cmd"
cmd = [truffle_base_command, '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,
solc=args.solc,
disable_solc_warnings=args.disable_solc_warnings,
solc_arguments=args.solc_args,
is_truffle=True,
truffle_build_directory=args.truffle_build_directory,
filter_paths=parse_filter_paths(args),
triage_mode=args.triage_mode)
return _process(slither, detector_classes, printer_classes)
def process_embark(dirname, args, detector_classes, printer_classes):
slither = Slither(dirname,
solc=args.solc,
disable_solc_warnings=args.disable_solc_warnings,
solc_arguments=args.solc_args,
is_truffle=False,
is_embark=True,
embark_overwrite_config=args.embark_overwrite_config,
filter_paths=parse_filter_paths(args),
triage_mode=args.triage_mode)
return _process(slither, detector_classes, printer_classes)
def process_files(filenames, args, detector_classes, printer_classes): def process_files(filenames, args, detector_classes, printer_classes):
all_contracts = [] all_contracts = []
@ -299,14 +246,14 @@ defaults_flag_in_config = {
'truffle_version': None, 'truffle_version': None,
'disable_color': False, 'disable_color': False,
'filter_paths': None, 'filter_paths': None,
'ignore_truffle_compile': False, 'truffle_ignore_compile': False,
'truffle_build_directory': 'build/contracts', 'truffle_build_directory': 'build/contracts',
'embark_overwrite_config': False, 'embark_overwrite_config': False,
'legacy_ast': False 'legacy_ast': False
} }
def parse_args(detector_classes, printer_classes): def parse_args(detector_classes, printer_classes):
parser = argparse.ArgumentParser(description='Slither', parser = argparse.ArgumentParser(description='Slither. For usage information, see https://github.com/crytic/slither/wiki/Usage',
usage="slither.py contract.sol [flag]") usage="slither.py contract.sol [flag]")
parser.add_argument('filename', parser.add_argument('filename',
@ -320,6 +267,8 @@ def parse_args(detector_classes, printer_classes):
group_detector = parser.add_argument_group('Detectors') group_detector = parser.add_argument_group('Detectors')
group_printer = parser.add_argument_group('Printers') group_printer = parser.add_argument_group('Printers')
group_solc = parser.add_argument_group('Solc options') group_solc = parser.add_argument_group('Solc options')
group_truffle = parser.add_argument_group('Truffle options')
group_embark = parser.add_argument_group('Embark options')
group_misc = parser.add_argument_group('Additional option') group_misc = parser.add_argument_group('Additional option')
group_detector.add_argument('--detect', group_detector.add_argument('--detect',
@ -396,15 +345,34 @@ def parse_args(detector_classes, printer_classes):
action='store_true', action='store_true',
default=False) default=False)
group_misc.add_argument('--json', group_truffle.add_argument('--truffle-ignore-compile',
help='Export results as JSON', help='Do not run truffle compile',
action='store_true',
dest='truffle_ignore_compile',
default=defaults_flag_in_config['truffle_ignore_compile'])
group_truffle.add_argument('--truffle-build-directory',
help='Do not run truffle compile',
action='store', action='store',
default=defaults_flag_in_config['json']) dest='truffle_build_directory',
group_misc.add_argument('--truffle-version', default=defaults_flag_in_config['truffle_build_directory'])
group_truffle.add_argument('--truffle-version',
help='Use a local Truffle version (with npx)', help='Use a local Truffle version (with npx)',
action='store', action='store',
default=defaults_flag_in_config['truffle_version']) default=defaults_flag_in_config['truffle_version'])
group_embark.add_argument('--embark-overwrite-config',
help='Install @trailofbits/embark-contract-export and add it to embark.json',
action='store_true',
default=defaults_flag_in_config['embark_overwrite_config'])
group_misc.add_argument('--json',
help='Export results as JSON',
action='store',
default=defaults_flag_in_config['json'])
group_misc.add_argument('--disable-color', group_misc.add_argument('--disable-color',
help='Disable output colorization', help='Disable output colorization',
action='store_true', action='store_true',
@ -416,18 +384,6 @@ def parse_args(detector_classes, printer_classes):
dest='filter_paths', dest='filter_paths',
default=defaults_flag_in_config['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('--truffle-build-directory',
help='Do not run truffle compile',
action='store',
dest='truffle_build_directory',
default=defaults_flag_in_config['truffle_build_directory'])
group_misc.add_argument('--triage-mode', group_misc.add_argument('--triage-mode',
help='Run triage mode (save results in slither.db.json)', help='Run triage mode (save results in slither.db.json)',
action='store_true', action='store_true',
@ -457,11 +413,6 @@ def parse_args(detector_classes, printer_classes):
action='store_true', action='store_true',
default=False) default=False)
group_misc.add_argument('--embark-overwrite-config',
help=argparse.SUPPRESS,
action='store_true',
default=defaults_flag_in_config['embark_overwrite_config'])
parser.add_argument('--wiki-detectors', parser.add_argument('--wiki-detectors',
help=argparse.SUPPRESS, help=argparse.SUPPRESS,
action=OutputWiki, action=OutputWiki,
@ -588,15 +539,12 @@ def main_impl(all_detector_classes, all_printer_classes):
globbed_filenames = glob.glob(filename, recursive=True) globbed_filenames = glob.glob(filename, recursive=True)
if os.path.isfile(filename): if os.path.isfile(filename) or\
os.path.isfile(os.path.join(filename, 'truffle.js')) or\
os.path.isfile(os.path.join(filename, 'truffle-config.js')) or\
os.path.isfile(os.path.join(filename, 'embark.json')):
(results, number_contracts) = process(filename, args, detector_classes, printer_classes) (results, number_contracts) = process(filename, args, detector_classes, printer_classes)
elif os.path.isfile(os.path.join(filename, 'truffle.js')) or os.path.isfile(os.path.join(filename, 'truffle-config.js')):
(results, number_contracts) = process_truffle(filename, args, detector_classes, printer_classes)
elif os.path.isfile(os.path.join(filename, 'embark.json')):
(results, number_contracts) = process_embark(filename, args, detector_classes, printer_classes)
elif os.path.isdir(filename) or len(globbed_filenames) > 0: elif os.path.isdir(filename) or len(globbed_filenames) > 0:
extension = "*.sol" if not args.solc_ast else "*.json" extension = "*.sol" if not args.solc_ast else "*.json"
filenames = glob.glob(os.path.join(filename, extension)) filenames = glob.glob(os.path.join(filename, extension))
@ -612,7 +560,6 @@ def main_impl(all_detector_classes, all_printer_classes):
number_contracts += number_contracts_tmp number_contracts += number_contracts_tmp
results += results_tmp results += results_tmp
else: else:
raise Exception("Unrecognised file/dir path: '#{filename}'".format(filename=filename)) raise Exception("Unrecognised file/dir path: '#{filename}'".format(filename=filename))

@ -4,6 +4,7 @@ import subprocess
import sys import sys
import glob import glob
import json import json
import platform
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.printers.abstract_printer import AbstractPrinter from slither.printers.abstract_printer import AbstractPrinter
@ -28,28 +29,36 @@ class Slither(SlitherSolc):
disable_solc_warnings (bool): True to disable solc warnings (default false) disable_solc_warnings (bool): True to disable solc warnings (default false)
solc_argeuments (str): solc arguments (default '') solc_argeuments (str): solc arguments (default '')
ast_format (str): ast format (default '--ast-compact-json') ast_format (str): ast format (default '--ast-compact-json')
is_truffle (bool): is a truffle directory (default false)
truffle_build_directory (str): build truffle directory (default 'build/contracts')
is_embark (bool): is an embark directory (default false)
embark_overwrite_config (bool): overwrite original config file (default false)
filter_paths (list(str)): list of path to filter (default []) filter_paths (list(str)): list of path to filter (default [])
triage_mode (bool): if true, switch to triage mode (default false) triage_mode (bool): if true, switch to triage mode (default false)
'''
is_truffle = kwargs.get('is_truffle', False) truffle_ignore (bool): ignore truffle.js presence (default false)
truffle_build_directory (str): build truffle directory (default 'build/contracts')
truffle_ignore_compile (bool): do not run truffle compile (default False)
truffle_version (str): use a specific truffle version (default None)
is_embark = kwargs.get('is_embark', False) embark_ignore (bool): ignore embark.js presence (default false)
embark_overwrite_config = kwargs.get('embark_overwrite_config', False) embark_overwrite_config (bool): overwrite original config file (default false)
'''
truffle_ignore = kwargs.get('truffle_ignore', False)
embark_ignore = kwargs.get('embark_ignore', False)
# truffle directory
if is_truffle:
self._init_from_truffle(contract, kwargs.get('truffle_build_directory', 'build/contracts'))
# embark directory
elif is_embark:
self._init_from_embark(contract, embark_overwrite_config)
# list of files provided (see --splitted option) # list of files provided (see --splitted option)
elif isinstance(contract, list): if isinstance(contract, list):
self._init_from_list(contract) self._init_from_list(contract)
# truffle directory
elif not truffle_ignore and (os.path.isfile(os.path.join(contract, 'truffle.js')) or
os.path.isfile(os.path.join(contract, 'truffle-config.js'))):
self._init_from_truffle(contract,
kwargs.get('truffle_build_directory', 'build/contracts'),
kwargs.get('truffle_ignore_compile', False),
kwargs.get('truffle_version', None))
# embark directory
elif not embark_ignore and os.path.isfile(os.path.join(contract, 'embark.json')):
self._init_from_embark(contract,
kwargs.get('embark_overwrite_config', False))
# .json or .sol provided # .json or .sol provided
else: else:
self._init_from_solc(contract, **kwargs) self._init_from_solc(contract, **kwargs)
@ -85,7 +94,7 @@ class Slither(SlitherSolc):
with open('embark.json', 'w') as outfile: with open('embark.json', 'w') as outfile:
json.dump(embark_json, outfile, indent=2) json.dump(embark_json, outfile, indent=2)
else: else:
if (not 'plugins' in embark_json) or (not 'embark-contract-info' in embark_json['plugins']): if (not 'plugins' in embark_json) or (not plugin_name in embark_json['plugins']):
logger.error(red('embark-contract-info plugin was found in embark.json. Please install the plugin (see https://github.com/crytic/slither/wiki/Usage#embark), or use --embark-overwrite-config.')) logger.error(red('embark-contract-info plugin was found in embark.json. Please install the plugin (see https://github.com/crytic/slither/wiki/Usage#embark), or use --embark-overwrite-config.'))
sys.exit(-1) sys.exit(-1)
@ -96,7 +105,6 @@ class Slither(SlitherSolc):
# Embark might return information to stderr, but compile without issue # Embark might return information to stderr, but compile without issue
logger.error("%s"%stderr.decode()) logger.error("%s"%stderr.decode())
infile = os.path.join(contract, 'crytic-export', 'contracts.json') infile = os.path.join(contract, 'crytic-export', 'contracts.json')
print(infile)
if not os.path.isfile(infile): if not os.path.isfile(infile):
logger.error(red('Embark did not generate the AST file. Is Embark installed (npm install -g embark)? Is embark-contract-info installed? (npm install -g embark).')) logger.error(red('Embark did not generate the AST file. Is Embark installed (npm install -g embark)? Is embark-contract-info installed? (npm install -g embark).'))
sys.exit(-1) sys.exit(-1)
@ -104,9 +112,39 @@ class Slither(SlitherSolc):
contracts_loaded = json.load(f) contracts_loaded = json.load(f)
contracts_loaded = contracts_loaded['asts'] contracts_loaded = contracts_loaded['asts']
for contract_loaded in contracts_loaded: for contract_loaded in contracts_loaded:
self._parse_contracts_from_loaded_json(contract_loaded, contract_loaded['absolutePath']) self._parse_contracts_from_loaded_json(contract_loaded,
contract_loaded['absolutePath'])
def _init_from_truffle(self, contract, build_directory, truffle_ignore_compile, truffle_version):
# Truffle on windows has naming conflicts where it will invoke truffle.js directly instead
# of truffle.cmd (unless in powershell or git bash). The cleanest solution is to explicitly call
# truffle.cmd. Reference:
# https://truffleframework.com/docs/truffle/reference/configuration#resolving-naming-conflicts-on-windows
if not truffle_ignore_compile:
truffle_base_command = "truffle" if platform.system() != 'Windows' else "truffle.cmd"
cmd = [truffle_base_command, 'compile']
if truffle_version:
cmd = ['npx', 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)
def _init_from_truffle(self, contract, build_directory): 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 os.path.isdir(os.path.join(contract, build_directory)): if not os.path.isdir(os.path.join(contract, build_directory)):
logger.info(red('No truffle build directory found, did you run `truffle compile`?')) logger.info(red('No truffle build directory found, did you run `truffle compile`?'))
sys.exit(-1) sys.exit(-1)

@ -36,7 +36,7 @@ def main():
args = parse_args() args = parse_args()
# Perform slither analysis on the given filename # Perform slither analysis on the given filename
slither = Slither(args.filename, is_truffle=os.path.isdir(args.filename), solc=args.solc, disable_solc_warnings=True) slither = Slither(args.filename, solc=args.solc, disable_solc_warnings=True)
try: try:
targets = resolve_functions(slither, args.targets) targets = resolve_functions(slither, args.targets)

@ -40,11 +40,11 @@ def main():
args = parse_args() args = parse_args()
proxy_filename = vars(args)['proxy.sol'] proxy_filename = vars(args)['proxy.sol']
proxy = Slither(proxy_filename, is_truffle=os.path.isdir(proxy_filename), solc=args.solc, disable_solc_warnings=True) proxy = Slither(proxy_filename, solc=args.solc, disable_solc_warnings=True)
proxy_name = args.ProxyName proxy_name = args.ProxyName
v1_filename = vars(args)['implem.sol'] v1_filename = vars(args)['implem.sol']
v1 = Slither(v1_filename, is_truffle=os.path.isdir(v1_filename), solc=args.solc, disable_solc_warnings=True) v1 = Slither(v1_filename, solc=args.solc, disable_solc_warnings=True)
v1_name = args.ContractName v1_name = args.ContractName
check_initialization(v1) check_initialization(v1)
@ -53,7 +53,7 @@ def main():
compare_function_ids(v1, v1_name, proxy, proxy_name) compare_function_ids(v1, v1_name, proxy, proxy_name)
compare_variables_order_proxy(v1, v1_name, proxy, proxy_name) compare_variables_order_proxy(v1, v1_name, proxy, proxy_name)
else: else:
v2 = Slither(args.new_version, is_truffle=os.path.isdir(args.new_version), solc=args.solc, disable_solc_warnings=True) v2 = Slither(args.new_version, solc=args.solc, disable_solc_warnings=True)
v2_name = v1_name if not args.new_contract_name else args.new_contract_name v2_name = v1_name if not args.new_contract_name else args.new_contract_name
check_initialization(v2) check_initialization(v2)
compare_function_ids(v2, v2_name, proxy, proxy_name) compare_function_ids(v2, v2_name, proxy, proxy_name)

Loading…
Cancel
Save