Beatland Festival

First Flight #44
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Impact: low
Likelihood: medium
Invalid

No Check for Duplicated Collection Names or URIs in createMemorabiliaCollection

Root + Impact

Description

  • Normal Behavior:
    When creating a new memorabilia collection using createMemorabiliaCollection, each collection should ideally have a unique name and baseUri to prevent confusion for users, off-chain indexers, and marketplaces. Unique identifiers help ensure that each collection is distinct and easily referenced.

    Issue:
    The createMemorabiliaCollection function in FestivalPass does not check for duplicate name or baseUri values. This means the organizer can accidentally or maliciously create multiple collections with the same name or URI. This can lead to confusion for users, difficulties for off-chain services (such as NFT explorers or indexers), and potential issues with collection management or provenance.

function createMemorabiliaCollection(
string memory name,
string memory baseUri,
uint256 priceInBeat,
uint256 maxSupply,
bool activateNow
) external onlyOrganizer returns (uint256) {
require(priceInBeat > 0, "Price must be greater than 0");
require(maxSupply > 0, "Supply must be at least 1");
require(bytes(name).length > 0, "Name required");
require(bytes(baseUri).length > 0, "URI required");
// No check for duplicate name or baseUri
// ...existing code...
}

Risk

Likelihood:

  • This can occur if the organizer reuses a name or URI by mistake or intentionally.

Impact:

  • While this does not break contract logic, it can cause confusion for users and off-chain systems, and may affect the perceived uniqueness and provenance of memorabilia collections.

Proof of Concept

An organizer can create two memorabilia collections with the same name and baseUri by calling createMemorabiliaCollection twice with identical parameters. Both calls succeed, resulting in two distinct collections that are indistinguishable by name or URI. This can confuse users, off-chain indexers, and marketplaces, as there is no enforced uniqueness for these identifiers.

// Organizer creates two collections with the same name and URI
festival.createMemorabiliaCollection("VIP Collection", "ipfs://collection", 1 ether, 10, true);
festival.createMemorabiliaCollection("VIP Collection", "ipfs://collection", 2 ether, 20, true);
// Both calls succeed, resulting in two collections with identical identifiers.

Recommended Mitigation

Add checks in createMemorabiliaCollection to ensure that both name and baseUri are unique among all existing collections. This can be done by maintaining mappings or iterating through existing collections (if gas usage is acceptable for the expected number of collections).

- // Memorabilia collections
- mapping(uint256 => MemorabiliaCollection) public collections; // collectionId => Collection
- mapping(uint256 => uint256) public tokenIdToEdition; // tokenId => edition number
+ // Memorabilia collections
+ mapping(uint256 => MemorabiliaCollection) public collections; // collectionId => Collection
+ mapping(uint256 => uint256) public tokenIdToEdition; // tokenId => edition number
+ // Uniqueness mappings for collection names and URIs
+ mapping(bytes32 => bool) private usedCollectionNames;
+ mapping(bytes32 => bool) private usedCollectionUris;
...
- uint256 collectionId = nextCollectionId++;
-
- collections[collectionId] = MemorabiliaCollection({
- name: name,
- baseUri: baseUri,
- priceInBeat: priceInBeat,
- maxSupply: maxSupply,
- currentItemId: 1, // Start item IDs at 1
- isActive: activateNow
- });
-
- emit CollectionCreated(collectionId, name, maxSupply);
- return collectionId;
+ bytes32 nameHash = keccak256(bytes(name));
+ bytes32 uriHash = keccak256(bytes(baseUri));
+ require(!usedCollectionNames[nameHash], "Collection name already exists");
+ require(!usedCollectionUris[uriHash], "Collection URI already exists");
+ usedCollectionNames[nameHash] = true;
+ usedCollectionUris[uriHash] = true;
+ uint256 collectionId = nextCollectionId++;
+ collections[collectionId] = MemorabiliaCollection({
+ name: name,
+ baseUri: baseUri,
+ priceInBeat: priceInBeat,
+ maxSupply: maxSupply,
+ currentItemId: 1, // Start item IDs at 1
+ isActive: activateNow
+ });
+ emit CollectionCreated(collectionId, name, maxSupply);
+ return collectionId;
Updates

Lead Judging Commences

inallhonesty Lead Judge 29 days ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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