Summary
The feedHorse() function does not verify if the horseId exists, so any user can feed any horse even if not minted yet.
Vulnerability Details
You should verify that the horse exists before feeding it.
function feedHorse(uint256 horseId) external {
@> horseIdToFedTimeStamp[horseId] = block.timestamp;
}
Impact
Any new horse may already be fed at its creation, breaking the rule that only horse NFTs can be fed, and that a horse is happy only when it is fed.
Tools Used
Foundry, Manual review
Proof of Concept
This is a test that feeds an nonexisting horse id and expects it to be happy.
function testCanFeedHorseBeforeMint() public {
vm.warp(horseStore.HORSE_HAPPY_IF_FED_WITHIN());
horseStore.feedHorse(0);
vm.warp(block.timestamp + 3600);
vm.prank(user);
horseStore.mintHorse();
bool isHappyHorse = horseStore.isHappyHorse(0);
assertEq(isHappyHorse, true);
}
Run Solidity tests
forge test --mc HorseStoreSolidity --mt testCanFeedNonexistentHorse
Test passes, even though it receives a non-existent horse Id.
Running 1 test for test/HorseStoreSolidity.t.sol:HorseStoreSolidity
[PASS] testCanFeedNonexistentHorse() (gas: 27664)
Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.87ms
Recommended Mitigation
Verify that the horse exists, and throw a custom error if it does not. Do it also in isHappyHorse(), to avoid confusion for users.
mapping(uint256 id => uint256 lastFedTimeStamp) public horseIdToFedTimeStamp;
+ error HorseDoesNotExist();
+
constructor() ERC721(NFT_NAME, NFT_SYMBOL) {}
function feedHorse(uint256 horseId) external {
+ if (_ownerOf(horseId) == address(0)) {
+ revert HorseDoesNotExist();
+ }
+
horseIdToFedTimeStamp[horseId] = block.timestamp;