Summary
The _getBeneficiaryIndex
function encounters an issue where the beneficiary index retrieval process lacks proper handling for cases where the beneficiary is not found in the beneficiaries
array.
Vulnerability Details
The _getBeneficiaryIndex
function iterates through the beneficiaries
array to find the index of a specific beneficiary. However, it does not handle scenarios where the beneficiary is not found in the array. As a result, if the beneficiary is not present, it will return 0
, that is the first element of the beneficiaries
array, and the removeBeneficiary
function use it to remove a _beneficiary
account, so when a non-existent _beneficiary
account is specified for removal, it will result in the removal of the first account in the array instead of throwing an error.
function removeBeneficiary(address _beneficiary) external onlyOwner {
uint256 indexToRemove = _getBeneficiaryIndex(_beneficiary);
delete beneficiaries[indexToRemove];
}
function _getBeneficiaryIndex(address _beneficiary) public view returns (uint256 _index) {
for (uint256 i = 0; i < beneficiaries.length; i++) {
if (_beneficiary == beneficiaries[i]) {
_index = i;
break;
}
}
}
Here is the test case:
function test_removeNonBeneficiary() public {
address alice = makeAddr("alice");
address bob = makeAddr("bob");
vm.startPrank(owner);
im.addBeneficiery(alice);
assertEq(
bytes32(uint256(uint160(address(alice)))),
vm.load(address(im), bytes32(uint256(keccak256(abi.encode(5))) + 0))
);
im.removeBeneficiary(bob);
assertEq(
bytes32(uint256(uint160(address(0)))),
vm.load(address(im), bytes32(uint256(keccak256(abi.encode(5))) + 0))
);
vm.stopPrank();
}
Impact
The current implementation of the _getBeneficiaryIndex
function does not work when attempting to retrieve the index of a beneficiary that does not exist in the beneficiaries
array. And do damage to relative functions, such as removeBeneficiary
Tools Used
Manual
Recommendations
Use EnumerableSet
of the OpenZeppelin:
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
xxx;
contract InheritanceManager is Trustee {
using EnumerableSet for EnumerableSet.AddressSet;
EnumerableSet.AddressSet private beneficiaries;
function removeBeneficiary(address _beneficiary) external onlyOwner {
require(beneficiaries.remove(_beneficiary), "Beneficiary does not exist");
_setDeadline();
}
function _getBeneficiaryIndex(address _beneficiary) public view returns (uint256 _index) {
require(beneficiaries.contains(_beneficiary), "Beneficiary not found");
for (uint256 i = 0; i < beneficiaries.length(); i++) {
if (beneficiaries.at(i) == _beneficiary) {
_index = i;
break;
}
}
}
}