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

fix issue-1029, FP on unprotected-upgrade detector #1046

Merged
merged 5 commits into from
Feb 11, 2022
Merged
Show file tree
Hide file tree
Changes from 4 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
81 changes: 52 additions & 29 deletions slither/detectors/statements/unprotected_upgradeable.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,23 @@ def _can_be_destroyed(contract) -> List[Function]:
return targets


def _has_initializer_modifier(functions: List[Function]) -> bool:
for f in functions:
for m in f.modifiers:
if m.name == "initializer":
return True
return False


def _has_protected_initialize(functions: List[Function]) -> bool:
Jaime-Iglesias marked this conversation as resolved.
Show resolved Hide resolved
for f in functions:
if f.name == "initialize":
for m in f.modifiers:
if m.name == "initializer":
return True
return False


class UnprotectedUpgradeable(AbstractDetector):

ARGUMENT = "unprotected-upgrade"
Expand Down Expand Up @@ -61,34 +78,40 @@ def _detect(self):

for contract in self.compilation_unit.contracts_derived:
if contract.is_upgradeable:
functions_that_can_destroy = _can_be_destroyed(contract)
if functions_that_can_destroy:
initiliaze_functions = [f for f in contract.functions if f.name == "initialize"]
vars_init_ = [
init.all_state_variables_written() for init in initiliaze_functions
]
vars_init = [item for sublist in vars_init_ for item in sublist]

vars_init_in_constructors_ = [
f.all_state_variables_written() for f in contract.constructors
]
vars_init_in_constructors = [
item for sublist in vars_init_in_constructors_ for item in sublist
]
if vars_init and (set(vars_init) - set(vars_init_in_constructors)):
info = (
[
contract,
" is an upgradeable contract that does not protect its initiliaze functions: ",
]
+ initiliaze_functions
+ [
". Anyone can delete the contract with: ",
]
+ functions_that_can_destroy
)

res = self.generate_result(info)
results.append(res)
if not _has_initializer_modifier(
contract.constructors
) or not _has_protected_initialize(contract.functions):
functions_that_can_destroy = _can_be_destroyed(contract)
if functions_that_can_destroy:
initiliaze_functions = [
f for f in contract.functions if f.name == "initialize"
]

vars_init_ = [
init.all_state_variables_written() for init in initiliaze_functions
]
vars_init = [item for sublist in vars_init_ for item in sublist]

vars_init_in_constructors_ = [
f.all_state_variables_written() for f in contract.constructors
]
vars_init_in_constructors = [
item for sublist in vars_init_in_constructors_ for item in sublist
]
if vars_init and (set(vars_init) - set(vars_init_in_constructors)):
info = (
[
contract,
" is an upgradeable contract that does not protect its initiliaze functions: ",
]
+ initiliaze_functions
+ [
". Anyone can delete the contract with: ",
]
+ functions_that_can_destroy
)

res = self.generate_result(info)
results.append(res)

return results
17 changes: 16 additions & 1 deletion tests/detectors/unprotected-upgrade/0.4.25/Fixed.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ contract Fixed is Initializable{
}
}


contract Not_Upgradeable{
}

Expand All @@ -36,4 +35,20 @@ contract UpgradeableNoDestruct is Initializable{
require(owner == address(0));
owner = msg.sender;
}
}

contract Fixed2 is Initializable {
address owner;

constructor() public initializer {}

function initialize() external initializer {
require(owner == address(0));
owner = msg.sender;
}

function kill() external {
require(msg.sender == owner);
selfdestruct(owner);
}
}
17 changes: 16 additions & 1 deletion tests/detectors/unprotected-upgrade/0.5.16/Fixed.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ contract Fixed is Initializable{
}
}


contract Not_Upgradeable{
}

Expand All @@ -36,4 +35,20 @@ contract UpgradeableNoDestruct is Initializable{
require(owner == address(0));
owner = msg.sender;
}
}

contract Fixed2 is Initializable {
address payable owner;

constructor() public initializer {}

function initialize() external initializer {
require(owner == address(0));
owner = msg.sender;
}

function kill() external {
require(msg.sender == owner);
selfdestruct(owner);
}
}
16 changes: 16 additions & 0 deletions tests/detectors/unprotected-upgrade/0.6.11/Fixed.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,20 @@ contract UpgradeableNoDestruct is Initializable{
require(owner == address(0));
owner = msg.sender;
}
}

contract Fixed2 is Initializable {
address payable owner;

constructor() public initializer {}

function initialize() external initializer {
require(owner == address(0));
owner = msg.sender;
}

function kill() external {
require(msg.sender == owner);
selfdestruct(owner);
}
}
21 changes: 18 additions & 3 deletions tests/detectors/unprotected-upgrade/0.7.6/Fixed.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import "./Initializable.sol";
contract Fixed is Initializable{
address payable owner;

constructor() public{
constructor() {
owner = msg.sender;
}

Expand All @@ -21,19 +21,34 @@ contract Fixed is Initializable{
}
}


contract Not_Upgradeable{
}

contract UpgradeableNoDestruct is Initializable{
address payable owner;

constructor() public{
constructor() {
owner = msg.sender;
}

function initialize() external initializer{
require(owner == address(0));
owner = msg.sender;
}
}

contract Fixed2 is Initializable {
address payable owner;

constructor() initializer {}

function initialize() external initializer {
require(owner == address(0));
owner = msg.sender;
}

function kill() external {
require(msg.sender == owner);
selfdestruct(owner);
}
}