Skip to content

Commit

Permalink
working version
Browse files Browse the repository at this point in the history
  • Loading branch information
Yhtiyar authored and olegggatttor committed Apr 17, 2024
1 parent 9a57d84 commit 8d3a6c4
Show file tree
Hide file tree
Showing 6 changed files with 845 additions and 1 deletion.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from typing import List
from slither.utils.output import Output
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.core.declarations import Function, Contract
from slither.core.cfg.node import Node


class BalancerReadonlyReentrancy(AbstractDetector):
"""
Sees if a contract has a beforeTokenTransfer function.
"""

ARGUMENT = "pess-balancer-readonly-reentrancy" # slither will launch the detector with slither.py --detect mydetector
HELP = "beforeTokenTransfer function does not follow OZ documentation"
IMPACT = DetectorClassification.LOW
CONFIDENCE = DetectorClassification.HIGH

WIKI = (
"https://docs.openzeppelin.com/contracts/4.x/extending-contracts#rules_of_hooks"
)
WIKI_TITLE = "Before Token Transfer"
WIKI_DESCRIPTION = "Follow OZ documentation using their contracts"
WIKI_EXPLOIT_SCENARIO = "-"
WIKI_RECOMMENDATION = (
"Make sure that beforeTokenTransfer function is used in the correct way."
)

VULNERABLE_FUNCTION_CALLS = ["getRate", "getPoolTokens"]
visited = []
contains_reentrancy_check = {}

def is_balancer_integration(self, c: Contract) -> bool:
"""
Iterates over all external function calls, and checks the interface/contract name
for a specific keywords to decide if the contract integrates with balancer
"""
for (
fcontract,
_,
) in c.all_high_level_calls:
contract_name = fcontract.name.lower()
if any(map(lambda x: x in contract_name, ["balancer", "ivault", "pool"])):
return True

def _has_reentrancy_check(self, node: Node) -> bool:
if node in self.visited:
return self.contains_reentrancy_check[node]

self.visited.append(node)
self.contains_reentrancy_check[node] = False

for c, n in node.high_level_calls:
if isinstance(n, Function):
if (
n.name == "ensureNotInVaultContext"
and c.name == "VaultReentrancyLib"
) or (
n.name == "manageUserBalance"
): # TODO check if errors out
self.contains_reentrancy_check[node] = True
return True

has_check = False
for internal_call in node.internal_calls:
if isinstance(internal_call, Function):
has_check |= self._has_reentrancy_check(internal_call)
# self.contains_reentrancy_check[internal_call] |= has_check

self.contains_reentrancy_check[node] = has_check
return has_check

def _check_function(self, function: Function) -> list:
has_dangerous_call = False
dangerous_call = None
for n in function.nodes:
for c, fc in n.high_level_calls:
if isinstance(fc, Function):
if fc.name in self.VULNERABLE_FUNCTION_CALLS:
dangerous_call = fc
has_dangerous_call = True
break

if has_dangerous_call and not any(
[self._has_reentrancy_check(node) for node in function.nodes]
):
print("READONLY_REENTRANCY!!!")

def _detect(self) -> List[Output]:
"""Main function"""
res = []
for contract in self.compilation_unit.contracts_derived:
if not self.is_balancer_integration(contract):
continue
for f in contract.functions_and_modifiers_declared:
self._check_function(f)
return res
Loading

0 comments on commit 8d3a6c4

Please sign in to comment.