From 16393d51fe1baefe0aa6ca4ba8487e4d4872db84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Mon, 3 Oct 2022 19:21:31 -0300 Subject: [PATCH] Separate checks into a module --- slither/tools/doctor/__main__.py | 105 ++---------------------- slither/tools/doctor/checks/__init__.py | 18 ++++ slither/tools/doctor/checks/platform.py | 59 +++++++++++++ slither/tools/doctor/checks/versions.py | 58 +++++++++++++ slither/tools/doctor/packages.py | 24 ------ 5 files changed, 140 insertions(+), 124 deletions(-) create mode 100644 slither/tools/doctor/checks/__init__.py create mode 100644 slither/tools/doctor/checks/platform.py create mode 100644 slither/tools/doctor/checks/versions.py delete mode 100644 slither/tools/doctor/packages.py diff --git a/slither/tools/doctor/__main__.py b/slither/tools/doctor/__main__.py index e7d7929e3..94ae865ec 100644 --- a/slither/tools/doctor/__main__.py +++ b/slither/tools/doctor/__main__.py @@ -1,13 +1,9 @@ import argparse -import logging -from pathlib import Path from crytic_compile import cryticparser -import crytic_compile.crytic_compile as crytic_compile -from slither.tools.doctor.packages import get_installed_version, get_github_version -from slither.tools.doctor.utils import report_section, snip_section -from slither.utils.colors import red, yellow, green +from slither.tools.doctor.utils import report_section +from slither.tools.doctor.checks import ALL_CHECKS def parse_args() -> argparse.Namespace: @@ -28,104 +24,13 @@ def parse_args() -> argparse.Namespace: return parser.parse_args() -def show_versions() -> None: - versions = { - "Slither": (get_installed_version("slither-analyzer"), get_github_version("slither")), - "crytic-compile": ( - get_installed_version("crytic-compile"), - get_github_version("crytic-compile"), - ), - "solc-select": (get_installed_version("solc-select"), get_github_version("solc-select")), - } - - outdated = { - name - for name, (installed, latest) in versions.items() - if not installed or not latest or latest > installed - } - - for name, (installed, latest) in versions.items(): - color = yellow if name in outdated else green - print(f"{name + ':':<16}{color(installed or 'N/A'):<16} (latest is {latest or 'Unknown'})") - - if len(outdated) > 0: - print() - print( - yellow( - f"Please update {', '.join(outdated)} to the latest release before creating a bug report." - ) - ) - else: - print() - print(green("Your tools are up to date.")) - - -def detect_platform(project: str, **kwargs) -> None: - path = Path(project) - if path.is_file(): - print( - yellow( - f"{project!r} is a file. Using it as target will manually compile your code with solc and _not_ use a compilation framework. Is that what you meant to do?" - ) - ) - return - - print(f"Trying to detect project type for {project!r}") - - supported_platforms = crytic_compile.get_platforms() - skip_platforms = {"solc", "solc-json", "archive", "standard", "etherscan"} - detected_platforms = { - platform.NAME: platform.is_supported(project, **kwargs) - for platform in supported_platforms - if platform.NAME.lower() not in skip_platforms - } - platform_qty = len([platform for platform, state in detected_platforms.items() if state]) - - print("Is this project using...") - for platform, state in detected_platforms.items(): - print(f" => {platform + '?':<15}{state and green('Yes') or red('No')}") - print() - - if platform_qty == 0: - print(red("No platform was detected! This doesn't sound right.")) - print( - yellow( - "Are you trying to analyze a folder with standalone solidity files, without using a compilation framework? If that's the case, then this is okay." - ) - ) - elif platform_qty > 1: - print(red("More than one platform was detected! This doesn't sound right.")) - print( - red("Please use `--compile-force-framework` in Slither to force the correct framework.") - ) - else: - print(green("A single platform was detected."), yellow("Is it the one you expected?")) - - -def compile_project(project: str, **kwargs): - print("Invoking crytic-compile on the project, please wait...") - - try: - crytic_compile.CryticCompile(project, **kwargs) - except Exception as e: - with snip_section("Project compilation failed :( The following error was generated:"): - logging.exception(e) - - def main(): args = parse_args() kwargs = vars(args) - with report_section("Software versions"): - show_versions() - - with report_section("Project platform"): - detect_platform(**kwargs) - - with report_section("Project compilation"): - compile_project(**kwargs) - - # TODO other checks + for check in ALL_CHECKS: + with report_section(check.title): + check.function(**kwargs) if __name__ == "__main__": diff --git a/slither/tools/doctor/checks/__init__.py b/slither/tools/doctor/checks/__init__.py new file mode 100644 index 000000000..762c60b5d --- /dev/null +++ b/slither/tools/doctor/checks/__init__.py @@ -0,0 +1,18 @@ +from typing import Callable, List +from dataclasses import dataclass + +from slither.tools.doctor.checks.platform import compile_project, detect_platform +from slither.tools.doctor.checks.versions import show_versions + + +@dataclass +class Check: + title: str + function: Callable[..., None] + + +ALL_CHECKS: List[Check] = [ + Check("Software versions", show_versions), + Check("Project platform", detect_platform), + Check("Project compilation", compile_project), +] diff --git a/slither/tools/doctor/checks/platform.py b/slither/tools/doctor/checks/platform.py new file mode 100644 index 000000000..412b6d37f --- /dev/null +++ b/slither/tools/doctor/checks/platform.py @@ -0,0 +1,59 @@ +import logging +from pathlib import Path + +import crytic_compile.crytic_compile as crytic_compile + +from slither.tools.doctor.utils import snip_section +from slither.utils.colors import red, yellow, green + + +def detect_platform(project: str, **kwargs) -> None: + path = Path(project) + if path.is_file(): + print( + yellow( + f"{project!r} is a file. Using it as target will manually compile your code with solc and _not_ use a compilation framework. Is that what you meant to do?" + ) + ) + return + + print(f"Trying to detect project type for {project!r}") + + supported_platforms = crytic_compile.get_platforms() + skip_platforms = {"solc", "solc-json", "archive", "standard", "etherscan"} + detected_platforms = { + platform.NAME: platform.is_supported(project, **kwargs) + for platform in supported_platforms + if platform.NAME.lower() not in skip_platforms + } + platform_qty = len([platform for platform, state in detected_platforms.items() if state]) + + print("Is this project using...") + for platform, state in detected_platforms.items(): + print(f" => {platform + '?':<15}{state and green('Yes') or red('No')}") + print() + + if platform_qty == 0: + print(red("No platform was detected! This doesn't sound right.")) + print( + yellow( + "Are you trying to analyze a folder with standalone solidity files, without using a compilation framework? If that's the case, then this is okay." + ) + ) + elif platform_qty > 1: + print(red("More than one platform was detected! This doesn't sound right.")) + print( + red("Please use `--compile-force-framework` in Slither to force the correct framework.") + ) + else: + print(green("A single platform was detected."), yellow("Is it the one you expected?")) + + +def compile_project(project: str, **kwargs): + print("Invoking crytic-compile on the project, please wait...") + + try: + crytic_compile.CryticCompile(project, **kwargs) + except Exception as e: + with snip_section("Project compilation failed :( The following error was generated:"): + logging.exception(e) diff --git a/slither/tools/doctor/checks/versions.py b/slither/tools/doctor/checks/versions.py new file mode 100644 index 000000000..b7fbb1e13 --- /dev/null +++ b/slither/tools/doctor/checks/versions.py @@ -0,0 +1,58 @@ +from importlib import metadata +import json +from typing import Optional +from packaging.version import parse, LegacyVersion, Version +import urllib + +from slither.utils.colors import yellow, green + + +def get_installed_version(name: str) -> Optional[LegacyVersion | Version]: + try: + return parse(metadata.version(name)) + except metadata.PackageNotFoundError: + return None + + +def get_github_version(name: str) -> Optional[LegacyVersion | Version]: + try: + with urllib.request.urlopen( + f"https://api.github.com/repos/crytic/{name}/releases/latest" + ) as response: + text = response.read() + data = json.loads(text) + return parse(data["tag_name"]) + except: + return None + + +def show_versions(**kwargs) -> None: + versions = { + "Slither": (get_installed_version("slither-analyzer"), get_github_version("slither")), + "crytic-compile": ( + get_installed_version("crytic-compile"), + get_github_version("crytic-compile"), + ), + "solc-select": (get_installed_version("solc-select"), get_github_version("solc-select")), + } + + outdated = { + name + for name, (installed, latest) in versions.items() + if not installed or not latest or latest > installed + } + + for name, (installed, latest) in versions.items(): + color = yellow if name in outdated else green + print(f"{name + ':':<16}{color(installed or 'N/A'):<16} (latest is {latest or 'Unknown'})") + + if len(outdated) > 0: + print() + print( + yellow( + f"Please update {', '.join(outdated)} to the latest release before creating a bug report." + ) + ) + else: + print() + print(green("Your tools are up to date.")) diff --git a/slither/tools/doctor/packages.py b/slither/tools/doctor/packages.py deleted file mode 100644 index 6cbf48f89..000000000 --- a/slither/tools/doctor/packages.py +++ /dev/null @@ -1,24 +0,0 @@ -from importlib import metadata -import json -from typing import Optional -from packaging.version import parse, LegacyVersion, Version -import urllib - - -def get_installed_version(name: str) -> Optional[LegacyVersion | Version]: - try: - return parse(metadata.version(name)) - except metadata.PackageNotFoundError: - return None - - -def get_github_version(name: str) -> Optional[LegacyVersion | Version]: - try: - with urllib.request.urlopen( - f"https://api.github.com/repos/crytic/{name}/releases/latest" - ) as response: - text = response.read() - data = json.loads(text) - return parse(data["tag_name"]) - except: - return None