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