Separate checks into a module

pull/1384/head
Emilio López 2 years ago
parent 5faddb4e7f
commit 16393d51fe
  1. 105
      slither/tools/doctor/__main__.py
  2. 18
      slither/tools/doctor/checks/__init__.py
  3. 59
      slither/tools/doctor/checks/platform.py
  4. 58
      slither/tools/doctor/checks/versions.py
  5. 24
      slither/tools/doctor/packages.py

@ -1,13 +1,9 @@
import argparse import argparse
import logging
from pathlib import Path
from crytic_compile import cryticparser 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
from slither.tools.doctor.utils import report_section, snip_section from slither.tools.doctor.checks import ALL_CHECKS
from slither.utils.colors import red, yellow, green
def parse_args() -> argparse.Namespace: def parse_args() -> argparse.Namespace:
@ -28,104 +24,13 @@ def parse_args() -> argparse.Namespace:
return parser.parse_args() 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(): def main():
args = parse_args() args = parse_args()
kwargs = vars(args) kwargs = vars(args)
with report_section("Software versions"): for check in ALL_CHECKS:
show_versions() with report_section(check.title):
check.function(**kwargs)
with report_section("Project platform"):
detect_platform(**kwargs)
with report_section("Project compilation"):
compile_project(**kwargs)
# TODO other checks
if __name__ == "__main__": if __name__ == "__main__":

@ -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),
]

@ -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)

@ -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."))

@ -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
Loading…
Cancel
Save