def main(): args = parse_args() 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_name = args.ProxyName 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_name = args.ContractName check_initialization(v1) if not args.new_version: compare_function_ids(v1, v1_name, proxy, proxy_name) compare_variables_order_proxy(v1, v1_name, proxy, proxy_name) else: v2 = Slither(args.new_version, is_truffle=os.path.isdir(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 check_initialization(v2) compare_function_ids(v2, v2_name, proxy, proxy_name) compare_variables_order_proxy(v2, v2_name, proxy, proxy_name) compare_variables_order_implementation(v1, v1_name, v2, v2_name)
def test_parsing(test_item: Test): flavors = ["compact"] if not test_item.disable_legacy: flavors += ["legacy"] for version, flavor in test_item.versions_with_flavors: test_file = os.path.join( TEST_ROOT, "compile", f"{test_item.test_file}-{version}-{flavor}.zip") expected_file = os.path.join( TEST_ROOT, "expected", f"{test_item.test_file}-{version}-{flavor}.json") cc = load_from_zip(test_file)[0] sl = Slither( cc, solc_force_legacy_json=flavor == "legacy", disallow_partial=True, skip_analyze=True, ) actual = generate_output(sl) try: with open(expected_file, "r", encoding="utf8") as f: expected = json.load(f) except OSError: pytest.xfail("the file for this test was not generated") raise diff = DeepDiff(expected, actual, ignore_order=True, verbose_level=2, view="tree") if diff: for change in diff.get("values_changed", []): path_list = re.findall(r"\['(.*?)'\]", change.path()) path = "_".join(path_list) with open( f"test_artifacts/{test_item.test_file}_{path}_expected.dot", "w", encoding="utf8", ) as f: f.write(change.t1) with open( f"test_artifacts/{test_item.test_file}_{version}_{flavor}_{path}_actual.dot", "w", encoding="utf8", ) as f: f.write(change.t2) assert not diff, diff.pretty() sl = Slither(cc, solc_force_legacy_json=flavor == "legacy", disallow_partial=True) sl.register_printer(Echidna) sl.run_printers()
def test_parsing(test_item: Item): flavor = "legacy" if test_item.is_legacy else "compact" test_file = os.path.join( TEST_ROOT, "compile", f"{test_item.test_id}-{test_item.solc_ver}-{flavor}.zip") expected_file = os.path.join( TEST_ROOT, "expected", f"{test_item.test_id}-{test_item.solc_ver}-{flavor}.json") if id_test(test_item) in XFAIL: pytest.xfail("this test needs to be fixed") # set_solc(test_item) cc = load_from_zip(test_file)[0] sl = Slither( cc, solc_force_legacy_json=test_item.is_legacy, disallow_partial=True, skip_analyze=True, ) actual = generate_output(sl) try: with open(expected_file, "r") as f: expected = json.load(f) except OSError: pytest.xfail("the file for this test was not generated") raise diff = DeepDiff(expected, actual, ignore_order=True, verbose_level=2, view="tree") if diff: for change in diff.get("values_changed", []): path_list = re.findall(r"\['(.*?)'\]", change.path()) path = "_".join(path_list) with open( f"test_artifacts/{id_test(test_item)}_{path}_expected.dot", "w") as f: f.write(change.t1) with open(f"test_artifacts/{id_test(test_item)}_{path}_actual.dot", "w") as f: f.write(change.t2) assert not diff, diff.pretty() sl = Slither(cc, solc_force_legacy_json=test_item.is_legacy, disallow_partial=True) sl.register_printer(Echidna) sl.run_printers()
def main() -> None: args = parse_args() if len(args.contract_source) == 2: # Source code is file.sol or project directory source_code, target = args.contract_source slither = Slither(source_code, **vars(args)) else: # Source code is published and retrieved via etherscan target = args.contract_source[0] slither = Slither(target, **vars(args)) if args.contract_name: contracts = slither.get_contract_from_name(args.contract_name) else: contracts = slither.contracts srs = SlitherReadStorage(contracts, args.max_depth) if args.rpc_url: # Remove target prefix e.g. rinkeby:0x0 -> 0x0. address = target[target.find(":") + 1:] # Default to implementation address unless a storage address is given. if not args.storage_address: args.storage_address = address srs.storage_address = args.storage_address srs.rpc = args.rpc_url if args.layout: srs.get_all_storage_variables() srs.get_storage_layout() else: assert args.variable_name # Use a lambda func to only return variables that have same name as target. # x is a tuple (`Contract`, `StateVariable`). srs.get_all_storage_variables( lambda x: bool(x[1].name == args.variable_name)) srs.get_target_variables(**vars(args)) # To retrieve slot values an rpc url is required. if args.value: assert args.rpc_url srs.get_slot_values() # Only write file if storage layout is used. if len(srs.slot_info) > 1: with open("storage_layout.json", "w", encoding="utf-8") as file: json.dump(srs.slot_info, file, indent=4)
def test_storage_layout(): subprocess.run(["solc-select", "use", "0.8.10"], stdout=subprocess.PIPE, check=True) test_item = os.path.join(STORAGE_TEST_ROOT, "storage_layout-0.8.10.sol") sl = Slither(test_item, solc_force_legacy_json=False, disallow_partial=True) with Popen(["solc", test_item, "--storage-layout"], stdout=PIPE) as process: for line in process.stdout: # parse solc output if '{"storage":[{' in line.decode( "utf-8"): # find the storage layout layout = iter(json.loads(line)["storage"]) while True: try: for contract in sl.contracts: curr_var = next(layout) var_name = curr_var["label"] sl_name = contract.variables_as_dict[var_name] slot, offset = contract.compilation_unit.storage_layout_of( contract, sl_name) assert slot == int(curr_var["slot"]) assert offset == int(curr_var["offset"]) except StopIteration: break except KeyError as e: print(f"not found {e} ")
def encode_contract(cfilename, **kwargs): r = dict() # Init slither try: slither = Slither(cfilename, **kwargs) except: simil_logger.error("Compilation failed for %s using %s", cfilename, kwargs['solc']) return r # Iterate over all the contracts for contract in slither.contracts: # Iterate over all the functions for function in contract.functions_not_inherited: if function.nodes == []: continue x = (cfilename, contract.name, function.name) r[x] = [] # Iterate over the nodes of the function for node in function.nodes: # Print the Solidity expression of the nodes # And the SlithIR operations if node.expression: for ir in node.irs: r[x].append(encode_ir(ir)) return r
def _generate_test(test_item: Test, skip_existing=False): test_dir_path = pathlib.Path( pathlib.Path().absolute(), "tests", "detectors", test_item.detector.ARGUMENT, test_item.solc_ver, ) test_file_path = str(pathlib.Path(test_dir_path, test_item.test_file)) expected_result_path = str(pathlib.Path(test_dir_path, test_item.expected_result).absolute()) if skip_existing: if os.path.isfile(expected_result_path): return set_solc(test_item) sl = Slither(test_file_path) sl.register_detector(test_item.detector) results = sl.run_detectors() results_as_string = json.dumps(results) results_as_string = results_as_string.replace(test_file_path, str(pathlib.Path(GENERIC_PATH))) for additional_file in test_item.additional_files: additional_path = str(pathlib.Path(test_dir_path, additional_file).absolute()) results_as_string = results_as_string.replace( additional_path, str(pathlib.Path(GENERIC_PATH)) ) results = json.loads(results_as_string) with open(expected_result_path, "w") as f: f.write(json.dumps(results, indent=4))
def _generate_test(test_item: Item, skip_existing=False): flavor = "legacy" if test_item.is_legacy else "compact" test_file = os.path.join( TEST_ROOT, "compile", f"{test_item.test_id}-{test_item.solc_ver}-{flavor}.zip" ) expected_file = os.path.join( TEST_ROOT, "expected", f"{test_item.test_id}-{test_item.solc_ver}-{flavor}.json" ) if expected_file in XFAIL: return if skip_existing: if os.path.isfile(expected_file): return if id_test(test_item) in XFAIL: return # set_solc(test_item) try: sl = Slither( test_file, solc_force_legacy_json=test_item.is_legacy, disallow_partial=True, skip_analyze=True, ) # pylint: disable=broad-except except Exception: print(test_item) print(f"{expected_file} failed") return actual = generate_output(sl) print(f"Generate {expected_file}") with open(expected_file, "w") as f: json.dump(actual, f, indent=" ")
def test_detector(test_item: Test): set_solc(test_item) sl = Slither(test_item.test_file) sl.register_detector(test_item.detector) results = sl.run_detectors() with open(test_item.expected_result, encoding="utf8") as f: expected_result = json.load(f) results_as_string = json.dumps(results) current_path = str(pathlib.Path(pathlib.Path().absolute(), test_item.test_file).absolute()) for additional_file in test_item.additional_files: additional_path = str(pathlib.Path(pathlib.Path().absolute(), additional_file).absolute()) results_as_string = results_as_string.replace( additional_path, str(pathlib.Path(GENERIC_PATH)) ) results_as_string = results_as_string.replace(current_path, str(pathlib.Path(GENERIC_PATH))) results = json.loads(results_as_string) diff = DeepDiff(results, expected_result, ignore_order=True, verbose_level=2) if diff: pprint(diff) diff_as_dict = diff.to_dict() if "iterable_item_added" in diff_as_dict: print("#### Findings added") for findings_added in diff_as_dict["iterable_item_added"].values(): for finding_added in findings_added: print(finding_added["description"]) if "iterable_item_removed" in diff_as_dict: print("#### Findings removed") for findings_added in diff_as_dict["iterable_item_removed"].values(): for finding_added in findings_added: print(finding_added["description"]) assert False
def _generate_test(test_item: Item, skip_existing=False): flavor = "legacy" if test_item.is_legacy else "compact" test_file = os.path.join(TEST_ROOT, f"{test_item.test_id}-{test_item.base_ver}.sol") expected_file = os.path.join( TEST_ROOT, "expected", f"{test_item.test_id}-{test_item.solc_ver}-{flavor}.json") if skip_existing: if os.path.isfile(expected_file): return if id_test(test_item) in XFAIL: return set_solc(test_item) sl = Slither( test_file, solc_force_legacy_json=test_item.is_legacy, disallow_partial=True, skip_analyze=True, ) actual = generate_output(sl) print(f"Generate {expected_file}") with open(expected_file, "w") as f: json.dump(actual, f, indent=" ")
def encode_contract(cfilename, **kwargs): r = dict() # Init slither try: slither = Slither(cfilename, **kwargs) except Exception: # pylint: disable=broad-except simil_logger.error("Compilation failed for %s using %s", cfilename, kwargs["solc"]) return r # Iterate over all the contracts for contract in slither.contracts: # Iterate over all the functions for function in contract.functions_declared: if function.nodes == [] or function.is_constructor_variables: continue x = (cfilename, contract.name, function.name) r[x] = [] # Iterate over the nodes of the function for node in function.nodes: # Print the Solidity expression of the nodes # And the SlithIR operations if node.expression: for ir in node.irs: r[x].append(encode_ir(ir)) return r
def main(): args = parse_args() # Perform slither analysis on the given filename slither = Slither(args.project, **vars(args)) ret = defaultdict(list) if args.erc.upper() in ERCS: contract = slither.get_contract_from_name(args.contract_name) if not contract: err = f"Contract not found: {args.contract_name}" _log_error(err, args) return # First elem is the function, second is the event erc = ERCS[args.erc.upper()] generic_erc_checks(contract, erc[0], erc[1], ret) if args.erc.upper() in ADDITIONAL_CHECKS: ADDITIONAL_CHECKS[args.erc.upper()](contract, ret) else: err = f"Incorrect ERC selected {args.erc}" _log_error(err, args) return if args.json: output_to_json(args.json, None, {"upgradeability-check": ret})
def main(): args = parse_args() slither = Slither(args.filename, **vars(args)) flat = Flattening( slither, external_to_public=args.convert_external, remove_assert=args.remove_assert, private_to_internal=args.convert_private, export_path=args.dir, ) try: strategy = Strategy[args.strategy] except KeyError: logger.error( f"{args.strategy} is not a valid strategy, use: {STRATEGIES_NAMES} (default MostDerived)" ) return flat.export( strategy=strategy, target=args.contract, json=args.json, zip=args.zip, zip_type=args.zip_type, )
def main(): args = parse_args() # Perform slither analysis on the given filename _slither = Slither(args.filename, **vars(args)) logger.info("Analysis done!")
def main(): args = parse_args() slither = Slither(args.filename, **vars(args)) flat = Flattening(slither, external_to_public=args.convert_external) flat.export(target=args.contract)
def _generate_test(test_item: Test, skip_existing=False): if skip_existing: if os.path.isfile(test_item.expected_result): return set_solc(test_item) sl = Slither(test_item.test_file) sl.register_detector(test_item.detector) results = sl.run_detectors() results_as_string = json.dumps(results) current_path = str(pathlib.Path(pathlib.Path().absolute(), test_item.test_file).absolute()) results_as_string = results_as_string.replace(current_path, str(pathlib.Path(GENERIC_PATH))) for additional_file in test_item.additional_files: additional_path = str(pathlib.Path(pathlib.Path().absolute(), additional_file).absolute()) results_as_string = results_as_string.replace( additional_path, str(pathlib.Path(GENERIC_PATH)) ) results = json.loads(results_as_string) with open(test_item.expected_result, "w") as f: f.write(json.dumps(results, indent=4))
def test_ternary_conversions() -> None: """This tests that true and false sons define the same number of variables that the father node declares""" slither = Slither("./tests/slithir/ternary_expressions.sol") for contract in slither.contracts: for function in contract.functions: for node in function.nodes: if node.type in [NodeType.IF, NodeType.IFLOOP]: vars_declared = 0 vars_assigned = 0 # Iterate over true and false son for inner_node in node.sons: # Count all variables declared expression = inner_node.expression if isinstance(expression, AssignmentOperation): var_expr = expression.expression_left # Only tuples declare more than one var if isinstance(var_expr, TupleExpression): vars_declared += len(var_expr.expressions) else: vars_declared += 1 for ir in inner_node.irs: # Count all variables defined if isinstance(ir, Assignment): vars_assigned += 1 assert vars_declared == vars_assigned
def main(): args = parse_args() slither = Slither(args.filename, is_truffle=os.path.isdir(args.filename), solc=args.solc) slither.register_printer(PrinterCallGraphStateChange) slither.run_printers()
def kspec_coverage(args): contract = args.contract kspec = args.kspec slither = Slither(contract, **vars(args)) # Run the analysis on the Klab specs run_analysis(args, slither, kspec)
def main(): args = parse_args() slither = Slither(args.filename, **vars(args)) flat = Flattening(slither, external_to_public=args.convert_external, remove_assert=args.remove_assert, private_to_internal=args.convert_private) flat.export(target=args.contract)
def main(): args = parse_args() print(args.codebase) sl = Slither(args.codebase, **vars(args)) for M in _get_mutators(): m = M(sl) m.mutate()
def main(fname): try: slither = Slither(fname) with open(fname, "r") as src_file: src = ''.join(src_file.readlines()) for contract in slither.contracts: for function in contract.functions_declared: extract_loops(contract, function, src) except Exception as e: print("FAILED TO RUN: {0}".format(fname)) print(e)
def do_filter_contract(self): """ 判断当前合约是否具有交易功能 """ slither = Slither(self.file_name) for contract in slither.contracts: for function in contract.functions: if function.can_send_eth(): # 只分析存在交易行为的函数 return True return False
def kspec_coverage(args): contract = args.contract kspec = args.kspec slither = Slither(contract, **vars(args)) compilation_units = slither.compilation_units if len(compilation_units) != 1: print("Only single compilation unit supported") return # Run the analysis on the Klab specs run_analysis(args, compilation_units[0], kspec)
def do_analyze_a_file_for_cfg_pdg(self): if self.cfg_filter is None: return print("使用过滤器:", self.cfg_filter) slither = Slither(self.file_name) for contract in slither.contracts: # 构建过滤器 contract_filter = {} if contract.name in self.cfg_filter: print("匹配到过滤器合约:{}".format(contract.name)) for target_function in self.cfg_filter[contract.name]: contract_filter[target_function] = 1 else: continue contract_info = ContractInfo(contract) # 1.合约信息抽取 for function in contract.functions: if function.name in contract_filter: print("【********】开始分析: {}".format(function.name)) # 简易流程,目标是获得CFG 和 PDG function_info = FunctionInfo(contract_info, function, test_mode=0, simple=1) # 函数对象 if function_info.cfg is None: continue # 没有cfg control_flow_analyzer = ControlFlowAnalyzer( contract_info, function_info) # 当前函数的控制流分析器 data_flow_analyzer = DataFlowAnalyzer( contract_info, function_info) # 当前函数的数据流分析器 code_constructor = CodeGraphConstructor(contract_info, function_info, mode=0) # 代码图表示构建器 # PDG分析 control_flow_analyzer.do_control_dependency_analyze( ) # 控制流分析 data_flow_analyzer.do_data_semantic_analyze() # 数据流分析 # 获得psc表示 function_info.save_psc_to_file() # 获得CFG和PDG code_constructor.get_cfg_and_pdg()
def main(): # ------------------------------ # PossiblePaths.py # Usage: python3 possible_paths.py filename targets # Example: python3 possible_paths.py contract.sol contract1.function1 contract2.function2 contract3.function3 # ------------------------------ # Parse all arguments args = parse_args() # 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) try: targets = resolve_functions(slither, args.targets) except ResolveFunctionException as r: print(red(r)) exit(-1) # Print out all target functions. print(f"Target functions:") for target in targets: print(f"- {target.contract.name}.{target.full_name}") print("\n") # Obtain all paths which reach the target functions. reaching_paths = find_target_paths(slither, targets) reaching_functions = set( [y for x in reaching_paths for y in x if y not in targets]) # Print out all function names which can reach the targets. print(f"The following functions reach the specified targets:") for function_desc in sorted( [f"{f.contract.name}.{f.full_name}" for f in reaching_functions]): print(f"- {function_desc}") print("\n") # Format all function paths. reaching_paths_str = [ ' -> '.join( [f"{f.contract.name}.{f.full_name}" for f in reaching_path]) for reaching_path in reaching_paths ] # Print a sorted list of all function paths which can reach the targets. print(f"The following paths reach the specified targets:") for reaching_path in sorted(reaching_paths_str): print(f"{reaching_path}\n")
def main(): args = parse_args() proxy = Slither(vars(args)['proxy.sol'], solc=args.solc) proxy_name = args.ProxyName v1 = Slither(vars(args)['implem.sol'], solc=args.solc) v1_name = args.ContractName last_contract = v1 last_name = v1_name if args.new_version: v2 = Slither(args.new_version, solc=args.solc) last_contract = v2 if args.new_contract_name: last_name = args.new_contract_name compare_function_ids(last_contract, proxy) compare_variables_order_proxy(last_contract, last_name, proxy, proxy_name) if args.new_version: compare_variables_order_implementation(v1, v1_name, v2, last_name)
def main(): args = parse_args() proxy_filename = vars(args)['proxy.sol'] proxy = Slither(proxy_filename, **vars(args)) proxy_name = args.ProxyName v1_filename = vars(args)['implem.sol'] v1 = Slither(v1_filename, **vars(args)) v1_name = args.ContractName check_initialization(v1) if not args.new_version: compare_function_ids(v1, v1_name, proxy, proxy_name) compare_variables_order_proxy(v1, v1_name, proxy, proxy_name) else: v2 = Slither(args.new_version, **vars(args)) v2_name = v1_name if not args.new_contract_name else args.new_contract_name check_initialization(v2) compare_function_ids(v2, v2_name, proxy, proxy_name) compare_variables_order_proxy(v2, v2_name, proxy, proxy_name) compare_variables_order_implementation(v1, v1_name, v2, v2_name)
def main(): # ------------------------------ # Usage: python3 -m slither_format filename # Example: python3 -m slither_format contract.sol # ------------------------------ # Parse all arguments args = parse_args() read_config_file(args) # Perform slither analysis on the given filename slither = Slither(args.filename, **vars(args)) # Format the input files based on slither analysis slither_format(slither, **vars(args))
def main(): # ------------------------------ # PossiblePaths.py # Usage: python3 possible_paths.py filename targets # Example: python3 possible_paths.py contract.sol contract1.function1 contract2.function2 contract3.function3 # ------------------------------ # Parse all arguments args = parse_args() # Perform slither analysis on the given filename slither = Slither(args.filename, **vars(args)) try: targets = resolve_functions(slither, args.targets) except ResolveFunctionException as resolvefunction: print(red(resolvefunction)) sys.exit(-1) # Print out all target functions. print("Target functions:") for target in targets: print(f"- {target.contract_declarer.name}.{target.full_name}") print("\n") # Obtain all paths which reach the target functions. reaching_paths = find_target_paths(slither, targets) reaching_functions = { y for x in reaching_paths for y in x if y not in targets } # Print out all function names which can reach the targets. print("The following functions reach the specified targets:") for function_desc in sorted( [f"{f.canonical_name}" for f in reaching_functions]): print(f"- {function_desc}") print("\n") # Format all function paths. reaching_paths_str = [ " -> ".join([f"{f.canonical_name}" for f in reaching_path]) for reaching_path in reaching_paths ] # Print a sorted list of all function paths which can reach the targets. print("The following paths reach the specified targets:") for reaching_path in sorted(reaching_paths_str): print(f"{reaching_path}\n")