President Elector

First Flight #24
Beginner FriendlyFoundry
100 EXP
View results
Submission Details
Severity: high
Invalid

No limit voting

Summary

A voter can rank the same person multiple times.

Vulnerability Details

In the function _rankCandidates the orderedCandidates array can consists of the same candidate.That means that a voter can call rankCandidates with the 10 rankings(MAX_CANDIDATES) being the same person.There is not a requirement or check to see if the candidates are the same candidate so it passes and does not revert anything. Here is a test:

function testIdiaVotes() public {
console.log("difthinsi 0", address(candidates[0]));
console.log("difthinsi 1", address(candidates[1]));
console.log("difthinsi 2", address(candidates[2]));
console.log("difthinsi 3", address(candidates[3]));
orderedCandidates = [candidates[0], candidates[0], candidates[0]];
uint256 startingIndex = 0;
uint256 endIndex = 10;
uint256 votes1 = 0;
for (uint256 i = startingIndex; i < endIndex; i++) {
vm.prank(voters[i]);
choice.rankCandidates(orderedCandidates);
votes1++;
}
uint256 startingIndex1 = endIndex + 1;
uint256 endIndex1 = 16;
uint256 votes2 = 0;
orderedCandidates = [candidates[3], candidates[2], candidates[1]];
for (uint256 j = startingIndex1; j < endIndex1; j++) {
vm.prank(voters[j]);
choice.rankCandidates(orderedCandidates);
votes2++;
}
uint256 startingIndex2 = endIndex1 + 1;
uint256 endIndex2 = 22;
uint256 votes3 = 0;
orderedCandidates = [candidates[3], candidates[2], candidates[5]];
for (uint256 j = startingIndex2; j < endIndex2; j++) {
vm.prank(voters[j]);
choice.rankCandidates(orderedCandidates);
votes3++;
}
console.log("Number of votes", votes1);
console.log("Number of votes2", votes2);
console.log("Number of votes3", votes3);
vm.warp(block.timestamp + 1460 days);
choice.selectPresident();
//assertEq(choice.getCurrentPresident(), candidates[0]);
choice.getCurrentPresident();
}

Foundry Test results:

RankedChoice::getCurrentPresident() [staticcall]:br│ └─ ← [Return] 0x1efF47bc3a10a45D4B230B5d10E37751FE6AA718:br└─ ← [Stop]

Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 100.85ms (44.23ms CPU time)

Ran 1 test suite in 806.26ms (100.85ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

Impact

This vulnerability can impact the voting logic of the contract giving false voting results in the end.It also imppacts trust in the voting system.

Tools Used

Manually/Foudnry

Recommendations

I would recommend changing the _rankCandidates function to include a check to ensure all the candidates are unique. For example you could add this:
\

function _rankCandidates(
address[] memory orderedCandidates,
address voter
) internal {
// Checks
if (orderedCandidates.length > MAX_CANDIDATES) {
revert RankedChoice__InvalidInput();
}
if (!_isInArray(VOTERS, voter)) {
revert RankedChoice__InvalidVoter();
}
+for (uint256 i = 0; i < orderedCandidates.length; i++) {
+ for (uint256 j = i + 1; j < orderedCandidates.length; j++) {
+ if (orderedCandidates[i] == orderedCandidates[j]) {
+ revert("Duplicate candidate");
+ }
+}
+ }
// Internal Effects
s_rankings[voter][s_voteNumber] = orderedCandidates;
}

}

Updates

Lead Judging Commences

inallhonesty Lead Judge 12 months ago
Submission Judgement Published
Validated
Assigned finding tags:

rankCandidates() allows duplicate votes inside the `orderedCandidates` array

Appeal created

inallhonesty Lead Judge 12 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

rankCandidates() allows duplicate votes inside the `orderedCandidates` array

Support

FAQs

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