Root + Impact
-
Normal behavior (per docs): "Propose new transactions (permission is tied to the role, so any signer can propose)."
-
Specific issue: The implementation requires the owner to propose transactions — signers cannot propose.
function proposeTransaction(...)
external
nonReentrant
noneZeroAddress(to)
@> onlyOwner returns (uint256)
{
return _proposeTransaction(to, value, data);
}
Proof of Concept
function test_poc() public {
multiSigTimelock.grantSigningRole(SIGNER_TWO);
vm.prank(SIGNER_TWO);
vm.expectRevert(
abi.encodeWithSelector(bytes4(keccak256("OwnableUnauthorizedAccount(address)")), SIGNER_TWO)
);
uint256 txIdOne = multiSigTimelock.proposeTransaction(
SIGNER_TWO,
0,
""
);
}
Recommended Mitigation
- function proposeTransaction(...) external nonReentrant noneZeroAddress(to) onlyOwner returns (uint256) {
+ function proposeTransaction(...) external nonReentrant noneZeroAddress(to) onlyRole(SIGNING_ROLE) returns (uint256) {
return _proposeTransaction(to, value, data);
}