Santa's List

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

No upper bound on collectPresent Christmas window allows NFT minting indefinitely after Christmas

Root + Impact

Description

collectPresent checks that block.timestamp >= CHRISTMAS_2023_BLOCK_TIME but never checks an upper bound. The known-issues section explicitly states the window should be "approximately Christmas, give or take 24 hours." The missing upper bound means any NICE or EXTRA_NICE address can wait arbitrarily long — months or years — after Christmas 2023 and still collect their present, permanently inflating NFT supply and SantaToken supply beyond the intended seasonal window.

Risk

uint256 public constant CHRISTMAS_2023_BLOCK_TIME = 1_703_480_381;
function collectPresent() external {
// Only checks lower bound — no upper bound
if (block.timestamp < CHRISTMAS_2023_BLOCK_TIME) {
revert SantasList__NotChristmasYet();
}
// ... rest of function executes forever after Christmas
}

The contract is deployed on Arbitrum where block.timestamp reflects real wall-clock time. Any NICE/EXTRA_NICE address that missed the intended 48-hour window can still mint at any future time.

Proof of Concept

// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;
import {Test} from "forge-std/Test.sol";
import {SantasList} from "../src/SantasList.sol";
import {SantaToken} from "../src/SantaToken.sol";
contract ChristmasWindowPoCTest is Test {
SantasList santasList;
SantaToken santaToken;
address santa = makeAddr("santa");
address user = makeAddr("user");
uint256 constant CHRISTMAS = 1_703_480_381;
uint256 constant ONE_YEAR = 365 days;
function setUp() public {
vm.prank(santa);
santasList = new SantasList();
santaToken = SantaToken(santasList.getSantaToken());
vm.startPrank(santa);
santasList.checkList(user, SantasList.Status.NICE);
santasList.checkTwice(user, SantasList.Status.NICE);
vm.stopPrank();
}
function test_canCollectOneYearLater() public {
// Warp to one full year after Christmas 2023
vm.warp(CHRISTMAS + ONE_YEAR);
// Still works — no upper bound check
vm.prank(user);
santasList.collectPresent();
assertEq(santasList.balanceOf(user), 1);
}
}

Recommended Mitigation

Add an upper bound constant and check it in collectPresent:

// Add constant (24 hours after Christmas, per the known-issues allowance)
uint256 public constant CHRISTMAS_END_TIME = 1_703_480_381 + 1 days;
function collectPresent() external {
if (block.timestamp < CHRISTMAS_2023_BLOCK_TIME) {
revert SantasList__NotChristmasYet();
}
// Add upper bound check
if (block.timestamp > CHRISTMAS_END_TIME) {
revert SantasList__ChristmasIsOver();
}
// ... rest unchanged
}
// Add corresponding error
error SantasList__ChristmasIsOver();
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge 1 day ago
Submission Judgement Published
Validated
Assigned finding tags:

[L-01] collectPresent() can be called at anytime after christmas

## Description The christmas present should only be collected with 24 hours before or after christmas. But the present can be minted at anytime after christmas. ## Vulnerability Details Documenation mentioned that "The Christmas date is approximate, if it's more then 24 hours before or after Christmas, please report that. Otherwise, it's OK." The `collectPresent()` has only checked that the present cannot be collected before the christmas. But hasn't checked in the case of after christmas collection. ```javascript function collectPresent() external { if (block.timestamp < CHRISTMAS_2023_BLOCK_TIME) { revert SantasList__NotChristmasYet(); } if (balanceOf(msg.sender) > 0) { revert SantasList__AlreadyCollected(); } if (s_theListCheckedOnce[msg.sender] == Status.NICE && s_theListCheckedTwice[msg.sender] == Status.NICE) { _mintAndIncrement(); return; } else if ( s_theListCheckedOnce[msg.sender] == Status.EXTRA_NICE && s_theListCheckedTwice[msg.sender] == Status.EXTRA_NICE ) { _mintAndIncrement(); i_santaToken.mint(msg.sender); return; } revert SantasList__NotNice(); } ``` `uint256 public constant CHRISTMAS_2023_BLOCK_TIME = 1_703_480_381;` The UTC time for this epoch is : `Monday, 25 December 2023 04:59:41` . The present can only be collected after approx 5 hours after the christmas arrived. But it can be collectable at anytime after Christmas. As there is no check for the after christmas case. ## Impact The impact of this vulnerability is that the intended use of the protocol is not acquired. Proof Of Code : ```javascript function testCollectPresentNiceAfterChristmas() public { vm.startPrank(santa); santasList.checkList(user, SantasList.Status.NICE); santasList.checkTwice(user, SantasList.Status.NICE); vm.stopPrank(); vm.warp(1703900189); // Saturday, 30 December 2023 01:36:29 vm.startPrank(user); santasList.collectPresent(); assertEq(santasList.balanceOf(user), 1); vm.stopPrank(); } ``` Add this test to `SantasListTest.t.sol` and run `forge test --mt testCollectPresentNiceAfterChristmas` to test. You can observe that the present is collectable at Saturday, 30 December 2023 01:36:29. ## Recommendations Include check for the after 24 hours of christmas. ```diff function collectPresent() external { - if (block.timestamp < CHRISTMAS_2023_BLOCK_TIME) { + if (block.timestamp < CHRISTMAS_2023_BLOCK_TIME && block.timestamp > 1703554589 ) { revert SantasList__NotChristmasYet(); } if (balanceOf(msg.sender) > 0) { revert SantasList__AlreadyCollected(); } if (s_theListCheckedOnce[msg.sender] == Status.NICE && s_theListCheckedTwice[msg.sender] == Status.NICE) { _mintAndIncrement(); return; } else if ( s_theListCheckedOnce[msg.sender] == Status.EXTRA_NICE && s_theListCheckedTwice[msg.sender] == Status.EXTRA_NICE ) { _mintAndIncrement(); i_santaToken.mint(msg.sender); return; } revert SantasList__NotNice(); } ```

Support

FAQs

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

Give us feedback!