Status.NICE Enum Default Allows Any Unchecked Address to Collect a Presentsrc/SantasList.sol (L69–74, L154–165)
Status.NICE is the first member of the Status enum, giving it the integer value 0. In Solidity, all mapping values default to 0 for uninitialized keys. Therefore, any address that has never been passed to checkList() or checkTwice() already appears as Status.NICE in both state mappings.
collectPresent() checks for NICE status but never validates that Santa actually performed the listing:
This finding is independent of F1: even if checkList() had the correct onlySanta modifier, any fresh address would still pass the NICE checks without anyone having ever interacted with them.
Likelihood: High
Any Ethereum address can exploit this immediately after Christmas without any prior setup.
No capital, tokens, or prior interactions required — a single transaction suffices.
Impact: High
The entire purpose of Santa's list — permissioned present distribution — is nullified.
When combined with F3 (NFT transfer double-mint bypass), this enables unlimited NFT minting by any single address.
Severity: High
unknownUser has never interacted with the protocol. After Christmas, they call collectPresent() and receive an NFT because the mapping returns Status.NICE (0) by default.
Reorder the Status enum so that 0 maps to a non-privileged state that requires explicit Santa action before it is upgraded:
With this change, all uninitialized addresses default to NOT_CHECKED_TWICE (0), which causes collectPresent() to revert. Santa must explicitly call checkList() and checkTwice() to elevate an address to NICE or EXTRA_NICE.
## Description `collectPresent` function is supposed to be called by users that are considered `NICE` or `EXTRA_NICE` by Santa. This means Santa is supposed to call `checkList` function to assigned a user to a status, and then call `checkTwice` function to execute a double check of the status. Currently, the enum `Status` assigns its default value (0) to `NICE`. This means that both mappings `s_theListCheckedOnce` and `s_theListCheckedTwice` consider every existent address as `NICE`. In other words, all users are by default double checked as `NICE`, and therefore eligible to call `collectPresent` function. ## Vulnerability Details The vulnerability arises due to the order of elements in the enum. If the first value is `NICE`, this means the enum value for each key in both mappings will be `NICE`, as it corresponds to `0` value. ## Impact The impact of this vulnerability is HIGH as it results in a flawed mechanism of the present distribution. Any unchecked address is currently able to call `collectPresent` function and mint an NFT. This is because this contract considers by default every address with a `NICE` status (or 0 value). ## Proof of Concept The following Foundry test will show that any user is able to call `collectPresent` function after `CHRISTMAS_2023_BLOCK_TIME` : ``` function testCollectPresentIsFlawed() external { // prank an attacker's address vm.startPrank(makeAddr("attacker")); // set block.timestamp to CHRISTMAS_2023_BLOCK_TIME vm.warp(1_703_480_381); // collect present without any check from Santa santasList.collectPresent(); vm.stopPrank(); } ``` ## Recommendations I suggest to modify `Status` enum, and use `UNKNOWN` status as the first one. This way, all users will default to `UNKNOWN` status, preventing the successful call to `collectPresent` before any check form Santa: ``` enum Status { UNKNOWN, NICE, EXTRA_NICE, NAUGHTY } ``` After modifying the enum, you can run the following test and see that `collectPresent` call will revert if Santa didn't check the address and assigned its status to `NICE` or `EXTRA_NICE` : ``` function testCollectPresentIsFlawed() external { // prank an attacker's address vm.startPrank(makeAddr("attacker")); // set block.timestamp to CHRISTMAS_2023_BLOCK_TIME vm.warp(1_703_480_381); // collect present without any check from Santa vm.expectRevert(SantasList.SantasList__NotNice.selector); santasList.collectPresent(); vm.stopPrank(); } ```
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.