slither.slither

  1import logging
  2
  3from crytic_compile import CryticCompile, InvalidCompilation
  4
  5
  6from slither.core.compilation_unit import SlitherCompilationUnit
  7from slither.core.slither_core import SlitherCore
  8from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
  9from slither.exceptions import SlitherError
 10from slither.printers.abstract_printer import AbstractPrinter
 11from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc
 12from slither.vyper_parsing.vyper_compilation_unit import VyperCompilationUnit
 13from slither.utils.output import Output
 14from slither.vyper_parsing.ast.ast import parse
 15
 16logger = logging.getLogger("Slither")
 17logging.basicConfig()
 18
 19logger_detector = logging.getLogger("Detectors")
 20logger_printer = logging.getLogger("Printers")
 21
 22
 23def _check_common_things(
 24    thing_name: str, cls: type, base_cls: type, instances_list: list[type[AbstractDetector]]
 25) -> None:
 26    if not issubclass(cls, base_cls) or cls is base_cls:
 27        raise SlitherError(
 28            f"You can't register {cls!r} as a {thing_name}. You need to pass a class that inherits from {base_cls.__name__}"
 29        )
 30
 31    if any(type(obj) is cls for obj in instances_list):
 32        raise SlitherError(f"You can't register {cls!r} twice.")
 33
 34
 35def _update_file_scopes(
 36    sol_parser: SlitherCompilationUnitSolc,
 37):
 38    """
 39    Since all definitions in a file are exported by default, including definitions from its (transitive) dependencies,
 40    we can identify all top level items that could possibly be referenced within the file from its exportedSymbols.
 41    It is not as straightforward for user defined types and functions as well as aliasing. See add_accessible_scopes for more details.
 42    """
 43    candidates = sol_parser.compilation_unit.scopes.values()
 44    learned_something = False
 45    # Because solc's import allows cycle in the import graph, iterate until we aren't adding new information to the scope.
 46    while True:
 47        for candidate in candidates:
 48            learned_something |= candidate.add_accessible_scopes()
 49        if not learned_something:
 50            break
 51        learned_something = False
 52
 53    for scope in candidates:
 54        for refId in scope.exported_symbols:
 55            if refId in sol_parser.contracts_by_id:
 56                contract = sol_parser.contracts_by_id[refId]
 57                scope.contracts[contract.name] = contract
 58            elif refId in sol_parser.functions_by_id:
 59                functions = sol_parser.functions_by_id[refId]
 60                assert len(functions) == 1
 61                scope.functions.add(functions[0])
 62            elif refId in sol_parser.imports_by_id:
 63                import_directive = sol_parser.imports_by_id[refId]
 64                scope.imports.add(import_directive)
 65            elif refId in sol_parser.top_level_variables_by_id:
 66                top_level_variable = sol_parser.top_level_variables_by_id[refId]
 67                scope.variables[top_level_variable.name] = top_level_variable
 68            elif refId in sol_parser.top_level_events_by_id:
 69                top_level_event = sol_parser.top_level_events_by_id[refId]
 70                scope.events.add(top_level_event)
 71            elif refId in sol_parser.top_level_structures_by_id:
 72                top_level_struct = sol_parser.top_level_structures_by_id[refId]
 73                scope.structures[top_level_struct.name] = top_level_struct
 74            elif refId in sol_parser.top_level_type_aliases_by_id:
 75                top_level_type_alias = sol_parser.top_level_type_aliases_by_id[refId]
 76                scope.type_aliases[top_level_type_alias.name] = top_level_type_alias
 77            elif refId in sol_parser.top_level_enums_by_id:
 78                top_level_enum = sol_parser.top_level_enums_by_id[refId]
 79                scope.enums[top_level_enum.name] = top_level_enum
 80            elif refId in sol_parser.top_level_errors_by_id:
 81                top_level_custom_error = sol_parser.top_level_errors_by_id[refId]
 82                scope.custom_errors.add(top_level_custom_error)
 83            else:
 84                logger.error(
 85                    f"Failed to resolved name for reference id {refId} in {scope.filename.absolute}."
 86                )
 87
 88
 89class Slither(SlitherCore):
 90    def __init__(self, target: str | CryticCompile, **kwargs) -> None:
 91        """
 92        Args:
 93            target (str | CryticCompile)
 94        Keyword Args:
 95            solc (str): solc binary location (default 'solc')
 96            disable_solc_warnings (bool): True to disable solc warnings (default false)
 97            solc_args (str): solc arguments (default '')
 98            ast_format (str): ast format (default '--ast-compact-json')
 99            filter_paths (list(str)): list of path to filter (default [])
100            triage_mode (bool): if true, switch to triage mode (default false)
101            exclude_dependencies (bool): if true, exclude results that are only related to dependencies
102            generate_patches (bool): if true, patches are generated (json output only)
103            change_line_prefix (str): Change the line prefix (default #)
104                for the displayed source codes (i.e. file.sol#1).
105
106        """
107        super().__init__()
108
109        self._disallow_partial: bool = kwargs.get("disallow_partial", False)
110        self._skip_assembly: bool = kwargs.get("skip_assembly", False)
111        self._show_ignored_findings: bool = kwargs.get("show_ignored_findings", False)
112
113        self.line_prefix = kwargs.get("change_line_prefix", "#")
114
115        # Indicate if Codex related features should be used
116        self.codex_enabled = kwargs.get("codex", False)
117        self.codex_contracts = kwargs.get("codex_contracts", "all")
118        self.codex_model = kwargs.get("codex_model", "text-davinci-003")
119        self.codex_temperature = kwargs.get("codex_temperature", 0)
120        self.codex_max_tokens = kwargs.get("codex_max_tokens", 300)
121        self.codex_log = kwargs.get("codex_log", False)
122        self.codex_organization: str | None = kwargs.get("codex_organization")
123
124        self.no_fail = kwargs.get("no_fail", False)
125
126        self._parsers: list[SlitherCompilationUnitSolc] = []
127        try:
128            if isinstance(target, CryticCompile):
129                crytic_compile = target
130            else:
131                crytic_compile = CryticCompile(target, **kwargs)
132            self._crytic_compile = crytic_compile
133        except InvalidCompilation as e:
134            raise SlitherError(f"Invalid compilation: \n{e!s}")
135        for compilation_unit in crytic_compile.compilation_units.values():
136            compilation_unit_slither = SlitherCompilationUnit(self, compilation_unit)
137            self._compilation_units.append(compilation_unit_slither)
138
139            if compilation_unit_slither.is_vyper:
140                vyper_parser = VyperCompilationUnit(compilation_unit_slither)
141                for path, ast in compilation_unit.asts.items():
142                    ast_nodes = parse(ast["ast"])
143                    vyper_parser.parse_module(ast_nodes, path)
144                self._parsers.append(vyper_parser)
145            else:
146                # Solidity specific
147                assert compilation_unit_slither.is_solidity
148                sol_parser = SlitherCompilationUnitSolc(compilation_unit_slither)
149                self._parsers.append(sol_parser)
150                for path, ast in compilation_unit.asts.items():
151                    sol_parser.parse_top_level_items(ast, path)
152                    self.add_source_code(path)
153
154                for contract in sol_parser._underlying_contract_to_parser:
155                    if contract.name.startswith("SlitherInternalTopLevelContract"):
156                        raise SlitherError(
157                            # region multi-line-string
158                            """Your codebase has a contract named 'SlitherInternalTopLevelContract'.
159        Please rename it, this name is reserved for Slither's internals"""
160                            # endregion multi-line
161                        )
162                    sol_parser._contracts_by_id[contract.id] = contract
163                    sol_parser._compilation_unit.contracts.append(contract)
164
165                _update_file_scopes(sol_parser)
166
167        if kwargs.get("generate_patches", False):
168            self.generate_patches = True
169
170        self._markdown_root = kwargs.get("markdown_root", "")
171
172        self._detectors = []
173        self._printers = []
174
175        filter_paths = kwargs.get("filter_paths", [])
176        for p in filter_paths:
177            self.add_path_to_filter(p)
178
179        include_paths = kwargs.get("include_paths", [])
180        for p in include_paths:
181            self.add_path_to_include(p)
182
183        self._exclude_dependencies = kwargs.get("exclude_dependencies", False)
184
185        triage_mode = kwargs.get("triage_mode", False)
186        triage_database = kwargs.get("triage_database", "slither.db.json")
187        self._triage_mode = triage_mode
188        self._previous_results_filename = triage_database
189
190        printers_to_run = kwargs.get("printers_to_run", "")
191        if printers_to_run == "echidna":
192            self.skip_data_dependency = True
193
194        # Used in inheritance-graph printer
195        self.include_interfaces = kwargs.get("include_interfaces", False)
196
197        self._init_parsing_and_analyses(kwargs.get("skip_analyze", False))
198
199    def _init_parsing_and_analyses(self, skip_analyze: bool) -> None:
200        from slither.utils.timing import PhaseTimer
201
202        timer = PhaseTimer.get()
203
204        for parser in self._parsers:
205            try:
206                with timer.phase("parse_contracts"):
207                    parser.parse_contracts()
208            except Exception as e:
209                if self.no_fail:
210                    continue
211                raise e
212
213        # skip_analyze is only used for testing
214        if not skip_analyze:
215            for parser in self._parsers:
216                try:
217                    with timer.phase("analyze_contracts"):
218                        parser.analyze_contracts()
219                except Exception as e:
220                    if self.no_fail:
221                        continue
222                    raise e
223
224    @property
225    def detectors(self):
226        return self._detectors
227
228    @property
229    def detectors_high(self):
230        return [d for d in self.detectors if d.IMPACT == DetectorClassification.HIGH]
231
232    @property
233    def detectors_medium(self):
234        return [d for d in self.detectors if d.IMPACT == DetectorClassification.MEDIUM]
235
236    @property
237    def detectors_low(self):
238        return [d for d in self.detectors if d.IMPACT == DetectorClassification.LOW]
239
240    @property
241    def detectors_informational(self):
242        return [d for d in self.detectors if d.IMPACT == DetectorClassification.INFORMATIONAL]
243
244    @property
245    def detectors_optimization(self):
246        return [d for d in self.detectors if d.IMPACT == DetectorClassification.OPTIMIZATION]
247
248    def register_detector(self, detector_class: type[AbstractDetector]) -> None:
249        """
250        :param detector_class: Class inheriting from `AbstractDetector`.
251        """
252        _check_common_things("detector", detector_class, AbstractDetector, self._detectors)
253
254        for compilation_unit in self.compilation_units:
255            instance = detector_class(compilation_unit, self, logger_detector)
256            self._detectors.append(instance)
257
258    def unregister_detector(self, detector_class: type[AbstractDetector]) -> None:
259        """
260        :param detector_class: Class inheriting from `AbstractDetector`.
261        """
262
263        for obj in self._detectors:
264            if isinstance(obj, detector_class):
265                self._detectors.remove(obj)
266                return
267
268    def register_printer(self, printer_class: type[AbstractPrinter]) -> None:
269        """
270        :param printer_class: Class inheriting from `AbstractPrinter`.
271        """
272        _check_common_things("printer", printer_class, AbstractPrinter, self._printers)
273
274        instance = printer_class(self, logger_printer)
275        self._printers.append(instance)
276
277    def unregister_printer(self, printer_class: type[AbstractPrinter]) -> None:
278        """
279        :param printer_class: Class inheriting from `AbstractPrinter`.
280        """
281
282        for obj in self._printers:
283            if isinstance(obj, printer_class):
284                self._printers.remove(obj)
285                return
286
287    def run_detectors(self) -> list[dict]:
288        """
289        :return: List of registered detectors results.
290        """
291        from slither.utils.timing import PhaseTimer
292
293        timer = PhaseTimer.get()
294
295        self.load_previous_results()
296        results = []
297        for d in self._detectors:
298            with timer.phase(f"detector:{d.ARGUMENT}"):
299                results.append(d.detect())
300
301        self.write_results_to_hide()
302        return results
303
304    def run_printers(self) -> list[Output]:
305        """
306        :return: List of registered printers outputs.
307        """
308
309        return [p.output(self._crytic_compile.target).data for p in self._printers]
310
311    @property
312    def triage_mode(self) -> bool:
313        return self._triage_mode
logger = <Logger Slither (WARNING)>
logger_detector = <Logger Detectors (WARNING)>
logger_printer = <Logger Printers (WARNING)>
class Slither(slither.core.slither_core.SlitherCore):
 90class Slither(SlitherCore):
 91    def __init__(self, target: str | CryticCompile, **kwargs) -> None:
 92        """
 93        Args:
 94            target (str | CryticCompile)
 95        Keyword Args:
 96            solc (str): solc binary location (default 'solc')
 97            disable_solc_warnings (bool): True to disable solc warnings (default false)
 98            solc_args (str): solc arguments (default '')
 99            ast_format (str): ast format (default '--ast-compact-json')
100            filter_paths (list(str)): list of path to filter (default [])
101            triage_mode (bool): if true, switch to triage mode (default false)
102            exclude_dependencies (bool): if true, exclude results that are only related to dependencies
103            generate_patches (bool): if true, patches are generated (json output only)
104            change_line_prefix (str): Change the line prefix (default #)
105                for the displayed source codes (i.e. file.sol#1).
106
107        """
108        super().__init__()
109
110        self._disallow_partial: bool = kwargs.get("disallow_partial", False)
111        self._skip_assembly: bool = kwargs.get("skip_assembly", False)
112        self._show_ignored_findings: bool = kwargs.get("show_ignored_findings", False)
113
114        self.line_prefix = kwargs.get("change_line_prefix", "#")
115
116        # Indicate if Codex related features should be used
117        self.codex_enabled = kwargs.get("codex", False)
118        self.codex_contracts = kwargs.get("codex_contracts", "all")
119        self.codex_model = kwargs.get("codex_model", "text-davinci-003")
120        self.codex_temperature = kwargs.get("codex_temperature", 0)
121        self.codex_max_tokens = kwargs.get("codex_max_tokens", 300)
122        self.codex_log = kwargs.get("codex_log", False)
123        self.codex_organization: str | None = kwargs.get("codex_organization")
124
125        self.no_fail = kwargs.get("no_fail", False)
126
127        self._parsers: list[SlitherCompilationUnitSolc] = []
128        try:
129            if isinstance(target, CryticCompile):
130                crytic_compile = target
131            else:
132                crytic_compile = CryticCompile(target, **kwargs)
133            self._crytic_compile = crytic_compile
134        except InvalidCompilation as e:
135            raise SlitherError(f"Invalid compilation: \n{e!s}")
136        for compilation_unit in crytic_compile.compilation_units.values():
137            compilation_unit_slither = SlitherCompilationUnit(self, compilation_unit)
138            self._compilation_units.append(compilation_unit_slither)
139
140            if compilation_unit_slither.is_vyper:
141                vyper_parser = VyperCompilationUnit(compilation_unit_slither)
142                for path, ast in compilation_unit.asts.items():
143                    ast_nodes = parse(ast["ast"])
144                    vyper_parser.parse_module(ast_nodes, path)
145                self._parsers.append(vyper_parser)
146            else:
147                # Solidity specific
148                assert compilation_unit_slither.is_solidity
149                sol_parser = SlitherCompilationUnitSolc(compilation_unit_slither)
150                self._parsers.append(sol_parser)
151                for path, ast in compilation_unit.asts.items():
152                    sol_parser.parse_top_level_items(ast, path)
153                    self.add_source_code(path)
154
155                for contract in sol_parser._underlying_contract_to_parser:
156                    if contract.name.startswith("SlitherInternalTopLevelContract"):
157                        raise SlitherError(
158                            # region multi-line-string
159                            """Your codebase has a contract named 'SlitherInternalTopLevelContract'.
160        Please rename it, this name is reserved for Slither's internals"""
161                            # endregion multi-line
162                        )
163                    sol_parser._contracts_by_id[contract.id] = contract
164                    sol_parser._compilation_unit.contracts.append(contract)
165
166                _update_file_scopes(sol_parser)
167
168        if kwargs.get("generate_patches", False):
169            self.generate_patches = True
170
171        self._markdown_root = kwargs.get("markdown_root", "")
172
173        self._detectors = []
174        self._printers = []
175
176        filter_paths = kwargs.get("filter_paths", [])
177        for p in filter_paths:
178            self.add_path_to_filter(p)
179
180        include_paths = kwargs.get("include_paths", [])
181        for p in include_paths:
182            self.add_path_to_include(p)
183
184        self._exclude_dependencies = kwargs.get("exclude_dependencies", False)
185
186        triage_mode = kwargs.get("triage_mode", False)
187        triage_database = kwargs.get("triage_database", "slither.db.json")
188        self._triage_mode = triage_mode
189        self._previous_results_filename = triage_database
190
191        printers_to_run = kwargs.get("printers_to_run", "")
192        if printers_to_run == "echidna":
193            self.skip_data_dependency = True
194
195        # Used in inheritance-graph printer
196        self.include_interfaces = kwargs.get("include_interfaces", False)
197
198        self._init_parsing_and_analyses(kwargs.get("skip_analyze", False))
199
200    def _init_parsing_and_analyses(self, skip_analyze: bool) -> None:
201        from slither.utils.timing import PhaseTimer
202
203        timer = PhaseTimer.get()
204
205        for parser in self._parsers:
206            try:
207                with timer.phase("parse_contracts"):
208                    parser.parse_contracts()
209            except Exception as e:
210                if self.no_fail:
211                    continue
212                raise e
213
214        # skip_analyze is only used for testing
215        if not skip_analyze:
216            for parser in self._parsers:
217                try:
218                    with timer.phase("analyze_contracts"):
219                        parser.analyze_contracts()
220                except Exception as e:
221                    if self.no_fail:
222                        continue
223                    raise e
224
225    @property
226    def detectors(self):
227        return self._detectors
228
229    @property
230    def detectors_high(self):
231        return [d for d in self.detectors if d.IMPACT == DetectorClassification.HIGH]
232
233    @property
234    def detectors_medium(self):
235        return [d for d in self.detectors if d.IMPACT == DetectorClassification.MEDIUM]
236
237    @property
238    def detectors_low(self):
239        return [d for d in self.detectors if d.IMPACT == DetectorClassification.LOW]
240
241    @property
242    def detectors_informational(self):
243        return [d for d in self.detectors if d.IMPACT == DetectorClassification.INFORMATIONAL]
244
245    @property
246    def detectors_optimization(self):
247        return [d for d in self.detectors if d.IMPACT == DetectorClassification.OPTIMIZATION]
248
249    def register_detector(self, detector_class: type[AbstractDetector]) -> None:
250        """
251        :param detector_class: Class inheriting from `AbstractDetector`.
252        """
253        _check_common_things("detector", detector_class, AbstractDetector, self._detectors)
254
255        for compilation_unit in self.compilation_units:
256            instance = detector_class(compilation_unit, self, logger_detector)
257            self._detectors.append(instance)
258
259    def unregister_detector(self, detector_class: type[AbstractDetector]) -> None:
260        """
261        :param detector_class: Class inheriting from `AbstractDetector`.
262        """
263
264        for obj in self._detectors:
265            if isinstance(obj, detector_class):
266                self._detectors.remove(obj)
267                return
268
269    def register_printer(self, printer_class: type[AbstractPrinter]) -> None:
270        """
271        :param printer_class: Class inheriting from `AbstractPrinter`.
272        """
273        _check_common_things("printer", printer_class, AbstractPrinter, self._printers)
274
275        instance = printer_class(self, logger_printer)
276        self._printers.append(instance)
277
278    def unregister_printer(self, printer_class: type[AbstractPrinter]) -> None:
279        """
280        :param printer_class: Class inheriting from `AbstractPrinter`.
281        """
282
283        for obj in self._printers:
284            if isinstance(obj, printer_class):
285                self._printers.remove(obj)
286                return
287
288    def run_detectors(self) -> list[dict]:
289        """
290        :return: List of registered detectors results.
291        """
292        from slither.utils.timing import PhaseTimer
293
294        timer = PhaseTimer.get()
295
296        self.load_previous_results()
297        results = []
298        for d in self._detectors:
299            with timer.phase(f"detector:{d.ARGUMENT}"):
300                results.append(d.detect())
301
302        self.write_results_to_hide()
303        return results
304
305    def run_printers(self) -> list[Output]:
306        """
307        :return: List of registered printers outputs.
308        """
309
310        return [p.output(self._crytic_compile.target).data for p in self._printers]
311
312    @property
313    def triage_mode(self) -> bool:
314        return self._triage_mode

Slither static analyzer

Slither(target: str | crytic_compile.crytic_compile.CryticCompile, **kwargs)
 91    def __init__(self, target: str | CryticCompile, **kwargs) -> None:
 92        """
 93        Args:
 94            target (str | CryticCompile)
 95        Keyword Args:
 96            solc (str): solc binary location (default 'solc')
 97            disable_solc_warnings (bool): True to disable solc warnings (default false)
 98            solc_args (str): solc arguments (default '')
 99            ast_format (str): ast format (default '--ast-compact-json')
100            filter_paths (list(str)): list of path to filter (default [])
101            triage_mode (bool): if true, switch to triage mode (default false)
102            exclude_dependencies (bool): if true, exclude results that are only related to dependencies
103            generate_patches (bool): if true, patches are generated (json output only)
104            change_line_prefix (str): Change the line prefix (default #)
105                for the displayed source codes (i.e. file.sol#1).
106
107        """
108        super().__init__()
109
110        self._disallow_partial: bool = kwargs.get("disallow_partial", False)
111        self._skip_assembly: bool = kwargs.get("skip_assembly", False)
112        self._show_ignored_findings: bool = kwargs.get("show_ignored_findings", False)
113
114        self.line_prefix = kwargs.get("change_line_prefix", "#")
115
116        # Indicate if Codex related features should be used
117        self.codex_enabled = kwargs.get("codex", False)
118        self.codex_contracts = kwargs.get("codex_contracts", "all")
119        self.codex_model = kwargs.get("codex_model", "text-davinci-003")
120        self.codex_temperature = kwargs.get("codex_temperature", 0)
121        self.codex_max_tokens = kwargs.get("codex_max_tokens", 300)
122        self.codex_log = kwargs.get("codex_log", False)
123        self.codex_organization: str | None = kwargs.get("codex_organization")
124
125        self.no_fail = kwargs.get("no_fail", False)
126
127        self._parsers: list[SlitherCompilationUnitSolc] = []
128        try:
129            if isinstance(target, CryticCompile):
130                crytic_compile = target
131            else:
132                crytic_compile = CryticCompile(target, **kwargs)
133            self._crytic_compile = crytic_compile
134        except InvalidCompilation as e:
135            raise SlitherError(f"Invalid compilation: \n{e!s}")
136        for compilation_unit in crytic_compile.compilation_units.values():
137            compilation_unit_slither = SlitherCompilationUnit(self, compilation_unit)
138            self._compilation_units.append(compilation_unit_slither)
139
140            if compilation_unit_slither.is_vyper:
141                vyper_parser = VyperCompilationUnit(compilation_unit_slither)
142                for path, ast in compilation_unit.asts.items():
143                    ast_nodes = parse(ast["ast"])
144                    vyper_parser.parse_module(ast_nodes, path)
145                self._parsers.append(vyper_parser)
146            else:
147                # Solidity specific
148                assert compilation_unit_slither.is_solidity
149                sol_parser = SlitherCompilationUnitSolc(compilation_unit_slither)
150                self._parsers.append(sol_parser)
151                for path, ast in compilation_unit.asts.items():
152                    sol_parser.parse_top_level_items(ast, path)
153                    self.add_source_code(path)
154
155                for contract in sol_parser._underlying_contract_to_parser:
156                    if contract.name.startswith("SlitherInternalTopLevelContract"):
157                        raise SlitherError(
158                            # region multi-line-string
159                            """Your codebase has a contract named 'SlitherInternalTopLevelContract'.
160        Please rename it, this name is reserved for Slither's internals"""
161                            # endregion multi-line
162                        )
163                    sol_parser._contracts_by_id[contract.id] = contract
164                    sol_parser._compilation_unit.contracts.append(contract)
165
166                _update_file_scopes(sol_parser)
167
168        if kwargs.get("generate_patches", False):
169            self.generate_patches = True
170
171        self._markdown_root = kwargs.get("markdown_root", "")
172
173        self._detectors = []
174        self._printers = []
175
176        filter_paths = kwargs.get("filter_paths", [])
177        for p in filter_paths:
178            self.add_path_to_filter(p)
179
180        include_paths = kwargs.get("include_paths", [])
181        for p in include_paths:
182            self.add_path_to_include(p)
183
184        self._exclude_dependencies = kwargs.get("exclude_dependencies", False)
185
186        triage_mode = kwargs.get("triage_mode", False)
187        triage_database = kwargs.get("triage_database", "slither.db.json")
188        self._triage_mode = triage_mode
189        self._previous_results_filename = triage_database
190
191        printers_to_run = kwargs.get("printers_to_run", "")
192        if printers_to_run == "echidna":
193            self.skip_data_dependency = True
194
195        # Used in inheritance-graph printer
196        self.include_interfaces = kwargs.get("include_interfaces", False)
197
198        self._init_parsing_and_analyses(kwargs.get("skip_analyze", False))

Args: target (str | CryticCompile) Keyword Args: solc (str): solc binary location (default 'solc') disable_solc_warnings (bool): True to disable solc warnings (default false) solc_args (str): solc arguments (default '') ast_format (str): ast format (default '--ast-compact-json') filter_paths (list(str)): list of path to filter (default []) triage_mode (bool): if true, switch to triage mode (default false) exclude_dependencies (bool): if true, exclude results that are only related to dependencies generate_patches (bool): if true, patches are generated (json output only) change_line_prefix (str): Change the line prefix (default #) for the displayed source codes (i.e. file.sol#1).

line_prefix
codex_enabled
codex_contracts
codex_model
codex_temperature
codex_max_tokens
codex_log
codex_organization: str | None
no_fail
include_interfaces
detectors
225    @property
226    def detectors(self):
227        return self._detectors
detectors_high
229    @property
230    def detectors_high(self):
231        return [d for d in self.detectors if d.IMPACT == DetectorClassification.HIGH]
detectors_medium
233    @property
234    def detectors_medium(self):
235        return [d for d in self.detectors if d.IMPACT == DetectorClassification.MEDIUM]
detectors_low
237    @property
238    def detectors_low(self):
239        return [d for d in self.detectors if d.IMPACT == DetectorClassification.LOW]
detectors_informational
241    @property
242    def detectors_informational(self):
243        return [d for d in self.detectors if d.IMPACT == DetectorClassification.INFORMATIONAL]
detectors_optimization
245    @property
246    def detectors_optimization(self):
247        return [d for d in self.detectors if d.IMPACT == DetectorClassification.OPTIMIZATION]
def register_detector( self, detector_class: type[slither.detectors.abstract_detector.AbstractDetector]) -> None:
249    def register_detector(self, detector_class: type[AbstractDetector]) -> None:
250        """
251        :param detector_class: Class inheriting from `AbstractDetector`.
252        """
253        _check_common_things("detector", detector_class, AbstractDetector, self._detectors)
254
255        for compilation_unit in self.compilation_units:
256            instance = detector_class(compilation_unit, self, logger_detector)
257            self._detectors.append(instance)
Parameters
  • detector_class: Class inheriting from AbstractDetector.
def unregister_detector( self, detector_class: type[slither.detectors.abstract_detector.AbstractDetector]) -> None:
259    def unregister_detector(self, detector_class: type[AbstractDetector]) -> None:
260        """
261        :param detector_class: Class inheriting from `AbstractDetector`.
262        """
263
264        for obj in self._detectors:
265            if isinstance(obj, detector_class):
266                self._detectors.remove(obj)
267                return
Parameters
  • detector_class: Class inheriting from AbstractDetector.
def register_printer( self, printer_class: type[slither.printers.abstract_printer.AbstractPrinter]) -> None:
269    def register_printer(self, printer_class: type[AbstractPrinter]) -> None:
270        """
271        :param printer_class: Class inheriting from `AbstractPrinter`.
272        """
273        _check_common_things("printer", printer_class, AbstractPrinter, self._printers)
274
275        instance = printer_class(self, logger_printer)
276        self._printers.append(instance)
Parameters
  • printer_class: Class inheriting from AbstractPrinter.
def unregister_printer( self, printer_class: type[slither.printers.abstract_printer.AbstractPrinter]) -> None:
278    def unregister_printer(self, printer_class: type[AbstractPrinter]) -> None:
279        """
280        :param printer_class: Class inheriting from `AbstractPrinter`.
281        """
282
283        for obj in self._printers:
284            if isinstance(obj, printer_class):
285                self._printers.remove(obj)
286                return
Parameters
  • printer_class: Class inheriting from AbstractPrinter.
def run_detectors(self) -> list[dict]:
288    def run_detectors(self) -> list[dict]:
289        """
290        :return: List of registered detectors results.
291        """
292        from slither.utils.timing import PhaseTimer
293
294        timer = PhaseTimer.get()
295
296        self.load_previous_results()
297        results = []
298        for d in self._detectors:
299            with timer.phase(f"detector:{d.ARGUMENT}"):
300                results.append(d.detect())
301
302        self.write_results_to_hide()
303        return results
Returns

List of registered detectors results.

def run_printers(self) -> list[slither.utils.output.Output]:
305    def run_printers(self) -> list[Output]:
306        """
307        :return: List of registered printers outputs.
308        """
309
310        return [p.output(self._crytic_compile.target).data for p in self._printers]
Returns

List of registered printers outputs.

triage_mode: bool
312    @property
313    def triage_mode(self) -> bool:
314        return self._triage_mode