AOSSIE-Org / AOSSIE-Org/Agora-Blockchain

fix(RankedBallot): Duplicate candidate IDs in vote array allow vote manipulation

Aperta
#244 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub
Lingua principale
JavaScript
Stelle
97
Fork
199
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Descrizione

### Is there an existing issue for this?

- [x] I have searched the existing issues

### Issue Description

**File:** `blockchain/contracts/ballots/RankedBallot.sol`

The `vote()` function in `RankedBallot.sol` does not validate that each candidate ID in the input array is unique. A voter can submit a `voteArr` like `[0, 0, 0]` to assign 1st-place, 2nd-place, and 3rd-place points all to candidate 0, inflating that candidate's score by 3x.

#### Root Cause

```solidity
function vote(uint[] memory voteArr) external onlyOwner {
uint totalCandidates = candidateVotes.length;
if (voteArr.length != totalCandidates) revert VoteInputLength();

for (uint i = 0; i < totalCandidates; i++) {
// No uniqueness check — voteArr[0,0,0] lets candidate 0 get all points
candidateVotes[voteArr[i]] += totalCandidates - i;
}
}
```

There are two additional missing validations:
1. No bounds check: `voteArr[i]` could be >= `totalCandidates`, causing an out-of-bounds array access (panic revert, no custom error).
2. No uniqueness check: the same candidate ID can appear multiple times, violating ranked-choice voting semantics.

#### Expected Behavior

`voteArr` must be a **permutation** of `[0, 1, ..., totalCandidates-1]`:
- Each candidate ID appears **exactly once**
- No out-of-bounds ID is accepted

#### Proposed Fix

Add two new custom errors (`InvalidCandidateID`, `DuplicateCandidateID`) and validate inside `vote()`:

```solidity
error InvalidCandidateID();
error DuplicateCandidateID();

function vote(uint[] memory voteArr) external onlyOwner {
uint totalCandidates = candidateVotes.length;
if (voteArr.length != totalCandidates) revert VoteInputLength();

bool[] memory seen = new bool[](totalCandidates);
for (uint i = 0; i < totalCandidates; i++) {
uint candidateId = voteArr[i];
if (candidateId >= totalCandidates) revert InvalidCandidateID();
if (seen[candidateId]) revert DuplicateCandidateID();
seen[candidateId] = true;
candidateVotes[candidateId] += totalCandidates - i;
}
}
```

#### Severity

**High** — directly exploitable to manipulate election results permanently on-chain.

### Record

- [x] I have synced all my node versions as mentioned in the project
- [x] I am using the same version of npm as is the project
- [x] My current branch is in sync with the development branch
- [x] I want to work on this issue

Guida per i contributori

Nessuna guida per i contributori indicizzata per questo repository

Valutazione

Questa issue non è ancora stata valutata.

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.