From 020d8638dab92b6fd952e988296e4d6b2478fdd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Wed, 4 Jan 2023 17:08:49 -0300 Subject: [PATCH 01/11] slither-doctor: remove LegacyVersion import, add `packaging` dependency This was used just for typing, and it was removed in a recent `packaging` version, so remove the LegacyVersion import. Also declare the missing `packaging` dependency in setup.py --- setup.py | 1 + slither/tools/doctor/checks/versions.py | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 86db4fa9ac..9cfbb29e40 100644 --- a/setup.py +++ b/setup.py @@ -12,6 +12,7 @@ packages=find_packages(), python_requires=">=3.8", install_requires=[ + "packaging", "prettytable>=0.7.2", "pycryptodome>=3.4.6", # "crytic-compile>=0.2.4", diff --git a/slither/tools/doctor/checks/versions.py b/slither/tools/doctor/checks/versions.py index 909bccf55b..90a478a1e5 100644 --- a/slither/tools/doctor/checks/versions.py +++ b/slither/tools/doctor/checks/versions.py @@ -3,19 +3,19 @@ from typing import Optional import urllib -from packaging.version import parse, LegacyVersion, Version +from packaging.version import parse, Version from slither.utils.colors import yellow, green -def get_installed_version(name: str) -> Optional[LegacyVersion | Version]: +def get_installed_version(name: str) -> Optional[Version]: try: return parse(metadata.version(name)) except metadata.PackageNotFoundError: return None -def get_github_version(name: str) -> Optional[LegacyVersion | Version]: +def get_github_version(name: str) -> Optional[Version]: try: with urllib.request.urlopen( f"https://api.github.com/repos/crytic/{name}/releases/latest" From 4c759ca6a4d0c0b6577a0a8e23ad80f948447865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Wed, 4 Jan 2023 17:29:24 -0300 Subject: [PATCH 02/11] slither-doctor: add new PATH checks This ensures PATH is correctly configured, and hints the user about what they might need to do to configure it correctly. --- slither/tools/doctor/checks/__init__.py | 2 + slither/tools/doctor/checks/paths.py | 56 +++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 slither/tools/doctor/checks/paths.py diff --git a/slither/tools/doctor/checks/__init__.py b/slither/tools/doctor/checks/__init__.py index 762c60b5d0..8a07419406 100644 --- a/slither/tools/doctor/checks/__init__.py +++ b/slither/tools/doctor/checks/__init__.py @@ -1,6 +1,7 @@ from typing import Callable, List from dataclasses import dataclass +from slither.tools.doctor.checks.paths import check_slither_path from slither.tools.doctor.checks.platform import compile_project, detect_platform from slither.tools.doctor.checks.versions import show_versions @@ -12,6 +13,7 @@ class Check: ALL_CHECKS: List[Check] = [ + Check("PATH configuration", check_slither_path), Check("Software versions", show_versions), Check("Project platform", detect_platform), Check("Project compilation", compile_project), diff --git a/slither/tools/doctor/checks/paths.py b/slither/tools/doctor/checks/paths.py new file mode 100644 index 0000000000..53fae78ad3 --- /dev/null +++ b/slither/tools/doctor/checks/paths.py @@ -0,0 +1,56 @@ +from pathlib import Path +from typing import List, Optional, Tuple +import shutil +import sysconfig + +from slither.utils.colors import yellow, green, red + + +def check_path_config(name: str) -> Tuple[bool, Optional[Path], List[Path]]: + binary_path = shutil.which(name) + possible_paths = [] + + for scheme in sysconfig.get_scheme_names(): + script_path = Path(sysconfig.get_path("scripts", scheme)) + purelib_path = Path(sysconfig.get_path("purelib", scheme)) + script_binary_path = shutil.which(name, path=script_path) + if script_binary_path is not None: + possible_paths.append((script_path, purelib_path)) + + binary_here = False + if binary_path is not None: + binary_path = Path(binary_path) + this_code = Path(__file__) + this_binary = list(filter(lambda x: this_code.is_relative_to(x[1]), possible_paths)) + binary_here = len(this_binary) > 0 and all( + binary_path.is_relative_to(script) for script, _ in this_binary + ) + + return binary_here, binary_path, list(set(script for script, _ in possible_paths)) + + +def check_slither_path(**_kwargs) -> None: + binary_here, binary_path, possible_paths = check_path_config("slither") + show_paths = False + + if binary_path: + print(green(f"`slither` found in PATH at `{binary_path}`.")) + if binary_here: + print(green("Its location matches this slither-doctor installation.")) + else: + print( + yellow( + f"This path does not correspond to this slither-doctor installation.\n" + + "Double-check the order of directories in PATH if you have several slither installations." + ) + ) + show_paths = True + else: + print(red("`slither` was not found in PATH.")) + show_paths = True + + if show_paths: + print() + print("Consider adding one of the following directories to PATH:") + for path in possible_paths: + print(f" * {path}") From c531681a8a7ec52e4b724336929d9d7377b21927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Wed, 4 Jan 2023 17:31:01 -0300 Subject: [PATCH 03/11] ci: slither-doctor: add workflow --- .github/workflows/doctor.yml | 74 ++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 .github/workflows/doctor.yml diff --git a/.github/workflows/doctor.yml b/.github/workflows/doctor.yml new file mode 100644 index 0000000000..3d1150eef4 --- /dev/null +++ b/.github/workflows/doctor.yml @@ -0,0 +1,74 @@ +--- +name: CI (slither-doctor) + +defaults: + run: + shell: bash + +on: + push: + branches: + - master + - dev + pull_request: + paths: + - 'slither/tools/doctor/**' + - '.github/workflows/doctor.yml' + +jobs: + doctor: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: ["ubuntu-latest", "windows-2022"] + python: ["3.8", "3.9", "3.10", "3.11"] + steps: + - uses: actions/checkout@v3 + + - name: Set up Python ${{ matrix.python }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python }} + + - name: Try system-wide Slither + run: | + pip3 install . + + # escape cwd so python doesn't pick up local module + cd / + + echo "Via module" + python3 -m slither.tools.doctor . + + echo "Via binary" + slither-doctor . + + - name: Try user Slither + run: | + pip3 install --user . + + # escape cwd so python doesn't pick up local module + cd / + + echo "Via module" + python3 -m slither.tools.doctor . + + echo "Via binary" + slither-doctor . + + - name: Try venv Slither + run: | + python3 -m venv venv + source venv/bin/activate + hash -r + pip3 install . + + # escape cwd so python doesn't pick up local module + cd / + + echo "Via module" + python3 -m slither.tools.doctor . + + echo "Via binary" + slither-doctor . From b2ce73108adb4ef50e65c079f51305d9dd20d917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Wed, 4 Jan 2023 17:53:48 -0300 Subject: [PATCH 04/11] slither-doctor: fix "unsupported format string passed to Version.__format__" --- slither/tools/doctor/checks/versions.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/slither/tools/doctor/checks/versions.py b/slither/tools/doctor/checks/versions.py index 90a478a1e5..ec7ef1d1f3 100644 --- a/slither/tools/doctor/checks/versions.py +++ b/slither/tools/doctor/checks/versions.py @@ -45,7 +45,9 @@ def show_versions(**_kwargs) -> None: 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'})") + print( + f"{name + ':':<16}{color(str(installed) or 'N/A'):<16} (latest is {str(latest) or 'Unknown'})" + ) if len(outdated) > 0: print() From 21daf7348997273424d8445998aab93c105e083f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Wed, 4 Jan 2023 17:55:27 -0300 Subject: [PATCH 05/11] slither-doctor: log on stdout This avoids mixed output in e.g. CI outputs --- slither/tools/doctor/__main__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/slither/tools/doctor/__main__.py b/slither/tools/doctor/__main__.py index 94ae865ec2..b9b4c54977 100644 --- a/slither/tools/doctor/__main__.py +++ b/slither/tools/doctor/__main__.py @@ -1,4 +1,6 @@ import argparse +import logging +import sys from crytic_compile import cryticparser @@ -25,6 +27,9 @@ def parse_args() -> argparse.Namespace: def main(): + # log on stdout to keep output in order + logging.basicConfig(stream=sys.stdout, level=logging.DEBUG, force=True) + args = parse_args() kwargs = vars(args) From 20198b9618d21e394d0b1599adefe46ec1c03102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Wed, 4 Jan 2023 17:57:28 -0300 Subject: [PATCH 06/11] slither-doctor: fix `is_relative_to` on Python 3.8 --- slither/tools/doctor/checks/paths.py | 33 ++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/slither/tools/doctor/checks/paths.py b/slither/tools/doctor/checks/paths.py index 53fae78ad3..4adf3b159a 100644 --- a/slither/tools/doctor/checks/paths.py +++ b/slither/tools/doctor/checks/paths.py @@ -1,12 +1,41 @@ from pathlib import Path from typing import List, Optional, Tuple import shutil +import sys import sysconfig from slither.utils.colors import yellow, green, red +def path_is_relative_to(path: Path, relative_to: Path) -> bool: + """ + Check if a path is relative to another one. + + Compatibility wrapper for Path.is_relative_to + """ + if sys.version_info >= (3, 9, 0): + return path.is_relative_to(relative_to) + + path_parts = path.resolve().parts + relative_to_parts = relative_to.resolve().parts + + if len(path_parts) < len(relative_to_parts): + return False + + for (a, b) in zip(path_parts, relative_to_parts): + if a != b: + return False + + return True + + def check_path_config(name: str) -> Tuple[bool, Optional[Path], List[Path]]: + """ + Check if a given Python binary/script is in PATH. + :return: Returns if the binary on PATH corresponds to this installation, + its path (if present), and a list of possible paths where this + binary might be found. + """ binary_path = shutil.which(name) possible_paths = [] @@ -21,9 +50,9 @@ def check_path_config(name: str) -> Tuple[bool, Optional[Path], List[Path]]: if binary_path is not None: binary_path = Path(binary_path) this_code = Path(__file__) - this_binary = list(filter(lambda x: this_code.is_relative_to(x[1]), possible_paths)) + this_binary = list(filter(lambda x: path_is_relative_to(this_code, x[1]), possible_paths)) binary_here = len(this_binary) > 0 and all( - binary_path.is_relative_to(script) for script, _ in this_binary + path_is_relative_to(binary_path, script) for script, _ in this_binary ) return binary_here, binary_path, list(set(script for script, _ in possible_paths)) From e65031ebd55e08da23f97919a3f7ecc49981177d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Wed, 4 Jan 2023 17:59:03 -0300 Subject: [PATCH 07/11] ci: slither-doctor: fix Windows venv execution --- .github/workflows/doctor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/doctor.yml b/.github/workflows/doctor.yml index 3d1150eef4..612374bc25 100644 --- a/.github/workflows/doctor.yml +++ b/.github/workflows/doctor.yml @@ -60,7 +60,7 @@ jobs: - name: Try venv Slither run: | python3 -m venv venv - source venv/bin/activate + source venv/bin/activate || source venv/Scripts/activate hash -r pip3 install . From 21967c2525c4dcf6bc40aa487e1eb08e46c4156f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Wed, 4 Jan 2023 19:18:45 -0300 Subject: [PATCH 08/11] ci: slither-doctor: group output --- .github/workflows/doctor.yml | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/.github/workflows/doctor.yml b/.github/workflows/doctor.yml index 612374bc25..582e0dc87a 100644 --- a/.github/workflows/doctor.yml +++ b/.github/workflows/doctor.yml @@ -33,42 +33,54 @@ jobs: - name: Try system-wide Slither run: | + echo "::group::Install slither" pip3 install . + echo "::endgroup::" # escape cwd so python doesn't pick up local module cd / - echo "Via module" + echo "::group::Via module" python3 -m slither.tools.doctor . + echo "::endgroup::" - echo "Via binary" + echo "::group::Via binary" slither-doctor . + echo "::endgroup::" - name: Try user Slither run: | + echo "::group::Install slither" pip3 install --user . + echo "::endgroup::" # escape cwd so python doesn't pick up local module cd / - echo "Via module" + echo "::group::Via module" python3 -m slither.tools.doctor . + echo "::endgroup::" - echo "Via binary" + echo "::group::Via binary" slither-doctor . + echo "::endgroup::" - name: Try venv Slither run: | + echo "::group::Install slither" python3 -m venv venv source venv/bin/activate || source venv/Scripts/activate hash -r pip3 install . + echo "::endgroup::" # escape cwd so python doesn't pick up local module cd / - echo "Via module" + echo "::group::Via module" python3 -m slither.tools.doctor . + echo "::endgroup::" - echo "Via binary" + echo "::group::Via binary" slither-doctor . + echo "::endgroup::" From d7538795a50eea61633daf54c86ae25ce2109e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Wed, 4 Jan 2023 20:06:47 -0300 Subject: [PATCH 09/11] ci: slither-doctor: disable on branches, add dispatch --- .github/workflows/doctor.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/doctor.yml b/.github/workflows/doctor.yml index 582e0dc87a..811680db40 100644 --- a/.github/workflows/doctor.yml +++ b/.github/workflows/doctor.yml @@ -1,22 +1,19 @@ --- -name: CI (slither-doctor) +name: CI defaults: run: shell: bash on: - push: - branches: - - master - - dev + workflow_dispatch: pull_request: paths: - 'slither/tools/doctor/**' - '.github/workflows/doctor.yml' jobs: - doctor: + slither-doctor: runs-on: ${{ matrix.os }} strategy: fail-fast: false From b01ba03fa8da270f51151926b789448928301afa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Wed, 4 Jan 2023 20:08:14 -0300 Subject: [PATCH 10/11] ci: slither-doctor: disable Windows with Python 3.8 --- .github/workflows/doctor.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/doctor.yml b/.github/workflows/doctor.yml index 811680db40..b6124216a1 100644 --- a/.github/workflows/doctor.yml +++ b/.github/workflows/doctor.yml @@ -20,6 +20,10 @@ jobs: matrix: os: ["ubuntu-latest", "windows-2022"] python: ["3.8", "3.9", "3.10", "3.11"] + exclude: + # strange failure + - os: windows-2022 + python: 3.8 steps: - uses: actions/checkout@v3 From 407d35cbd8bb21c34e9731ddaf77751ff496a840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Wed, 4 Jan 2023 20:31:09 -0300 Subject: [PATCH 11/11] slither-doctor: fix lint error --- slither/tools/doctor/checks/paths.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/slither/tools/doctor/checks/paths.py b/slither/tools/doctor/checks/paths.py index 4adf3b159a..d388847ef4 100644 --- a/slither/tools/doctor/checks/paths.py +++ b/slither/tools/doctor/checks/paths.py @@ -69,8 +69,8 @@ def check_slither_path(**_kwargs) -> None: else: print( yellow( - f"This path does not correspond to this slither-doctor installation.\n" - + "Double-check the order of directories in PATH if you have several slither installations." + "This path does not correspond to this slither-doctor installation.\n" + + "Double-check the order of directories in PATH if you have several Slither installations." ) ) show_paths = True