Puppy Raffle

AI First Flight #1
Beginner FriendlyFoundrySolidityNFT
EXP
View results
Submission Details
Impact: low
Likelihood: low
Invalid

Low 2 form get Active Player Index

Root + Impact

Description

getActivePlayerIndex() returns 0 in two indistinguishable situations — the player genuinely at index 0, and "address is not a player at all" (lines 110-117):

function getActivePlayerIndex(address player) external view returns (uint256) {
for (uint256 i = 0; i < players.length; i++) {
if (players[i] == player) {
return i;
}
}
@> return 0;
}

The function's documented purpose is locating the index for refund(playerIndex). A UI or integration that looks up a non-player address receives 0 — the same value as a real index-0 player — and may then call refund(0). Instead of reverting on a bad lookup, the caller silently operates on the wrong player: whoever sits at index 0 gets their ticket refunded to someone else's msg.sender... and in fact refund(0) would then revert on the playerAddress == msg.sender check — but only by luck of the access control, not by the lookup failing loudly. Any future caller that trusts the index without re-validating acts on the wrong player with no warning.

Risk

Likelihood: Low — requires an integrator to trust the return value without a separate membership check.

Impact: Low — no direct fund loss inside the contract today, but a classic footgun: silent wrong-index operations instead of an explicit failure.

Proof of Concept

The Foundry test below demonstrates the ambiguity directly:

function test_PoC_L1_GetActivePlayerIndexAmbiguous() public {
address[] memory p = new address[](1);
p[0] = playerOne;
puppyRaffle.enterRaffle{value: 1 ether}(p);
assertEq(puppyRaffle.getActivePlayerIndex(playerOne), 0);
// a non-player ALSO returns 0: the caller cannot tell index 0 from "not found"
assertEq(puppyRaffle.getActivePlayerIndex(address(0xBEEF)), 0);
}

Test assertions and measured result (forge test --match-test test_PoC_L1_GetActivePlayerIndexAmbiguous):

Recommended Mitigation

Fail loudly on a missed lookup instead of returning a value that collides with a valid index. Reverting (or returning a sentinel like type(uint256).max that can never be a real index) turns the silent wrong-player footgun into an explicit, catchable failure — integrators can no longer accidentally act on index 0.

for (uint256 i = 0; i < players.length; i++) {
if (players[i] == player) {
return i;
}
}
- return 0;
+ revert("PuppyRaffle: Player not found");
}
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 1 hour ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!