President Elector

First Flight #24
Beginner FriendlyFoundry
100 EXP
View results
Submission Details
Severity: medium
Valid

Voters can vote for the current president after the 4 year election period, so long as `RankedChoice::selectPresident` has not yet been called

Description

Since there is no check for the duration remaining in RankedChoice::rankCandidates, voters can cast votes in the current election even after the i_presidentalDuration for that election has passed, so long as selectPresident has not yet been called.

Impact

Users might think all valid votes have been cast for the current election once the duration period of 1460 has passed since last election, however if no one has called selectPresident then additional votes may be cast and influence the outcome of the election.

Proof of Concept

function testCanVoteAfterDurationIfSelectPresidentNotYetCalled() public {
assert(rankedChoice.getCurrentPresident() != candidates[0]);
// first voter votes for candidates 0, 1, then 2
orderedCandidates = [candidates[0], candidates[1], candidates[2]];
uint256 startingIndex = 0;
uint256 endingIndex = 1;
for (uint256 i = startingIndex; i < endingIndex; i++) {
vm.prank(voters[i]);
rankedChoice.rankCandidates(orderedCandidates);
}
// then we skip ahead to end of presidential period
vm.warp(block.timestamp + rankedChoice.getDuration());
// after 4 year period, but with selectPresident not yet called, voter[1] will submit votes for candidates 3, 1, then 4
startingIndex = endingIndex + 1;
endingIndex = 3;
orderedCandidates = [candidates[3], candidates[1], candidates[4]];
for (uint256 i = startingIndex; i < endingIndex; i++) {
vm.prank(voters[i]);
rankedChoice.rankCandidates(orderedCandidates);
}
// now, candidate[0] has 1 1st-choice vote from voter[0], and candidate[3] also has 1 1st-choice votes that came in after the election period -> as the README says, when candidates are tied the earliest in the list is dropped, so candidate[3] becomes president -even though the vote was cast after the election!!
rankedChoice.selectPresident();
assertEq(rankedChoice.getCurrentPresident(), candidates[3]);
}
Output: [PASS] testCanVoteAfterDurationIfSelectPresidentNotYetCalled() (gas: 1466189) Traces: [1585589] MyTest::testCanVoteAfterDurationIfSelectPresidentNotYetCalled() ├─ [2291] RankedChoice::getCurrentPresident() [staticcall] │ └─ ← [Return] MyTest: [0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496] ├─ [0] VM::prank(0x0000000000000000000000000000000000000064) │ └─ ← [Return] ├─ [312448] RankedChoice::rankCandidates([0x00000000000000000000000000000000000000C8, 0x00000000000000000000000000000000000000C9, 0x00000000000000000000000000000000000000ca]) │ └─ ← [Stop] ├─ [236] RankedChoice::getDuration() [staticcall] │ └─ ← [Return] 126144000 [1.261e8] ├─ [0] VM::warp(126144001 [1.261e8]) │ └─ ← [Return] ├─ [0] VM::prank(0x0000000000000000000000000000000000000066) │ └─ ← [Return] ├─ [108756] RankedChoice::rankCandidates([0x00000000000000000000000000000000000000CB, 0x00000000000000000000000000000000000000C9, 0x00000000000000000000000000000000000000cc]) │ └─ ← [Stop] ├─ [1035997] RankedChoice::selectPresident() │ └─ ← [Stop] ├─ [291] RankedChoice::getCurrentPresident() [staticcall] │ └─ ← [Return] 0x00000000000000000000000000000000000000CB ├─ [0] VM::assertEq(0x00000000000000000000000000000000000000CB, 0x00000000000000000000000000000000000000CB) [staticcall] │ └─ ← [Return] └─ ← [Stop]

Recommendations

Add the notTimeToVote check that is used in selectPresident to rankCandidates as well:

function rankCandidates(address[] memory orderedCandidates) external {
+ if (
+ block.timestamp - s_previousVoteEndTimeStamp <=
+ i_presidentalDuration
+ ) {
+ revert RankedChoice__NotTimeToVote();
+ }
_rankCandidates(orderedCandidates, msg.sender);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

No checks for time constraints to prevent voters from submitting or modifying votes after the voting period had ended

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.