The cancellation of a queued proposal through the Governance::cancel function does not prevent its execution if the proposal has already been scheduled in the timelock. This creates a critical issue where a proposer goes below voting power threshold and his proposal gets canceled, or a proposer himself cancels a queued proposal yet the proposal may still be executed if the timelock conditions are met. Thereby, undermining the integrity of the governance process and potentially allowing for governance manipulation.
A user calls Governance::propose to create a proposal.
The proposal is successfully created and reaches the queued state after passing the voting period.
The proposer goes below threshold power can the Governance::cancel function is called asper this comment on the code:
The Governance::cancel function checks if the proposal is in the Executed state before allowing cancellation. However, it does not account for the Queued state, and since all it does is update the cancelled property to true ie proposal.canceled = true; it does not stop a proposal that has been added to TimlockController::scheduleBatch ie Queued from executing. Thus one of two things happen depending on design preference
Cancelled Proposals (possibly malicious) ends up executing.
Executed proposals possibly being labelled as cancelled, which reduced the governance integrity.
This vulnerability allows a proposer to cancel proposals that have already been approved and queued for execution, undermining the governance process and potentially allowing for malicious actions against the interests of the token holders.
Manual code review
Static analysis tools
Add State Check: Modify the cancel function to include a check for the Queued state.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.