diff --git a/slither/core/slither_core.py b/slither/core/slither_core.py index 01af779164..1f265da56d 100644 --- a/slither/core/slither_core.py +++ b/slither/core/slither_core.py @@ -72,6 +72,7 @@ def __init__(self): self._show_ignored_findings = False self._compilation_units: List[SlitherCompilationUnit] = [] + self._certik_compilation_units: List[SlitherCompilationUnit] = [] self._contracts: List[Contract] = [] self._contracts_derived: List[Contract] = [] @@ -90,6 +91,14 @@ def __init__(self): def compilation_units(self) -> List[SlitherCompilationUnit]: return list(self._compilation_units) + @property + def certik_compilation_units(self) -> List[SlitherCompilationUnit]: + """ + The list of all compilation units generated to include the CertiK version + of the CFG and IR. + """ + return list(self._certik_compilation_units) + def add_compilation_unit(self, compilation_unit: SlitherCompilationUnit): self._compilation_units.append(compilation_unit) diff --git a/slither/detectors/abstract_detector.py b/slither/detectors/abstract_detector.py index 4ebead96af..e805867b7c 100644 --- a/slither/detectors/abstract_detector.py +++ b/slither/detectors/abstract_detector.py @@ -139,6 +139,13 @@ def _log(self, info: str) -> None: if self.logger: self.logger.info(self.color(info)) + @property + def uses_certik_ir() -> bool: + """ + Does this detector expect the CertiK version of SlithIR? + """ + return False + @abc.abstractmethod def _detect(self) -> List[Output]: """TODO Documentation""" diff --git a/slither/printers/all_printers.py b/slither/printers/all_printers.py index 6dc8dddbdd..73939912a8 100644 --- a/slither/printers/all_printers.py +++ b/slither/printers/all_printers.py @@ -5,6 +5,7 @@ from .inheritance.inheritance_graph import PrinterInheritanceGraph from .call.call_graph import PrinterCallGraph from .functions.authorization import PrinterWrittenVariablesAndAuthorization +from .summary.certikir import PrinterCertiKIR from .summary.slithir import PrinterSlithIR from .summary.slithir_ssa import PrinterSlithIRSSA from .summary.human_summary import PrinterHumanSummary diff --git a/slither/printers/summary/certikir.py b/slither/printers/summary/certikir.py new file mode 100644 index 0000000000..248c8eb701 --- /dev/null +++ b/slither/printers/summary/certikir.py @@ -0,0 +1,17 @@ +""" + Module printing summary of the contract using CertiK IR +""" +from typing import List + +from slither.printers.summary.slithir import PrinterSlithIR +from slither.core.compilation_unit import SlitherCompilationUnit + +class PrinterCertiKIR(PrinterSlithIR): + ARGUMENT = "certikir" + HELP = "Print the Certik IR representation of the functions" + + WIKI = "https://github.com/trailofbits/slither/wiki/Printer-documentation#slithir" + + @property + def compilation_units(self) -> List[SlitherCompilationUnit]: + return self.slither.certik_compilation_units diff --git a/slither/printers/summary/slithir.py b/slither/printers/summary/slithir.py index 6f64d76244..a1b95d8f8f 100644 --- a/slither/printers/summary/slithir.py +++ b/slither/printers/summary/slithir.py @@ -3,7 +3,8 @@ """ from slither.core.declarations import Function from slither.printers.abstract_printer import AbstractPrinter - +from typing import List +from slither.core.compilation_unit import SlitherCompilationUnit def _print_function(function: Function) -> str: txt = "" @@ -26,6 +27,13 @@ class PrinterSlithIR(AbstractPrinter): WIKI = "https://github.com/trailofbits/slither/wiki/Printer-documentation#slithir" + @property + def compilation_units(self) -> List[SlitherCompilationUnit]: + """ + List of compilation units to print the IR for + """ + return self.slither.compilation_units + def output(self, _filename): """ _filename is not used @@ -34,7 +42,7 @@ def output(self, _filename): """ txt = "" - for compilation_unit in self.slither.compilation_units: + for compilation_unit in self.compilation_units: for contract in compilation_unit.contracts: if contract.is_top_level: continue diff --git a/slither/slither.py b/slither/slither.py index dcfc0ad7e5..2ed4514194 100644 --- a/slither/slither.py +++ b/slither/slither.py @@ -1,4 +1,5 @@ import logging + from typing import Union, List, ValuesView, Type, Dict from crytic_compile import CryticCompile, InvalidCompilation @@ -93,16 +94,25 @@ def __init__(self, target: Union[str, CryticCompile], **kwargs): except InvalidCompilation as e: # pylint: disable=raise-missing-from raise SlitherError(f"Invalid compilation: \n{str(e)}") + for compilation_unit in crytic_compile.compilation_units.values(): compilation_unit_slither = SlitherCompilationUnit(self, compilation_unit) self._compilation_units.append(compilation_unit_slither) - parser = SlitherCompilationUnitSolc(compilation_unit_slither) + parser = SlitherCompilationUnitSolc(compilation_unit_slither, generates_certik_ir = False) self._parsers.append(parser) + + new_compilation_unit_slither = SlitherCompilationUnit(self, compilation_unit) + self._certik_compilation_units.append(new_compilation_unit_slither) + new_parser = SlitherCompilationUnitSolc(new_compilation_unit_slither, generates_certik_ir = True) + self._parsers.append(new_parser) + for path, ast in compilation_unit.asts.items(): parser.parse_top_level_from_loaded_json(ast, path) + new_parser.parse_top_level_from_loaded_json(ast, path) self.add_source_code(path) _update_file_scopes(compilation_unit_slither.scopes.values()) + _update_file_scopes(new_compilation_unit_slither.scopes.values()) if kwargs.get("generate_patches", False): self.generate_patches = True @@ -186,9 +196,16 @@ def register_detector(self, detector_class: Type[AbstractDetector]) -> None: """ _check_common_things("detector", detector_class, AbstractDetector, self._detectors) - for compilation_unit in self.compilation_units: - instance = detector_class(compilation_unit, self, logger_detector) - self._detectors.append(instance) + if detector_class.uses_certik_ir: + for compilation_unit in self.certik_compilation_units: + instance = detector_class(compilation_unit, self, logger_detector) + self._detectors.append(instance) + else: + for compilation_unit in self.compilation_units: + instance = detector_class(compilation_unit, self, logger_detector) + self._detectors.append(instance) + + def register_printer(self, printer_class: Type[AbstractPrinter]) -> None: """ diff --git a/slither/solc_parsing/slither_compilation_unit_solc.py b/slither/solc_parsing/slither_compilation_unit_solc.py index d12bda1b4a..931fe41f49 100644 --- a/slither/solc_parsing/slither_compilation_unit_solc.py +++ b/slither/solc_parsing/slither_compilation_unit_solc.py @@ -59,12 +59,13 @@ def _handle_import_aliases( class SlitherCompilationUnitSolc: # pylint: disable=no-self-use,too-many-instance-attributes - def __init__(self, compilation_unit: SlitherCompilationUnit): + def __init__(self, compilation_unit: SlitherCompilationUnit, generates_certik_ir: bool): super().__init__() self._contracts_by_id: Dict[int, ContractSolc] = {} self._parsed = False self._analyzed = False + self._generates_certik_ir = generates_certik_ir self._underlying_contract_to_parser: Dict[Contract, ContractSolc] = {} self._structures_top_level_parser: List[StructureTopLevelSolc] = [] @@ -84,6 +85,13 @@ def __init__(self, compilation_unit: SlitherCompilationUnit): def compilation_unit(self) -> SlitherCompilationUnit: return self._compilation_unit + @property + def generates_certik_ir(self) -> bool: + """ + Does this parser generate the CertiK version of SlithIR? + """ + return self._generates_certik_ir + @property def all_functions_and_modifiers_parser(self) -> List[FunctionSolc]: return self._all_functions_and_modifier_parser