Skip to content

Commit

Permalink
fetch remappings from crytic-compile, add two tests
Browse files Browse the repository at this point in the history
  • Loading branch information
tuturu-tech committed Apr 24, 2024
1 parent 1fbd4d2 commit a0371a1
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 28 deletions.
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
35 changes: 20 additions & 15 deletions fuzz_utils/utils/remappings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,39 @@
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*)"

working_dir = slither.crytic_compile.working_dir
platform_config = slither.crytic_compile.platform.config(working_dir)

remappings: str = ""

if os.path.exists("remappings.txt"):
with open("remappings.txt", "r", encoding="utf-8") as file:
remappings = file.read()

output = subprocess.run(["forge", "remappings"], capture_output=True, text=True, check=True)
forge_remappings = str(output.stdout)
if platform_config:
remappings = "\n".join(platform_config.remappings)
else:
output = subprocess.run(["forge", "remappings"], capture_output=True, text=True, check=True)
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)
if len(oz_matches) == 0:
oz_matches = re.findall(openzeppelin, forge_remappings)
sol_matches = re.findall(solmate, remappings)
if len(sol_matches) == 0:
sol_matches = re.findall(solmate, forge_remappings)
prop_matches = re.findall(properties, remappings)
if len(prop_matches) == 0:
prop_matches = re.findall(properties, forge_remappings)

if include_attacks and len(oz_matches) == 0 and len(sol_matches) == 0:
handle_exit(
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)

0 comments on commit a0371a1

Please sign in to comment.