Santa's List

AI First Flight #3
Beginner FriendlyFoundry
EXP
View results
Submission Details
Severity: high
Valid

F-2: Default Enum Value (NICE=0) Allows Any Unchecked Address to Collect

Root + Impact

Description

  • In Solidity, uninitialized mapping entries return 0, which equals the first
    enum member. The Status enum declares NICE as its first member (index 0).

  • Any address never touched by checkList or checkTwice automatically has
    both s_theListCheckedOnce and s_theListCheckedTwice equal to
    Status.NICE (the default value 0).

  • The collectPresent() function checks both mappings for Status.NICE at
    line 154 — this check passes for every address that was never checked by
    Santa, granting them unauthorized access to mint NFTs.

// SantasList.sol:69-74
enum Status {
NICE, // @> = 0 = Solidity default for uninitialized mappings
EXTRA_NICE, // = 1
NAUGHTY, // = 2
NOT_CHECKED_TWICE // = 3
}
// SantasList.sol:154
if (s_theListCheckedOnce[msg.sender] == Status.NICE
&& s_theListCheckedTwice[msg.sender] == Status.NICE) {
// @> Both return 0 (NICE) for any address never checked by Santa
_mintAndIncrement();
return;
}

Risk

Likelihood:

  • Every address that has never interacted with Santa automatically qualifies
    as NICE. Zero setup required — just call collectPresent() after the
    Christmas timestamp.

  • The Christmas 2023 timestamp (1_703_480_381) has already passed, so this
    is immediately exploitable on deployment to Arbitrum.

Impact:

  • Any address can mint an NFT without Santa's approval.

  • Unlimited addresses can collect, inflating NFT supply infinitely.

  • The entire naughty/nice checking mechanism is rendered completely
    meaningless — the core protocol feature does not work.

Proof of Concept

  • A completely random address that was never checked by Santa on either list
    calls collectPresent() after the Christmas timestamp. Both
    s_theListCheckedOnce and s_theListCheckedTwice return their default
    value (0 = NICE), so the status checks pass. The address receives an NFT
    despite never being approved by Santa.

function test_F2_uncheckedAddressCollects() public {
vm.warp(1_703_480_381); // Christmas 2023
address nobody = makeAddr("never_checked_by_santa");
// nobody was never checked by Santa on either list
// Both mappings return default 0 = Status.NICE
vm.prank(nobody);
santasList.collectPresent(); // No revert — default NICE passes!
assertEq(santasList.balanceOf(nobody), 1); // Got an NFT
}

Recommended Mitigation

  • Reorder the enum so the default value (0) corresponds to a non-approved
    status. By placing NOT_CHECKED_TWICE first, any uninitialized mapping
    entry defaults to "not checked" instead of "nice", preventing unchecked
    addresses from passing the status check in collectPresent().

enum Status {
+ NOT_CHECKED_TWICE,
NICE,
EXTRA_NICE,
- NAUGHTY,
- NOT_CHECKED_TWICE
+ NAUGHTY
}
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 2 hours ago
Submission Judgement Published
Validated
Assigned finding tags:

[H-02] All addresses are considered `NICE` by default and are able to claim a NFT through `collectPresent` function before any Santa check.

## 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(); } ```

Support

FAQs

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

Give us feedback!