Skip to content
Open
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
63 changes: 63 additions & 0 deletions slither/tools/mutator/mutators/ACN.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
from slither.core.declarations.solidity_variables import SolidityFunction
from slither.core.expressions.call_expression import CallExpression
from slither.core.expressions.identifier import Identifier
from slither.core.expressions.unary_operation import UnaryOperation, UnaryOperationType
from slither.tools.mutator.mutators.abstract_mutator import AbstractMutator
from slither.tools.mutator.utils.patch import create_patch_with_line


class ACN(AbstractMutator):
NAME = "ACN"
HELP = "Assert Condition Negation"

def _mutate(self) -> dict:
result: dict = {}

for function in self.contract.functions_and_modifiers_declared:
if not self.should_mutate_function(function):
continue

for node in function.nodes:
if not self.should_mutate_node(node):
continue

try:
expression = node.expression
except AttributeError:
continue

if not isinstance(expression, CallExpression):
continue

if not isinstance(expression.called, Identifier):
continue

called = expression.called.value
if not isinstance(called, SolidityFunction):
continue

if not called.name.startswith("assert("):
continue

if not expression.arguments:
continue

condition = expression.arguments[0]

start = condition.source_mapping.start
stop = start + condition.source_mapping.length
old_str = condition.source_mapping.content
line_no = condition.source_mapping.lines[0]
new_str = f"!({old_str})"

create_patch_with_line(
result,
self.in_file,
start,
stop,
old_str,
new_str,
line_no,
)

return result
1 change: 1 addition & 0 deletions slither/tools/mutator/mutators/all_mutators.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@
from slither.tools.mutator.mutators.FHR import FHR # severity medium
from slither.tools.mutator.mutators.MIA import MIA # severity medium
from slither.tools.mutator.mutators.ROR import ROR # severity medium
from slither.tools.mutator.mutators.ACN import ACN # severity medium
from slither.tools.mutator.mutators.RR import RR # severity high
from slither.tools.mutator.mutators.CR import CR # severity high
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ contract Counter {
}

function setNumber(uint256 newNumber) public {
assert(newNumber != 7);
number = newNumber;
}

Expand Down
32 changes: 32 additions & 0 deletions tests/tools/mutator/test_mutator.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from slither.tools.mutator.utils.file_handling import get_sol_file_list, backup_source_file
from slither.utils.function import get_function_id
from slither.tools.mutator.mutators.RR import RR
from slither.tools.mutator.mutators.ACN import ACN


TEST_DATA_DIR = Path(__file__).resolve().parent / "test_data"
Expand Down Expand Up @@ -283,3 +284,34 @@ def test_should_mutate_function_includes_modifier(solc_binary_path):
for mod in contract.modifiers:
if mod.name == "onlyOwner":
assert mutator.should_mutate_function(mod) is True


def test_acn_mutates_assert_condition(solc_binary_path):
solc_path = solc_binary_path("0.8.15")
file_path = (TEST_DATA_DIR / "test_source_unit" / "src" / "Counter.sol").as_posix()
sl = Slither(file_path, solc=solc_path, compile_force_framework="solc")
contract = next(c for c in sl.contracts if c.name == "Counter")

with tempfile.TemporaryDirectory() as tmpdir:
mutator = ACN(
sl.compilation_units[0],
timeout=30,
testing_command="true",
testing_directory=None,
contract_instance=contract,
solc_remappings=None,
verbose=False,
output_folder=Path(tmpdir),
dont_mutate_line=[],
target_selectors=None,
target_modifiers=None,
)

patches = mutator._mutate()
assert "patches" in patches
assert file_path in patches["patches"]

assert any(
patch["old_string"] == "newNumber != 7" and patch["new_string"] == "!(newNumber != 7)"
for patch in patches["patches"][file_path]
)
Loading