Santa's List

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

[H-4] Status.NICE == 0 means all uninitialized addresses default to NICE status

Root + Impact

Description

  • Solidity initializes unset mapping values to zero. The Status enum defines NICE as its first member, giving it the integer value 0. This means any address that has never been checked by Santa has s_theListCheckedOnce[addr] == Status.NICE and s_theListCheckedTwice[addr] == Status.NICE by default.

  • collectPresent grants an NFT to anyone whose status is NICE in both mappings. Because every unregistered address defaults to NICE, every address in existence is eligible to collect a present without Santa ever touching the list.

enum Status {
// @> NICE is 0 — the default value for all uninitialized storage
NICE,
EXTRA_NICE,
NAUGHTY,
NOT_CHECKED_TWICE
}
function collectPresent() external {
// ...
// @> Both mappings return NICE(0) for any address never checked by Santa
if (s_theListCheckedOnce[msg.sender] == Status.NICE && s_theListCheckedTwice[msg.sender] == Status.NICE) {
_mintAndIncrement();
return;
}

Risk

Likelihood:

  • Every address that has never interacted with the contract automatically passes the NICE check no prior action needed

  • This affects every wallet in existence by default at deployment

Impact:

  • The entire NFT supply can be minted by arbitrary, unknown addresses, completely bypassing Santa's list

  • The protocol's core mechanic, gating presents behind Santa's approval, is rendered completely meaningless

  • NFT scarcity is destroyed; the reward system has no integrity

Proof of Concept

A completely unknown address, never registered by Santa, calls collectPresent after Christmas and successfully mints an NFT due to the default zero-value mapping returning Status.NICE.

function testStrangerCollectsPresent() public {
address stranger = makeAddr("randomStranger");
// stranger was never added to santa's list — no checkList or checkTwice called
vm.warp(santasList.CHRISTMAS_2023_BLOCK_TIME() + 1);
vm.prank(stranger);
santasList.collectPresent(); // succeeds — stranger gets a free NFT
assertEq(santasList.balanceOf(stranger), 1);
}

Recommended Mitigation

Reorder the Status enum so that NOT_CHECKED_TWICE is the first (zero/default) value. This ensures that unregistered addresses cannot accidentally pass the NICE check.

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

Lead Judging Commences

ai-first-flight-judge Lead Judge 4 days 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!