President Elector

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

Missing Time Constraint in `_rankCandidates` Function Exposes Voting System to Manipulation

Missing Time Constraint in _rankCandidates Function Exposes Voting System to Manipulation

Summary

The RankedChoice contract had a critical vulnerability in the _rankCandidates function, where there were no checks for time constraints to prevent voters from submitting or modifying votes after the voting period had ended. This flaw could have allowed malicious actors to influence the voting process by voting at any time or changing their vote even after the majority had cast theirs. By introducing a strict time validation, we ensure that votes are only cast within the defined voting period, preserving the integrity of the election process.

Vulnerability Details

Issue:

The _rankCandidates function previously lacked a time-based restriction, which allowed voters to rank candidates at any point in time, even after the voting period was supposed to have ended. This could result in voters casting or modifying their votes long after other voters, potentially manipulating the results by having more information about the candidates' standings.

  • Lack of Time Constraints: The original function allowed voters to submit or modify their vote even after the voting period had expired.

  • Impact on Voting Integrity: Without time validation, voters could unfairly influence the results by voting or modifying their votes after observing others' actions.

Code Analysis

Original Function:

function _rankCandidates(
address[] memory orderedCandidates,
address voter
) internal {
// Checks
if (orderedCandidates.length > MAX_CANDIDATES) {
revert RankedChoice__InvalidInput();
}
if (!_isInArray(VOTERS, voter)) {
revert RankedChoice__InvalidVoter();
}
// Internal Effects
s_rankings[voter][s_voteNumber] = orderedCandidates;
}

Key Issues:

  • No Time Constraint: Voters could submit votes or change them at any time, even after the official voting period had ended, undermining the fairness of the election.

  • Potential Exploitation: By voting later than the majority, a voter could gain an unfair advantage by observing the trends in voting behavior and then casting their vote strategically.

Modified Function:

The function has been updated to include time validation, ensuring votes can only be submitted within the allowed voting period and preventing any changes once a vote has been cast.

function _rankCandidates(
address[] memory orderedCandidates,
address voter
) internal {
// Checks
if (orderedCandidates.length > MAX_CANDIDATES) {
revert RankedChoice__InvalidInput();
}
if (!_isInArray(VOTERS, voter)) {
revert RankedChoice__InvalidVoter();
}
// Validate that the voting period is still active
require(
block.timestamp <= s_previousVoteEndTimeStamp + i_presidentalDuration,
"Voting period has ended"
);
// Ensure the voter has not already submitted a vote
if (s_rankings[voter][s_voteNumber].length > 0) {
revert("Cannot change vote after submission");
}
// Internal Effects
s_rankings[voter][s_voteNumber] = orderedCandidates;
}

Proof of Concept (PoC)

  1. Before the fix, a voter could submit or change their vote after the majority of votes had already been cast, or even after the voting period had ended.

  2. After the fix, any attempt to vote outside of the voting period is rejected with a "Voting period has ended" error.

// Voter attempts to vote after the voting period
vm.warp(block.timestamp + i_presidentalDuration + 1);
vm.expectRevert("Voting period has ended");
rankedChoice.rankCandidates(orderedCandidates);

Test Cases

1. Test for Time Constraint Enforcement

This test checks that the function correctly rejects any votes submitted after the voting period has ended.

function testCannotChangeVoteAfterMajority() public {
address[] memory orderedCandidates = new address[]();
orderedCandidates[0] = candidates[0];
orderedCandidates[1] = candidates[1];
orderedCandidates[2] = candidates[2];
// Simulate majority of voters voting
for (uint256 i = 0; i < 80; i++) {
vm.prank(voters[i]);
rankedChoice.rankCandidates(orderedCandidates);
}
// Simulate a delay that exceeds the voting period
vm.warp(block.timestamp + rankedChoice.getDuration() + 1);
vm.prank(voters[81]);
vm.expectRevert("Voting period has ended");
rankedChoice.rankCandidates(orderedCandidates);
}

2. Test for Duplicate Votes Prevention

This test ensures that voters cannot change their vote once it has been submitted.

function testFuzzChangeVote(address[] memory orderedCandidates) public {
// Ensure the vote is valid
vm.assume(orderedCandidates.length > 0 && orderedCandidates.length <= MAX_CANDIDATES);
// Voter 0 votes first
vm.prank(voters[0]);
rankedChoice.rankCandidates(orderedCandidates);
// Attempt to change vote after submission
address[] memory newOrderedCandidates = new address[]();
newOrderedCandidates[0] = candidates[1];
vm.prank(voters[0]);
vm.expectRevert("Cannot change vote after submission");
rankedChoice.rankCandidates(newOrderedCandidates);
}

Impact

  • Manipulation of Votes: Without the time constraint, voters could manipulate the system by casting their vote after observing the majority, thus gaining an unfair advantage.

  • Unfair Voting: Late submissions could undermine the fairness of the election and potentially change the final result.

  • Data Integrity: Allowing post-period votes or changes to submitted votes could corrupt the integrity of the election.

Tools Used

  • Manual code review

  • Unit testing and fuzz testing using Forge to validate time constraints

Recommendations

  • Enforce Time Limits: Always enforce time-based restrictions in voting systems to prevent manipulation and ensure fairness.

  • Strict Validation: Ensure that once a vote is cast, it cannot be modified, and that votes are only accepted within the designated voting period.

Conclusion

This report provides a detailed explanation of the vulnerability found in the _rankCandidates function, along with the modifications made to ensure voting integrity and fairness.

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.