Beatland Festival

First Flight #44
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: low
Valid

`FestivalPass::uri` The function may return URIs for non-existent or invalid tokens

FestivalPass::uri The function may return URIs for non-existent or invalid tokens

Description

The uri(uint256 tokenId) function returns the URI associated with a token, distinguishing between regular passes (tokenId <= BACKSTAGE_PASS) and memorabilia (tokenId encoded with collectionId + itemId).

If the tokenId does not match either case, the function returns super.uri(tokenId), using the base URI defined in the constructor. This may result in apparently valid URIs being returned for non-existent tokenIds.

function uri(uint256 tokenId) public view override returns (string memory) {
// Handle regular passes (IDs 1-3)
if (tokenId <= BACKSTAGE_PASS) {
return string(abi.encodePacked("ipfs://beatdrop/", Strings.toString(tokenId)));
}
// Decode collection and item IDs
(uint256 collectionId, uint256 itemId) = decodeTokenId(tokenId);
// Check if it's a valid memorabilia token
if (collections[collectionId].priceInBeat > 0) {
// Return specific URI for this item
// e.g., "ipfs://QmXXX/metadata/5" for item #5
return string(abi.encodePacked(
collections[collectionId].baseUri,
"/metadata/",
Strings.toString(itemId)
));
}
return super.uri(tokenId);
}

Risk

Likelihood: Medium

Services like marketplaces could display false metadata for non-existent tokenIds, causing user confusion.

Impact: Low

Although there is no direct risk of financial loss or security vulnerability, this behavior can cause functional confusion and a poor user experience. If a URI is returned for a non-existent tokenId, interfaces like marketplaces (OpenSea, etc.) or NFT explorers could display incorrect metadata, suggesting the token exists when it does not.
This can cause uncertainty for users, hinder contract debugging, and create false trust in assets that have not been created or authorized.

Proof of Concept

This test verifies that the uri() function returns a URI even for a tokenId
that has never been minted or does not correspond to a valid pass or memorabilia item.

function test_UriReturnsForInvalidTokenId() public {
uint256 randomId = uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao)));
string memory uri = festivalPass.uri(randomId);
console.log("URI : ", uri);
}
Logs:
URI: ipfs://beatdrop/{id}

Recommended Mitigation

Add explicit validation to ensure the tokenId actually exists before returning a URI. If it is not a valid pass or an existing memorabilia, the function should revert.

function uri(uint256 tokenId) public view override returns (string memory) {
// Handle regular passes (IDs 1-3)
if (tokenId <= BACKSTAGE_PASS) {
return string(abi.encodePacked("ipfs://beatdrop/", Strings.toString(tokenId)));
}
// Decode collection and item IDs
(uint256 collectionId, uint256 itemId) = decodeTokenId(tokenId);
// Check if it's a valid memorabilia token
if (collections[collectionId].priceInBeat > 0) {
// Return specific URI for this item
// e.g., "ipfs://QmXXX/metadata/5" for item #5
return string(abi.encodePacked(
collections[collectionId].baseUri,
"/metadata/",
Strings.toString(itemId)
));
}
- return super.uri(tokenId);
+ revert("Invalid tokenId");
}
Updates

Lead Judging Commences

inallhonesty Lead Judge about 2 months ago
Submission Judgement Published
Validated
Assigned finding tags:

uri for non-existing ids

Support

FAQs

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