Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🔨 fallback to Foundry remappings #44

Merged
merged 2 commits into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion fuzz_utils/parsing/commands/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def template_command(args: Namespace) -> None:

# Check if dependencies are installed
include_attacks = bool("attacks" in config and len(config["attacks"]) > 0)
remappings = find_remappings(include_attacks)
remappings = find_remappings(include_attacks, slither)

generator = HarnessGenerator(config, slither, remappings)
generator.generate_templates()
Expand Down
25 changes: 18 additions & 7 deletions fuzz_utils/utils/remappings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,35 @@
import os
import subprocess
import re
from slither import Slither
from fuzz_utils.utils.crytic_print import CryticPrint
from fuzz_utils.utils.error_handler import handle_exit


def find_remappings(include_attacks: bool) -> dict:
# pylint: disable=too-many-locals
def find_remappings(include_attacks: bool, slither: Slither) -> dict:
"""Finds the remappings used and returns a dict with the values"""
CryticPrint().print_information("Checking dependencies...")
openzeppelin = r"(\S+)=lib\/openzeppelin-contracts\/(?!\S*lib\/)(\S*)"
solmate = r"(\S+)=lib\/solmate\/(?!\S*lib\/)(\S*)"
properties = r"(\S+)=lib\/properties\/(?!\S*lib\/)(\S*)"
remappings: str = ""

if os.path.exists("remappings.txt"):
with open("remappings.txt", "r", encoding="utf-8") as file:
remappings = file.read()
working_dir = slither.crytic_compile.working_dir
platform_config = slither.crytic_compile.platform.config(working_dir)

remappings: str = ""
if platform_config:
remappings = "\n".join(platform_config.remappings)
else:
output = subprocess.run(["forge", "remappings"], capture_output=True, text=True, check=True)
remappings = str(output.stdout)
forge_remaps = str(output.stdout).split("\n")

if os.path.exists("remappings.txt"):
with open("remappings.txt", "r", encoding="utf-8") as file:
remappings_file = file.read().split("\n")
# Converting to set to remove duplicates, back to list for joining
forge_remaps = list(set(forge_remaps + remappings_file))

remappings = "\n".join(forge_remaps)

oz_matches = re.findall(openzeppelin, remappings)
sol_matches = re.findall(solmate, remappings)
Expand Down
13 changes: 8 additions & 5 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def setup_foundry_temp_dir(tmp_path_factory: Any) -> None:
["forge", "install", "transmissions11/solmate", "--no-git"], check=True, cwd=temp_dir
)
# Create remappings file
create_remappings_file(temp_dir)
create_remappings_file(temp_dir, None)

# Delete unnecessary files
counter_path = temp_dir / "src" / "Counter.sol"
Expand Down Expand Up @@ -155,13 +155,16 @@ def setup_foundry_temp_dir(tmp_path_factory: Any) -> None:
os.chdir(temp_dir)


def create_remappings_file(temp_dir: Any) -> None:
def create_remappings_file(temp_dir: Any, out_str: str | None) -> None:
"""Creates a remappings file"""
remappings = os.path.join(temp_dir, "remappings.txt")
with open(remappings, "w", encoding="utf-8") as outfile:
outfile.write(
"forge-std/=lib/forge-std/src/\nproperties/=lib/properties/contracts/\nsolmate/=lib/solmate/src/\nsrc/=src/"
)
if out_str:
outfile.write(out_str)
else:
outfile.write(
"forge-std/=lib/forge-std/src/\nproperties/=lib/properties/contracts/\nsolmate/=lib/solmate/src/\nsrc/=src/"
)


def copy_directory_contents(src_dir: str, dest_dir: str) -> None:
Expand Down
4 changes: 2 additions & 2 deletions tests/test_harness.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,9 @@ def run_harness(
expected_functions: set[str],
) -> None:
"""Sets up the HarnessGenerator"""
remappings = find_remappings(False)
config = copy.deepcopy(default_config)
slither = Slither(compilation_path)
remappings = find_remappings(False, slither)
config = copy.deepcopy(default_config)

config["name"] = harness_name
config["compilationPath"] = compilation_path
Expand Down
68 changes: 63 additions & 5 deletions tests/test_remapping_detection.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os
from typing import Any
import pytest
from slither import Slither
from fuzz_utils.utils.remappings import find_remappings
from .conftest import create_remappings_file

Expand All @@ -11,6 +12,7 @@ def test_remappings_are_detected_when_no_file(
) -> None:
"""Test if remappings are fetched when no remappings.txt file exists"""
temp_dir = os.getcwd()
slither = Slither(temp_dir)

# Remove remappings file
remappings_path = os.path.join(temp_dir, "remappings.txt")
Expand All @@ -19,24 +21,80 @@ def test_remappings_are_detected_when_no_file(

# Look for remappings, expecting not to fail
try:
find_remappings(True)
find_remappings(True, slither)
except SystemExit:
# Re-create remappings file and raise error
create_remappings_file(temp_dir)
create_remappings_file(temp_dir, None)
pytest.fail("Finding remappings failed with SystemExit")
except Exception: # pylint: disable=broad-except
# Re-create remappings file and raise error
create_remappings_file(temp_dir)
create_remappings_file(temp_dir, None)
pytest.fail("Finding remappings failed with an Exception")

# If success
create_remappings_file(temp_dir)
create_remappings_file(temp_dir, None)


def test_remappings_are_detected_when_file_exists(
setup_foundry_temp_dir: Any, # pylint: disable=unused-argument
) -> None:
"""Test if remappings are fetched when a remappings.txt file exists"""
temp_dir = os.getcwd()
slither = Slither(temp_dir)
assert os.path.exists(os.path.join(temp_dir, "remappings.txt"))
find_remappings(True)
find_remappings(True, slither)


def test_remappings_are_detected_when_incomplete_remappings_file(
setup_foundry_temp_dir: Any, # pylint: disable=unused-argument
) -> None:
"""Test if remappings are fetched when a remappings.txt file exists but is incomplete"""
temp_dir = os.getcwd()
slither = Slither(temp_dir)

# Modify remappings file to contain an incomplete list of remappings
out_str = "forge-std/=lib/forge-std/src/\nsolmate/=lib/solmate/src/\nsrc/=src/"
create_remappings_file(temp_dir, out_str)

# Look for remappings, expecting not to fail
try:
find_remappings(True, slither)
except SystemExit:
# Reset remappings file and raise error
create_remappings_file(temp_dir, None)
pytest.fail("Finding remappings failed with SystemExit")
except Exception: # pylint: disable=broad-except
# Reset remappings file and raise error
create_remappings_file(temp_dir, None)
pytest.fail("Finding remappings failed with an Exception")

# If success, reset remappings file
create_remappings_file(temp_dir, None)


def test_remappings_are_detected_when_file_target(
setup_foundry_temp_dir: Any, # pylint: disable=unused-argument
) -> None:
"""Test if remappings are fetched when the slither target is a .sol file"""
temp_dir = os.getcwd()
file_path = os.path.join(temp_dir, "src", "BasicTypes.sol")
slither = Slither(file_path)

# Modify remappings file to contain an incomplete list of remappings
out_str = "forge-std/=lib/forge-std/src/\nsolmate/=lib/solmate/src/\nsrc/=src/"
create_remappings_file(temp_dir, out_str)

# Look for remappings, expecting not to fail
try:
find_remappings(True, slither)
except SystemExit:
# Reset remappings file and raise error
create_remappings_file(temp_dir, None)
pytest.fail("Finding remappings failed with SystemExit")
except Exception: # pylint: disable=broad-except
# Reset remappings file and raise error
create_remappings_file(temp_dir, None)
pytest.fail("Finding remappings failed with an Exception")

# If success, reset remappings file
create_remappings_file(temp_dir, None)
Loading