The RockPaperScissors::cancelGame() internal function is vulnerable to a reentrancy attack due to the use of low-level .call{value: ...}("") to refund ETH before updating all critical state or applying reentrancy protections.
The following lines in the code are vulnerable:
The contract sends ETH to both players using low-level call before completing the full game termination flow.
This gives a malicious player/contract (attacker) a chance to re-enter the same function again and again get a refund for the ETH bet.
Malicious players could exploit reentrancy to re-enter _cancelGame() multiple times and thus corrupt game state, claim extra tokens or ETH, or manipulate game logic.
Manual review
Paste the following contract code in your RockPaperScissos.t.sol file:
Now add the following in your test cases:
This will pass and thus verify the reentrancy issue.
Apply the checks-effects-interactions pattern:
Update all game state before making external calls.
Or use OpenZeppelin's ReentrancyGuard and mark all external-entry points as nonReentrant.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.