Root + Impact-
Status enum defines NICE at position 0. In Solidity, mapping default value is 0. So s_theListCheckedTwice[anyAddress] defaults to NICE even before Santa calls checkTwice. This breaks two-pass verification system.
The SantasList uses two-pass: checkList() first pass, checkTwice() second pass. Only addresses passing both should collect.
Root Cause
File: SantasList.sol - enum definition
enum Status {
NICE, // 0 - default for all addresses
EXTRA_NICE,
NAUGHTY,
NOT_CHECKED_TWICE
}
Both mappings:
mapping(address => Status) s_theListCheckedOnce
mapping(address => Status) s_theListCheckedTwice
Default to 0 = NICE for any address that was never checked. In collectPresent():
if (s_theListCheckedOnce[msg.sender] == Status.NICE
&& s_theListCheckedTwice[msg.sender] == Status.NICE) {
_mintAndIncrement();
}
Second condition is always true by default. No second pass needed.
Impact
Likelihood: High - No attacker action required for vulnerable state
Impact: Medium - Breaks core invariant of dual confirmation
Proof of Concept
function test_DefaultBypass() public {
SantasList list = new SantasList(santa);
vm.prank(santa);
list.checkList(alice, SantasList.Status.NICE);
vm.prank(alice);
list.collectPresent();
assertEq(list.balanceOf(alice), 1);
}
Description-Description
Status enum defines NICE at position 0. In Solidity, mapping default value is 0. So s_theListCheckedTwice[anyAddress] defaults to NICE even before Santa calls checkTwice. This breaks two-pass verification system.
## Summary
Status enum defines NICE at position 0. In Solidity, mapping default value is 0. So `s_theListCheckedTwice[anyAddress]` defaults to NICE even before Santa calls checkTwice. This breaks two-pass verification system.
## Vulnerability Details
File: SantasList.sol - enum definition
enum Status {
NICE,
EXTRA_NICE,
NAUGHTY,
NOT_CHECKED_TWICE
}
- s_theListCheckedOnce defaults to 0 = NICE
- s_theListCheckedTwice defaults to 0 = NICE
In collectPresent():
```solidity
if (s_theListCheckedOnce[msg.sender] == Status.NICE
&& s_theListCheckedTwice[msg.sender] == Status.NICE) {
_mintAndIncrement();
}
Risk
Likelihood:Affects every address from deployment
Impact:Two-pass design is broken. Intended security requirement of double confirmation from Santa does not hold. Any address that passes first check can collect without second check.
Proof of Concept-Description
Status enum defines NICE at position 0. In Solidity, mapping default value is 0. So s_theListCheckedTwice[anyAddress] defaults to NICE even before Santa calls checkTwice. This breaks two-pass verification system.
The SantasList uses two-pass: checkList() first pass, checkTwice() second pass. Only addresses passing both should collect.
Root Cause
File: SantasList.sol - enum definition
function test_DefaultBypass() public {
SantasList list = new SantasList(santa);
vm.prank(santa);
list.checkList(alice, SantasList.Status.NICE);
vm.prank(alice);
list.collectPresent();
assertEq(list.balanceOf(alice), 1);
}
Recommended Mitigation-Put NOT_CHECKED or UNKNOWN at index 0:
## Description
SantasList uses a two-pass check: checkList (first) and checkTwice (second). The Status enum defines NICE at index 0. In Solidity, all mappings default to 0. Therefore s_theListCheckedTwice for any address that was never checked returns NICE by default, making the second verification pass without Santa ever calling checkTwice.
## Vulnerability Details
File: SantasList.sol - enum Status
enum Status {
NICE, // 0 - default value
EXTRA_NICE,
NAUGHTY,
NOT_CHECKED_TWICE
}
When a user never checked twice:
s_theListCheckedTwice[user] == 0 == NICE (true by default)
This breaks the invariant in collectPresent():
`if (s_theListCheckedOnce[msg.sender] == NICE && s_theListCheckedTwice[msg.sender] == NICE)`
The second condition is true from deployment for all addresses.
## Impact
High Likelihood, Medium Impact: Any address that gets only first check can collect present without second Santa approval. With unguarded checkList, attacker can self-mark first pass and collect.
## Proof of Concept
// Only first check done
vm.prank(santa); list.checkList(alice, Status.NICE);
// Alice collects without ever calling checkTwice - succeeds due to default
vm.prank(alice); list.collectPresent();
assertEq(list.balanceOf(alice), 1);
## Mitigation
Root cause is enum order. NICE should not be default. The fix requires reordering enum so default (0) is a non-privileged state.
Recommended Mitigation:
Reorder enum to put NOT_CHECKED_TWICE at index 0. This ensures uninitialized mappings are considered NOT_CHECKED, not NICE.
```diff
enum Status {
+ NOT_CHECKED_TWICE,
NICE,
EXTRA_NICE,
- NAUGHTY,
- NOT_CHECKED_TWICE
+ NAUGHTY
}