Vulnerability Details
Contract : Governance
castVote
function is used to vote on proposals.
The standard practice is to disallow voting on proposals that are already cancelled.
There is no such check in the castVote
function to restrict voting on cancelled proposals.
https://github.com/Cyfrin/2025-02-raac/blob/main/contracts/core/governance/proposals/Governance.sol#L181-L194
function castVote(uint256 proposalId, bool support) external override returns (uint256) {
ProposalCore storage proposal = _proposals[proposalId];
if (proposal.startTime == 0) revert ProposalDoesNotExist(proposalId);
if (block.timestamp < proposal.startTime) {
revert VotingNotStarted(proposalId, proposal.startTime, block.timestamp);
}
if (block.timestamp > proposal.endTime) {
revert VotingEnded(proposalId, proposal.endTime, block.timestamp);
}
ProposalVote storage proposalVote = _proposalVotes[proposalId];
if (proposalVote.hasVoted[msg.sender]) {
revert AlreadyVoted(proposalId, msg.sender, block.timestamp);
}
......
Impact
Users can still cast votes on cancelled proposals.
Tools Used
Recommendations
Revert early if the proposal in question is already cancelled
function castVote(uint256 proposalId, bool support) external override returns (uint256) {
ProposalCore storage proposal = _proposals[proposalId];
if (proposal.cancelled == true) revert proposalAlreadyCancelled(proposalId);
.....