Beginner FriendlyFoundryNFT
100 EXP
View results
Submission Details
Severity: low
Invalid

`OneShot::getRapperStats` doesn't check if the `tokenId` is valid

Summary

The OneShot::getRapperStats function does not validate whether the tokenId provided as an input parameter corresponds to a minted token. The function uses rapperStats mapping to return the rapper stats for a given Id. In solidity if the rapperStats mapping is invoke with invalid token, the mapping will return the default values of each variable.

Vulnerability Details

The OneShot::getRapperStats function does not validate whether the tokenId provided as an input parameter corresponds to a minted token. This could lead to the function returning a default RapperStats struct for non-existent token IDs.

function getRapperStats(uint256 tokenId) public view returns (RapperStats memory) {
@> return rapperStats[tokenId];
}

The OneShot::getRapperStats is called in RapBattle::getRapperSkill function. The getRapperSkill function also doesn't validate the input parameter _tokenId. If the getRapperStats returns the default values for the _tokenId, the getRapperSkill will return 65 as a finalSkill.

function getRapperSkill(uint256 _tokenId) public view returns (uint256 finalSkill) {
@> IOneShot.RapperStats memory stats = oneShotNft.getRapperStats(_tokenId);
finalSkill = BASE_SKILL;
if (stats.weakKnees) {
finalSkill -= VICE_DECREMENT;
}
if (stats.heavyArms) {
finalSkill -= VICE_DECREMENT;
}
if (stats.spaghettiSweater) {
finalSkill -= VICE_DECREMENT;
}
if (stats.calmAndReady) {
finalSkill += VIRTUE_INCREMENT;
}
}

Impact

The OneShot::getRapperStats is public and anyone can call it. If a user calls the function with the non-existing tokenId, the user will receive the default values for rapper stats. So the user will not understand that the tokenId is invalid. The following test shows the scenario when Bob calls the function with non-existing tokenId. You can add the test in the OneShot.t.test file and execute it with the Foundry command: forge test --match-test "testGetRapperStatsWithInvalidTokenId".

function testGetRapperStatsWithInvalidTokenId() public {
address bob = makeAddr("bob");
vm.prank(bob);
oneShot.mintRapper();
uint256 invalidTokenId = 1;
stats = oneShot.getRapperStats(invalidTokenId);
assert(stats.weakKnees == false);
assert(stats.heavyArms == false);
assert(stats.spaghettiSweater == false);
assert(stats.calmAndReady == false);
assert(stats.battlesWon == 0);
}

The test function testGetRapperSkillNonExistingTokenId demonstrates the sceanrio when the RapBattle::getRapperSkill is called with non-exisiting tokenId. The returned value for the skill will be 65 that is the BASE_SKILL. Execute the function with the Foundry command: forge test --match-test "testGetRapperSkillNonExistingTokenId" -vvvvv.

function testGetRapperSkillNonExistingTokenId() public mintRapper {
uint256 skill = rapBattle.getRapperSkill(1);
console.log(skill);
}

Tools Used

Manual Review, Foundry

Recommendations

Add a check in the OneShot::getRapperStats or/and in the RapBattle::getRapperSkill for existence of the tokenId.

In OneShot::getRapperStats:

function getRapperStats(uint256 tokenId) public view returns (RapperStats memory) {
+ require(tokenId < getNextTokenId(), "The tokenId is not valid");
return rapperStats[tokenId];
}

In RapBattle::getRapperSkill:

function getRapperSkill(uint256 _tokenId) public view returns (uint256 finalSkill) {
+ require(_tokenId < oneShotNft.getNextTokenId(), "The _tokenId is not valid");
IOneShot.RapperStats memory stats = oneShotNft.getRapperStats(_tokenId);
finalSkill = BASE_SKILL;
if (stats.weakKnees) {
finalSkill -= VICE_DECREMENT;
}
if (stats.heavyArms) {
finalSkill -= VICE_DECREMENT;
}
if (stats.spaghettiSweater) {
finalSkill -= VICE_DECREMENT;
}
if (stats.calmAndReady) {
finalSkill += VIRTUE_INCREMENT;
}
}

If you change the RapBattle::getRapperSkill function, you also should add the following in IOneShot.sol file:
function getNextTokenId() external view returns (uint256);

Updates

Lead Judging Commences

inallhonesty Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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