President Elector

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

[L-02] Lack of Input Validation to Prevent Duplicate Candidates in Rankings

Summary

The RankedChoice contract's _rankCandidates function does not validate the orderedCandidates array to ensure that each candidate is unique. As a result, voters can rank the same candidate multiple times, potentially skewing the election results and deviating from the expected Ranked Choice Voting (RCV) protocol.

Vulnerability Details

  • Duplicate Candidates Allowed:

    • The _rankCandidates function assigns the orderedCandidates array to the voter’s ranking without checking for duplicates.

    function _rankCandidates(
    address[] memory orderedCandidates,
    address voter
    ) internal {
    ...
    s_rankings[voter][s_voteNumber] = orderedCandidates;
    }

Impact

  • Allows manipulation of election outcomes by inflating the vote count for specific candidates.

  • Voters may be unaware that duplicate rankings are allowed, leading to unintentional influence over election results.

Tools Used

  • Manual Code Review: Identified the absence of duplicate checks within the _rankCandidates function.

Recommendations

  • Implement Duplicate Checks:

    • Modify the _rankCandidates function to ensure that each candidate appears only once in the orderedCandidates array.

      function _rankCandidates(
      address[] memory orderedCandidates,
      address voter
      ) internal {
      // Check for duplicate candidates
      for (uint256 i = 0; i < orderedCandidates.length; i++) {
      for (uint256 j = i + 1; j < orderedCandidates.length; j++) {
      require(
      orderedCandidates[i] != orderedCandidates[j],
      "Duplicate candidates in ranking"
      );
      }
      }
      s_rankings[voter][s_voteNumber] = orderedCandidates;
      }
    • Emit informative error messages when duplicate candidates are detected to guide users in correcting their submissions.

      require(
      orderedCandidates[i] != orderedCandidates[j],
      string(
      abi.encodePacked(
      "Duplicate candidate detected: ",
      toString(orderedCandidates[i])
      )
      )
      );
    • Utilize more gas-efficient methods for duplicate detection, such as using a temporary mapping within the function scope.

      function _rankCandidates(
      address[] memory orderedCandidates,
      address voter
      ) internal {
      mapping(address => bool) memory seen;
      for (uint256 i = 0; i < orderedCandidates.length; i++) {
      require(!seen[orderedCandidates[i]], "Duplicate candidates in ranking");
      seen[orderedCandidates[i]] = true;
      }
      s_rankings[voter][s_voteNumber] = orderedCandidates;
      }

POC

  • Implement tests to verify that the contract correctly rejects rankings with duplicate candidates.

function testDuplicateCandidatesRejection() public {
address[] memory orderedCandidates = new address[]();
orderedCandidates[0] = address(1);
orderedCandidates[1] = address(1); // Duplicate
orderedCandidates[2] = address(3);
vm.expectRevert("Duplicate candidates in ranking");
rankedChoice.rankCandidates(orderedCandidates);
}
Updates

Lead Judging Commences

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

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

Appeal created

inallhonesty Lead Judge about 1 year 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.