Description
The rankCandidates function has no checks on whether the voter is casting their vote for the zero address.
Impact
Voters can call rankCandidates and pass the zero address a chosen candidate, potentially leading them to throw away their vote.
Proof of Concept
function testCanVoteForZeroAddress() public {
orderedCandidates = [candidates[0]];
vm.prank(voters[0]);
rankedChoice.rankCandidates(orderedCandidates);
assertEq(rankedChoice.getUserCurrentVote(voters[0]), orderedCandidates);
}
Output:
[PASS] testCanVoteForZeroAddress() (gas: 78520)
Traces:
[78520] MoreTests::testCanVoteForZeroAddress()
├─ [0] VM::prank(ECRecover: [0x0000000000000000000000000000000000000001])
│ └─ ← [Return]
├─ [32541] RankedChoice::rankCandidates([0x0000000000000000000000000000000000000000])
│ └─ ← [Stop]
├─ [1370] RankedChoice::getUserCurrentVote(ECRecover: [0x0000000000000000000000000000000000000001]) [staticcall]
│ └─ ← [Return] [0x0000000000000000000000000000000000000000]
├─ [0] VM::assertEq([0x0000000000000000000000000000000000000000], [0x0000000000000000000000000000000000000000]) [staticcall]
│ └─ ← [Return]
└─ ← [Stop]
Recommendations
Add a zero-address check on _rankCandidates function and also create a new error RankedChoice__ZeroAddressNotAllowed:
+ error RankedChoice__ZeroAddressNotAllowed();
// in `_rankCandidates`
if (orderedCandidates.length > MAX_CANDIDATES) {
revert RankedChoice__InvalidInput();
}
if (!_isInArray(VOTERS, voter)) {
revert RankedChoice__InvalidVoter();
}
// Check for zero address
+ for (uint256 i = 0; i < orderedCandidates.length; i++) {
+ if (orderedCandidates[i] == address(0)) {
+ revert RankedChoice__ZeroAddressNotAllowed();
+ }
+ }