Summary
The LiquidationKeeper contract lacks proper owner validation in the constructor and initialization process, which could potentially lead to ownership control issues in the upgrade process.
Vulnerability Details
constructor() {
_disableInitializers();
}
function initialize(address owner, IPerpsEngine perpsEngine) external initializer {
__BaseKeeper_init(owner);
if (address(perpsEngine) == address(0)) {
revert Errors.ZeroInput("perpsEngine");
}
LiquidationKeeperStorage storage self = _getLiquidationKeeperStorage();
self.perpsEngine = perpsEngine;
}
The vulnerability exists because:
No validation of owner address in initialization
No two-step ownership transfer process
No delay mechanism for critical ownership changes
No events emitted for ownership changes
Impact
Potential ownership transfer to invalid addresses
No ability to recover from incorrect ownership settings
Lack of transparency in ownership changes
Risk of immediate ownership changes without time for community review
Tools Used
Recommendations
-
Implement comprehensive owner validation:
function initialize(address owner, IPerpsEngine perpsEngine) external initializer {
if (owner == address(0)) {
revert Errors.ZeroInput("owner");
}
__BaseKeeper_init(owner);
if (address(perpsEngine) == address(0)) {
revert Errors.ZeroInput("perpsEngine");
}
LiquidationKeeperStorage storage self = _getLiquidationKeeperStorage();
self.perpsEngine = perpsEngine;
emit KeeperInitialized(owner, address(perpsEngine));
}
event KeeperInitialized(address indexed owner, address indexed perpsEngine);
2/ Add a two-step ownership transfer process:
address private _pendingOwner;
uint256 private constant OWNERSHIP_TRANSFER_DELAY = 2 days;
uint256 private _transferRequestTime;
function transferOwnership(address newOwner) public override onlyOwner {
if (newOwner == address(0)) {
revert Errors.ZeroInput("newOwner");
}
_pendingOwner = newOwner;
_transferRequestTime = block.timestamp;
emit OwnershipTransferInitiated(owner(), newOwner);
}
function acceptOwnership() external {
require(msg.sender == _pendingOwner, "Not pending owner");
require(block.timestamp >= _transferRequestTime + OWNERSHIP_TRANSFER_DELAY, "Transfer delay not met");
address oldOwner = owner();
_transferOwnership(_pendingOwner);
_pendingOwner = address(0);
_transferRequestTime = 0;
emit OwnershipTransferred(oldOwner, _pendingOwner);
}
event OwnershipTransferInitiated(address indexed currentOwner, address indexed pendingOwner);
3 / Add emergency recovery mechanisms:
function renounceOwnership() public override onlyOwner {
revert Errors.OperationNotAllowed("Ownership renouncement disabled");
}
These changes will enhance the security and transparency of ownership management while providing safeguards against potential ownership-related issues.