slither.utils.command_line

  1import argparse
  2import enum
  3import json
  4import os
  5import re
  6import logging
  7from collections import defaultdict
  8from typing import Dict, List, Type, Union
  9
 10from crytic_compile.cryticparser.defaults import (
 11    DEFAULTS_FLAG_IN_CONFIG as DEFAULTS_FLAG_IN_CONFIG_CRYTIC_COMPILE,
 12)
 13
 14from slither.detectors.abstract_detector import classification_txt, AbstractDetector
 15from slither.printers.abstract_printer import AbstractPrinter
 16from slither.utils.colors import yellow, red
 17from slither.utils.myprettytable import MyPrettyTable
 18
 19logger = logging.getLogger("Slither")
 20
 21DEFAULT_JSON_OUTPUT_TYPES = ["detectors", "printers"]
 22JSON_OUTPUT_TYPES = [
 23    "compilations",
 24    "console",
 25    "detectors",
 26    "printers",
 27    "list-detectors",
 28    "list-printers",
 29]
 30
 31
 32class FailOnLevel(enum.Enum):
 33    PEDANTIC = "pedantic"
 34    LOW = "low"
 35    MEDIUM = "medium"
 36    HIGH = "high"
 37    NONE = "none"
 38
 39
 40# Those are the flags shared by the command line and the config file
 41defaults_flag_in_config = {
 42    "codex": False,
 43    "codex_contracts": "all",
 44    "codex_model": "text-davinci-003",
 45    "codex_temperature": 0,
 46    "codex_max_tokens": 300,
 47    "codex_log": False,
 48    "detectors_to_run": "all",
 49    "printers_to_run": None,
 50    "detectors_to_exclude": None,
 51    "exclude_dependencies": False,
 52    "exclude_informational": False,
 53    "exclude_optimization": False,
 54    "exclude_low": False,
 55    "exclude_medium": False,
 56    "exclude_high": False,
 57    "fail_on": FailOnLevel.PEDANTIC,
 58    "json": None,
 59    "sarif": None,
 60    "json-types": ",".join(DEFAULT_JSON_OUTPUT_TYPES),
 61    "disable_color": False,
 62    "filter_paths": None,
 63    "include_paths": None,
 64    "generate_patches": False,
 65    # debug command
 66    "skip_assembly": False,
 67    "legacy_ast": False,
 68    "zip": None,
 69    "zip_type": "lzma",
 70    "show_ignored_findings": False,
 71    "no_fail": False,
 72    "sarif_input": "export.sarif",
 73    "sarif_triage": "export.sarif.sarifexplorer",
 74    "triage_database": "slither.db.json",
 75    **DEFAULTS_FLAG_IN_CONFIG_CRYTIC_COMPILE,
 76}
 77
 78deprecated_flags = {
 79    "fail_pedantic": True,
 80    "fail_low": False,
 81    "fail_medium": False,
 82    "fail_high": False,
 83}
 84
 85
 86def read_config_file(args: argparse.Namespace) -> None:
 87    # No config file was provided as an argument
 88    if args.config_file is None:
 89        # Check wether the default config file is present
 90        if os.path.exists("slither.config.json"):
 91            # The default file exists, use it
 92            args.config_file = "slither.config.json"
 93        else:
 94            return
 95
 96    if os.path.isfile(args.config_file):
 97        try:
 98            with open(args.config_file, encoding="utf8") as f:
 99                config = json.load(f)
100                for key, elem in config.items():
101                    if key in deprecated_flags:
102                        logger.info(
103                            yellow(f"{args.config_file} has a deprecated key: {key} : {elem}")
104                        )
105                        migrate_config_options(args, key, elem)
106                        continue
107                    if key not in defaults_flag_in_config:
108                        logger.info(
109                            yellow(f"{args.config_file} has an unknown key: {key} : {elem}")
110                        )
111                        continue
112                    if getattr(args, key) == defaults_flag_in_config[key]:
113                        setattr(args, key, elem)
114        except json.decoder.JSONDecodeError as e:
115            logger.error(red(f"Impossible to read {args.config_file}, please check the file {e}"))
116    else:
117        logger.error(red(f"File {args.config_file} is not a file or does not exist"))
118        logger.error(yellow("Falling back to the default settings..."))
119
120
121def migrate_config_options(args: argparse.Namespace, key: str, elem):
122    if key.startswith("fail_") and getattr(args, "fail_on") == defaults_flag_in_config["fail_on"]:
123        if key == "fail_pedantic":
124            pedantic_setting = elem
125            fail_on = FailOnLevel.PEDANTIC if pedantic_setting else FailOnLevel.NONE
126            setattr(args, "fail_on", fail_on)
127            logger.info(f"Migrating fail_pedantic: {pedantic_setting} as fail_on: {fail_on.value}")
128        elif key == "fail_low" and elem is True:
129            logger.info("Migrating fail_low: true -> fail_on: low")
130            setattr(args, "fail_on", FailOnLevel.LOW)
131
132        elif key == "fail_medium" and elem is True:
133            logger.info("Migrating fail_medium: true -> fail_on: medium")
134            setattr(args, "fail_on", FailOnLevel.MEDIUM)
135
136        elif key == "fail_high" and elem is True:
137            logger.info("Migrating fail_high: true -> fail_on: high")
138            setattr(args, "fail_on", FailOnLevel.HIGH)
139        else:
140            logger.warning(yellow(f"Key {key} was deprecated but no migration was provided"))
141
142
143def output_to_markdown(
144    detector_classes: List[Type[AbstractDetector]],
145    printer_classes: List[Type[AbstractPrinter]],
146    filter_wiki: str,
147) -> None:
148    def extract_help(cls: Union[Type[AbstractDetector], Type[AbstractPrinter]]) -> str:
149        if cls.WIKI == "":
150            return cls.HELP
151        return f"[{cls.HELP}]({cls.WIKI})"
152
153    detectors_list = []
154    print(filter_wiki)
155    for detector in detector_classes:
156        argument = detector.ARGUMENT
157        # dont show the backdoor example
158        if argument == "backdoor":
159            continue
160        if not filter_wiki in detector.WIKI:
161            continue
162        help_info = extract_help(detector)
163        impact = detector.IMPACT
164        confidence = classification_txt[detector.CONFIDENCE]
165        detectors_list.append((argument, help_info, impact, confidence))
166
167    # Sort by impact, confidence, and name
168    detectors_list = sorted(
169        detectors_list, key=lambda element: (element[2], element[3], element[0])
170    )
171    idx = 1
172    for (argument, help_info, impact, confidence) in detectors_list:
173        print(f"{idx} | `{argument}` | {help_info} | {classification_txt[impact]} | {confidence}")
174        idx = idx + 1
175
176    print()
177    printers_list = []
178    for printer in printer_classes:
179        argument = printer.ARGUMENT
180        help_info = extract_help(printer)
181        printers_list.append((argument, help_info))
182
183    # Sort by impact, confidence, and name
184    printers_list = sorted(printers_list, key=lambda element: (element[0]))
185    idx = 1
186    for (argument, help_info) in printers_list:
187        print(f"{idx} | `{argument}` | {help_info}")
188        idx = idx + 1
189
190
191def get_level(l: str) -> int:
192    tab = l.count("\t") + 1
193    if l.replace("\t", "").startswith(" -"):
194        tab = tab + 1
195    if l.replace("\t", "").startswith("-"):
196        tab = tab + 1
197    return tab
198
199
200def convert_result_to_markdown(txt: str) -> str:
201    # -1 to remove the last \n
202    lines = txt[0:-1].split("\n")
203    ret = []
204    level = 0
205    for l in lines:
206        next_level = get_level(l)
207        prefix = "<li>"
208        if next_level < level:
209            prefix = "</ul>" * (level - next_level) + prefix
210        if next_level > level:
211            prefix = "<ul>" * (next_level - level) + prefix
212        level = next_level
213        ret.append(prefix + l)
214
215    return "".join(ret)
216
217
218def output_results_to_markdown(
219    all_results: List[Dict], checklistlimit: str, show_ignored_findings: bool
220) -> None:
221    checks = defaultdict(list)
222    info: Dict = defaultdict(dict)
223    for results_ in all_results:
224        checks[results_["check"]].append(results_)
225        info[results_["check"]] = {
226            "impact": results_["impact"],
227            "confidence": results_["confidence"],
228        }
229
230    if not show_ignored_findings:
231        print(
232            "**THIS CHECKLIST IS NOT COMPLETE**. Use `--show-ignored-findings` to show all the results."
233        )
234
235    print("Summary")
236    for check_ in checks:
237        print(
238            f" - [{check_}](#{check_}) ({len(checks[check_])} results) ({info[check_]['impact']})"
239        )
240
241    counter = 0
242    for (check, results) in checks.items():
243        print(f"## {check}")
244        print(f'Impact: {info[check]["impact"]}')
245        print(f'Confidence: {info[check]["confidence"]}')
246        additional = False
247        if checklistlimit and len(results) > 5:
248            results = results[0:5]
249            additional = True
250        for result in results:
251            print(" - [ ] ID-" + f"{counter}")
252            counter = counter + 1
253            print(result["markdown"])
254            if result["first_markdown_element"]:
255                print(result["first_markdown_element"])
256                print("\n")
257        if additional:
258            print(f"**More results were found, check [{checklistlimit}]({checklistlimit})**")
259
260
261def output_wiki(detector_classes: List[Type[AbstractDetector]], filter_wiki: str) -> None:
262
263    # Sort by impact, confidence, and name
264    detectors_list = sorted(
265        detector_classes,
266        key=lambda element: (element.IMPACT, element.CONFIDENCE, element.ARGUMENT),
267    )
268
269    for detector in detectors_list:
270        argument = detector.ARGUMENT
271        # dont show the backdoor example
272        if argument == "backdoor":
273            continue
274        if not filter_wiki in detector.WIKI:
275            continue
276        check = detector.ARGUMENT
277        impact = classification_txt[detector.IMPACT]
278        confidence = classification_txt[detector.CONFIDENCE]
279        title = detector.WIKI_TITLE
280        description = detector.WIKI_DESCRIPTION
281        exploit_scenario = detector.WIKI_EXPLOIT_SCENARIO
282        recommendation = detector.WIKI_RECOMMENDATION
283
284        print(f"\n## {title}")
285        print("### Configuration")
286        print(f"* Check: `{check}`")
287        print(f"* Severity: `{impact}`")
288        print(f"* Confidence: `{confidence}`")
289        print("\n### Description")
290        print(description)
291        if exploit_scenario:
292            print("\n### Exploit Scenario:")
293            print(exploit_scenario)
294        print("\n### Recommendation")
295        print(recommendation)
296
297
298def output_detectors(detector_classes: List[Type[AbstractDetector]]) -> None:
299    detectors_list = []
300    for detector in detector_classes:
301        argument = detector.ARGUMENT
302        # dont show the backdoor example
303        if argument == "backdoor":
304            continue
305        help_info = detector.HELP
306        impact = detector.IMPACT
307        confidence = classification_txt[detector.CONFIDENCE]
308        detectors_list.append((argument, help_info, impact, confidence))
309    table = MyPrettyTable(["Num", "Check", "What it Detects", "Impact", "Confidence"])
310
311    # Sort by impact, confidence, and name
312    detectors_list = sorted(
313        detectors_list, key=lambda element: (element[2], element[3], element[0])
314    )
315    idx = 1
316    for (argument, help_info, impact, confidence) in detectors_list:
317        table.add_row([str(idx), argument, help_info, classification_txt[impact], confidence])
318        idx = idx + 1
319    print(table)
320
321
322# pylint: disable=too-many-locals
323def output_detectors_json(
324    detector_classes: List[Type[AbstractDetector]],
325) -> List[Dict]:
326    detectors_list = []
327    for detector in detector_classes:
328        argument = detector.ARGUMENT
329        # dont show the backdoor example
330        if argument == "backdoor":
331            continue
332        help_info = detector.HELP
333        impact = detector.IMPACT
334        confidence = classification_txt[detector.CONFIDENCE]
335        wiki_url = detector.WIKI
336        wiki_description = detector.WIKI_DESCRIPTION
337        wiki_exploit_scenario = detector.WIKI_EXPLOIT_SCENARIO
338        wiki_recommendation = detector.WIKI_RECOMMENDATION
339        detectors_list.append(
340            (
341                argument,
342                help_info,
343                impact,
344                confidence,
345                wiki_url,
346                wiki_description,
347                wiki_exploit_scenario,
348                wiki_recommendation,
349            )
350        )
351
352    # Sort by impact, confidence, and name
353    detectors_list = sorted(
354        detectors_list, key=lambda element: (element[2], element[3], element[0])
355    )
356    idx = 1
357    table = []
358    for (
359        argument,
360        help_info,
361        impact,
362        confidence,
363        wiki_url,
364        description,
365        exploit,
366        recommendation,
367    ) in detectors_list:
368        table.append(
369            {
370                "index": idx,
371                "check": argument,
372                "title": help_info,
373                "impact": classification_txt[impact],
374                "confidence": confidence,
375                "wiki_url": wiki_url,
376                "description": description,
377                "exploit_scenario": exploit,
378                "recommendation": recommendation,
379            }
380        )
381        idx = idx + 1
382    return table
383
384
385def output_printers(printer_classes: List[Type[AbstractPrinter]]) -> None:
386    printers_list = []
387    for printer in printer_classes:
388        argument = printer.ARGUMENT
389        help_info = printer.HELP
390        printers_list.append((argument, help_info))
391    table = MyPrettyTable(["Num", "Printer", "What it Does"])
392
393    # Sort by impact, confidence, and name
394    printers_list = sorted(printers_list, key=lambda element: (element[0]))
395    idx = 1
396    for (argument, help_info) in printers_list:
397        table.add_row([str(idx), argument, help_info])
398        idx = idx + 1
399    print(table)
400
401
402def output_printers_json(printer_classes: List[Type[AbstractPrinter]]) -> List[Dict]:
403    printers_list = []
404    for printer in printer_classes:
405        argument = printer.ARGUMENT
406        help_info = printer.HELP
407
408        printers_list.append((argument, help_info))
409
410    # Sort by name
411    printers_list = sorted(printers_list, key=lambda element: (element[0]))
412    idx = 1
413    table = []
414    for (argument, help_info) in printers_list:
415        table.append({"index": idx, "check": argument, "title": help_info})
416        idx = idx + 1
417    return table
418
419
420def check_and_sanitize_markdown_root(markdown_root: str) -> str:
421    # Regex to check whether the markdown_root is a GitHub URL
422    match = re.search(
423        r"(https://)github.com/([a-zA-Z-]+)([:/][A-Za-z0-9_.-]+[:/]?)([A-Za-z0-9_.-]*)(.*)",
424        markdown_root,
425    )
426    if match:
427        if markdown_root[-1] != "/":
428            logger.warning("Appending '/' in markdown_root url for better code referencing")
429            markdown_root = markdown_root + "/"
430
431        if not match.group(4):
432            logger.warning(
433                "Appending 'master/tree/' in markdown_root url for better code referencing"
434            )
435            markdown_root = markdown_root + "master/tree/"
436        elif match.group(4) == "tree":
437            logger.warning(
438                "Replacing 'tree' with 'blob' in markdown_root url for better code referencing"
439            )
440            positions = match.span(4)
441            markdown_root = f"{markdown_root[:positions[0]]}blob{markdown_root[positions[1]:]}"
442
443    return markdown_root
logger = <Logger Slither (WARNING)>
DEFAULT_JSON_OUTPUT_TYPES = ['detectors', 'printers']
JSON_OUTPUT_TYPES = ['compilations', 'console', 'detectors', 'printers', 'list-detectors', 'list-printers']
class FailOnLevel(enum.Enum):
33class FailOnLevel(enum.Enum):
34    PEDANTIC = "pedantic"
35    LOW = "low"
36    MEDIUM = "medium"
37    HIGH = "high"
38    NONE = "none"

An enumeration.

PEDANTIC = <FailOnLevel.PEDANTIC: 'pedantic'>
LOW = <FailOnLevel.LOW: 'low'>
MEDIUM = <FailOnLevel.MEDIUM: 'medium'>
HIGH = <FailOnLevel.HIGH: 'high'>
NONE = <FailOnLevel.NONE: 'none'>
Inherited Members
enum.Enum
name
value
defaults_flag_in_config = {'codex': False, 'codex_contracts': 'all', 'codex_model': 'text-davinci-003', 'codex_temperature': 0, 'codex_max_tokens': 300, 'codex_log': False, 'detectors_to_run': 'all', 'printers_to_run': None, 'detectors_to_exclude': None, 'exclude_dependencies': False, 'exclude_informational': False, 'exclude_optimization': False, 'exclude_low': False, 'exclude_medium': False, 'exclude_high': False, 'fail_on': <FailOnLevel.PEDANTIC: 'pedantic'>, 'json': None, 'sarif': None, 'json-types': 'detectors,printers', 'disable_color': False, 'filter_paths': None, 'include_paths': None, 'generate_patches': False, 'skip_assembly': False, 'legacy_ast': False, 'zip': None, 'zip_type': 'lzma', 'show_ignored_findings': False, 'no_fail': False, 'sarif_input': 'export.sarif', 'sarif_triage': 'export.sarif.sarifexplorer', 'triage_database': 'slither.db.json', 'compile_force_framework': None, 'compile_remove_metadata': False, 'compile_custom_build': None, 'solc': 'solc', 'solc_remaps': None, 'solc_args': None, 'solc_disable_warnings': False, 'solc_working_dir': None, 'solc_solcs_select': None, 'solc_solcs_bin': None, 'solc_standard_json': False, 'solc_force_legacy_json': False, 'truffle_version': None, 'truffle_ignore_compile': False, 'truffle_build_directory': 'build/contracts', 'truffle_overwrite_config': False, 'truffle_overwrite_version': None, 'embark_ignore_compile': False, 'embark_overwrite_config': False, 'brownie_ignore_compile': False, 'dapp_ignore_compile': False, 'etherlime_ignore_compile': False, 'etherlime_compile_arguments': None, 'etherscan_only_source_code': False, 'etherscan_only_bytecode': False, 'etherscan_api_key': None, 'etherscan_export_directory': 'etherscan-contracts', 'waffle_ignore_compile': False, 'waffle_config_file': None, 'npx_disable': False, 'ignore_compile': False, 'skip_clean': False, 'buidler_ignore_compile': False, 'buidler_cache_directory': 'cache', 'buidler_skip_directory_name_fix': False, 'hardhat_ignore_compile': False, 'hardhat_cache_directory': None, 'hardhat_artifacts_directory': None, 'foundry_ignore_compile': False, 'foundry_out_directory': 'out', 'foundry_compile_all': False, 'export_dir': 'crytic-export', 'compile_libraries': None}
deprecated_flags = {'fail_pedantic': True, 'fail_low': False, 'fail_medium': False, 'fail_high': False}
def read_config_file(args: argparse.Namespace) -> None:
 87def read_config_file(args: argparse.Namespace) -> None:
 88    # No config file was provided as an argument
 89    if args.config_file is None:
 90        # Check wether the default config file is present
 91        if os.path.exists("slither.config.json"):
 92            # The default file exists, use it
 93            args.config_file = "slither.config.json"
 94        else:
 95            return
 96
 97    if os.path.isfile(args.config_file):
 98        try:
 99            with open(args.config_file, encoding="utf8") as f:
100                config = json.load(f)
101                for key, elem in config.items():
102                    if key in deprecated_flags:
103                        logger.info(
104                            yellow(f"{args.config_file} has a deprecated key: {key} : {elem}")
105                        )
106                        migrate_config_options(args, key, elem)
107                        continue
108                    if key not in defaults_flag_in_config:
109                        logger.info(
110                            yellow(f"{args.config_file} has an unknown key: {key} : {elem}")
111                        )
112                        continue
113                    if getattr(args, key) == defaults_flag_in_config[key]:
114                        setattr(args, key, elem)
115        except json.decoder.JSONDecodeError as e:
116            logger.error(red(f"Impossible to read {args.config_file}, please check the file {e}"))
117    else:
118        logger.error(red(f"File {args.config_file} is not a file or does not exist"))
119        logger.error(yellow("Falling back to the default settings..."))
def migrate_config_options(args: argparse.Namespace, key: str, elem):
122def migrate_config_options(args: argparse.Namespace, key: str, elem):
123    if key.startswith("fail_") and getattr(args, "fail_on") == defaults_flag_in_config["fail_on"]:
124        if key == "fail_pedantic":
125            pedantic_setting = elem
126            fail_on = FailOnLevel.PEDANTIC if pedantic_setting else FailOnLevel.NONE
127            setattr(args, "fail_on", fail_on)
128            logger.info(f"Migrating fail_pedantic: {pedantic_setting} as fail_on: {fail_on.value}")
129        elif key == "fail_low" and elem is True:
130            logger.info("Migrating fail_low: true -> fail_on: low")
131            setattr(args, "fail_on", FailOnLevel.LOW)
132
133        elif key == "fail_medium" and elem is True:
134            logger.info("Migrating fail_medium: true -> fail_on: medium")
135            setattr(args, "fail_on", FailOnLevel.MEDIUM)
136
137        elif key == "fail_high" and elem is True:
138            logger.info("Migrating fail_high: true -> fail_on: high")
139            setattr(args, "fail_on", FailOnLevel.HIGH)
140        else:
141            logger.warning(yellow(f"Key {key} was deprecated but no migration was provided"))
def output_to_markdown( detector_classes: List[Type[slither.detectors.abstract_detector.AbstractDetector]], printer_classes: List[Type[slither.printers.abstract_printer.AbstractPrinter]], filter_wiki: str) -> None:
144def output_to_markdown(
145    detector_classes: List[Type[AbstractDetector]],
146    printer_classes: List[Type[AbstractPrinter]],
147    filter_wiki: str,
148) -> None:
149    def extract_help(cls: Union[Type[AbstractDetector], Type[AbstractPrinter]]) -> str:
150        if cls.WIKI == "":
151            return cls.HELP
152        return f"[{cls.HELP}]({cls.WIKI})"
153
154    detectors_list = []
155    print(filter_wiki)
156    for detector in detector_classes:
157        argument = detector.ARGUMENT
158        # dont show the backdoor example
159        if argument == "backdoor":
160            continue
161        if not filter_wiki in detector.WIKI:
162            continue
163        help_info = extract_help(detector)
164        impact = detector.IMPACT
165        confidence = classification_txt[detector.CONFIDENCE]
166        detectors_list.append((argument, help_info, impact, confidence))
167
168    # Sort by impact, confidence, and name
169    detectors_list = sorted(
170        detectors_list, key=lambda element: (element[2], element[3], element[0])
171    )
172    idx = 1
173    for (argument, help_info, impact, confidence) in detectors_list:
174        print(f"{idx} | `{argument}` | {help_info} | {classification_txt[impact]} | {confidence}")
175        idx = idx + 1
176
177    print()
178    printers_list = []
179    for printer in printer_classes:
180        argument = printer.ARGUMENT
181        help_info = extract_help(printer)
182        printers_list.append((argument, help_info))
183
184    # Sort by impact, confidence, and name
185    printers_list = sorted(printers_list, key=lambda element: (element[0]))
186    idx = 1
187    for (argument, help_info) in printers_list:
188        print(f"{idx} | `{argument}` | {help_info}")
189        idx = idx + 1
def get_level(l: str) -> int:
192def get_level(l: str) -> int:
193    tab = l.count("\t") + 1
194    if l.replace("\t", "").startswith(" -"):
195        tab = tab + 1
196    if l.replace("\t", "").startswith("-"):
197        tab = tab + 1
198    return tab
def convert_result_to_markdown(txt: str) -> str:
201def convert_result_to_markdown(txt: str) -> str:
202    # -1 to remove the last \n
203    lines = txt[0:-1].split("\n")
204    ret = []
205    level = 0
206    for l in lines:
207        next_level = get_level(l)
208        prefix = "<li>"
209        if next_level < level:
210            prefix = "</ul>" * (level - next_level) + prefix
211        if next_level > level:
212            prefix = "<ul>" * (next_level - level) + prefix
213        level = next_level
214        ret.append(prefix + l)
215
216    return "".join(ret)
def output_results_to_markdown( all_results: List[Dict], checklistlimit: str, show_ignored_findings: bool) -> None:
219def output_results_to_markdown(
220    all_results: List[Dict], checklistlimit: str, show_ignored_findings: bool
221) -> None:
222    checks = defaultdict(list)
223    info: Dict = defaultdict(dict)
224    for results_ in all_results:
225        checks[results_["check"]].append(results_)
226        info[results_["check"]] = {
227            "impact": results_["impact"],
228            "confidence": results_["confidence"],
229        }
230
231    if not show_ignored_findings:
232        print(
233            "**THIS CHECKLIST IS NOT COMPLETE**. Use `--show-ignored-findings` to show all the results."
234        )
235
236    print("Summary")
237    for check_ in checks:
238        print(
239            f" - [{check_}](#{check_}) ({len(checks[check_])} results) ({info[check_]['impact']})"
240        )
241
242    counter = 0
243    for (check, results) in checks.items():
244        print(f"## {check}")
245        print(f'Impact: {info[check]["impact"]}')
246        print(f'Confidence: {info[check]["confidence"]}')
247        additional = False
248        if checklistlimit and len(results) > 5:
249            results = results[0:5]
250            additional = True
251        for result in results:
252            print(" - [ ] ID-" + f"{counter}")
253            counter = counter + 1
254            print(result["markdown"])
255            if result["first_markdown_element"]:
256                print(result["first_markdown_element"])
257                print("\n")
258        if additional:
259            print(f"**More results were found, check [{checklistlimit}]({checklistlimit})**")
def output_wiki( detector_classes: List[Type[slither.detectors.abstract_detector.AbstractDetector]], filter_wiki: str) -> None:
262def output_wiki(detector_classes: List[Type[AbstractDetector]], filter_wiki: str) -> None:
263
264    # Sort by impact, confidence, and name
265    detectors_list = sorted(
266        detector_classes,
267        key=lambda element: (element.IMPACT, element.CONFIDENCE, element.ARGUMENT),
268    )
269
270    for detector in detectors_list:
271        argument = detector.ARGUMENT
272        # dont show the backdoor example
273        if argument == "backdoor":
274            continue
275        if not filter_wiki in detector.WIKI:
276            continue
277        check = detector.ARGUMENT
278        impact = classification_txt[detector.IMPACT]
279        confidence = classification_txt[detector.CONFIDENCE]
280        title = detector.WIKI_TITLE
281        description = detector.WIKI_DESCRIPTION
282        exploit_scenario = detector.WIKI_EXPLOIT_SCENARIO
283        recommendation = detector.WIKI_RECOMMENDATION
284
285        print(f"\n## {title}")
286        print("### Configuration")
287        print(f"* Check: `{check}`")
288        print(f"* Severity: `{impact}`")
289        print(f"* Confidence: `{confidence}`")
290        print("\n### Description")
291        print(description)
292        if exploit_scenario:
293            print("\n### Exploit Scenario:")
294            print(exploit_scenario)
295        print("\n### Recommendation")
296        print(recommendation)
def output_detectors( detector_classes: List[Type[slither.detectors.abstract_detector.AbstractDetector]]) -> None:
299def output_detectors(detector_classes: List[Type[AbstractDetector]]) -> None:
300    detectors_list = []
301    for detector in detector_classes:
302        argument = detector.ARGUMENT
303        # dont show the backdoor example
304        if argument == "backdoor":
305            continue
306        help_info = detector.HELP
307        impact = detector.IMPACT
308        confidence = classification_txt[detector.CONFIDENCE]
309        detectors_list.append((argument, help_info, impact, confidence))
310    table = MyPrettyTable(["Num", "Check", "What it Detects", "Impact", "Confidence"])
311
312    # Sort by impact, confidence, and name
313    detectors_list = sorted(
314        detectors_list, key=lambda element: (element[2], element[3], element[0])
315    )
316    idx = 1
317    for (argument, help_info, impact, confidence) in detectors_list:
318        table.add_row([str(idx), argument, help_info, classification_txt[impact], confidence])
319        idx = idx + 1
320    print(table)
def output_detectors_json( detector_classes: List[Type[slither.detectors.abstract_detector.AbstractDetector]]) -> List[Dict]:
324def output_detectors_json(
325    detector_classes: List[Type[AbstractDetector]],
326) -> List[Dict]:
327    detectors_list = []
328    for detector in detector_classes:
329        argument = detector.ARGUMENT
330        # dont show the backdoor example
331        if argument == "backdoor":
332            continue
333        help_info = detector.HELP
334        impact = detector.IMPACT
335        confidence = classification_txt[detector.CONFIDENCE]
336        wiki_url = detector.WIKI
337        wiki_description = detector.WIKI_DESCRIPTION
338        wiki_exploit_scenario = detector.WIKI_EXPLOIT_SCENARIO
339        wiki_recommendation = detector.WIKI_RECOMMENDATION
340        detectors_list.append(
341            (
342                argument,
343                help_info,
344                impact,
345                confidence,
346                wiki_url,
347                wiki_description,
348                wiki_exploit_scenario,
349                wiki_recommendation,
350            )
351        )
352
353    # Sort by impact, confidence, and name
354    detectors_list = sorted(
355        detectors_list, key=lambda element: (element[2], element[3], element[0])
356    )
357    idx = 1
358    table = []
359    for (
360        argument,
361        help_info,
362        impact,
363        confidence,
364        wiki_url,
365        description,
366        exploit,
367        recommendation,
368    ) in detectors_list:
369        table.append(
370            {
371                "index": idx,
372                "check": argument,
373                "title": help_info,
374                "impact": classification_txt[impact],
375                "confidence": confidence,
376                "wiki_url": wiki_url,
377                "description": description,
378                "exploit_scenario": exploit,
379                "recommendation": recommendation,
380            }
381        )
382        idx = idx + 1
383    return table
def output_printers( printer_classes: List[Type[slither.printers.abstract_printer.AbstractPrinter]]) -> None:
386def output_printers(printer_classes: List[Type[AbstractPrinter]]) -> None:
387    printers_list = []
388    for printer in printer_classes:
389        argument = printer.ARGUMENT
390        help_info = printer.HELP
391        printers_list.append((argument, help_info))
392    table = MyPrettyTable(["Num", "Printer", "What it Does"])
393
394    # Sort by impact, confidence, and name
395    printers_list = sorted(printers_list, key=lambda element: (element[0]))
396    idx = 1
397    for (argument, help_info) in printers_list:
398        table.add_row([str(idx), argument, help_info])
399        idx = idx + 1
400    print(table)
def output_printers_json( printer_classes: List[Type[slither.printers.abstract_printer.AbstractPrinter]]) -> List[Dict]:
403def output_printers_json(printer_classes: List[Type[AbstractPrinter]]) -> List[Dict]:
404    printers_list = []
405    for printer in printer_classes:
406        argument = printer.ARGUMENT
407        help_info = printer.HELP
408
409        printers_list.append((argument, help_info))
410
411    # Sort by name
412    printers_list = sorted(printers_list, key=lambda element: (element[0]))
413    idx = 1
414    table = []
415    for (argument, help_info) in printers_list:
416        table.append({"index": idx, "check": argument, "title": help_info})
417        idx = idx + 1
418    return table
def check_and_sanitize_markdown_root(markdown_root: str) -> str:
421def check_and_sanitize_markdown_root(markdown_root: str) -> str:
422    # Regex to check whether the markdown_root is a GitHub URL
423    match = re.search(
424        r"(https://)github.com/([a-zA-Z-]+)([:/][A-Za-z0-9_.-]+[:/]?)([A-Za-z0-9_.-]*)(.*)",
425        markdown_root,
426    )
427    if match:
428        if markdown_root[-1] != "/":
429            logger.warning("Appending '/' in markdown_root url for better code referencing")
430            markdown_root = markdown_root + "/"
431
432        if not match.group(4):
433            logger.warning(
434                "Appending 'master/tree/' in markdown_root url for better code referencing"
435            )
436            markdown_root = markdown_root + "master/tree/"
437        elif match.group(4) == "tree":
438            logger.warning(
439                "Replacing 'tree' with 'blob' in markdown_root url for better code referencing"
440            )
441            positions = match.span(4)
442            markdown_root = f"{markdown_root[:positions[0]]}blob{markdown_root[positions[1]:]}"
443
444    return markdown_root