DatingDapp

AI First Flight #6
Beginner FriendlyFoundrySolidityNFT
EXP
View results
Submission Details
Impact: low
Likelihood: low
Invalid

Unbounded profile strings can make minting and tokenURI() expensive or unusable

Root + Impact

Description

  • No length cap exists on name or profileImage. A user can mint with a multi-kilobyte string, bloating the on-chain storage and the tokenURI() output beyond what most RPC nodes, indexers, or frontends will handle gracefully.

//@SoulboundProfileNFT.sol line 30
function mintProfile(string memory name, uint8 age, string memory profileImage) external {
require(profileToToken[msg.sender] == 0, "Profile already exists");

Risk

Likelihood:

  • Low. Self-inflicted and economically disincentivised by gas cost. More likely accidental than malicious.


Impact:

  • Low. Caller pays their own inflated gas — no direct fund loss to others. However extremely large strings can cause tokenURI() to exceed RPC response limits, breaking frontend/indexer reads for that token permanently.


Proof of Concept

Applying this function at the end of /test/TestSnowmanAirdrop.t.sol to know what the correct and wrong digest output HASH.

Ran with command: forge test --match-test testFrontendSignatureVerification -vvvv

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "forge-std/Test.sol";
import "../src/SoulboundProfileNFT.sol";
contract UnboundedStringsPoC is Test {
SoulboundProfileNFT profileNFT;
address alice = address(0xA11CE);
function setUp() public {
profileNFT = new SoulboundProfileNFT();
}
function testUnboundedStringsMintSucceeds() public {
// Build a 10 000 character name — no validation stops this
bytes memory hugeName = new bytes(10_000);
for (uint256 i = 0; i < hugeName.length; i++) {
hugeName[i] = 0x41; // 'A'
}
vm.prank(alice);
uint256 gasBefore = gasleft();
profileNFT.mintProfile(string(hugeName), 25, "ipfs://alice");
uint256 gasUsed = gasBefore - gasleft();
emit log_named_uint("gas used to mint with 10k-char name", gasUsed);
// tokenURI() must now serialise the entire 10 000 char string on every call
uint256 tokenId = profileNFT.profileToToken(alice);
uint256 gasBefore2 = gasleft();
profileNFT.tokenURI(tokenId);
uint256 gasUsedURI = gasBefore2 - gasleft();
emit log_named_uint("gas used by tokenURI() with 10k-char name", gasUsedURI);
// Mint succeeds — contract imposes no upper bound whatsoever
assertTrue(tokenId != 0, "minted with unbounded string");
}
}

Recommended Mitigation

Add a constant max length and enforce it in the same mintProfile entry point.

+ uint256 public constant MAX_NAME_LENGTH = 64;
+ uint256 public constant MAX_PROFILE_IMAGE_LENGTH = 200;
function mintProfile(
string memory name,
uint256 age,
string memory profileImage
) external {
require(bytes(name).length <= MAX_NAME_LENGTH, "Name too long");
require(bytes(profileImage).length <= MAX_PROFILE_IMAGE_LENGTH, "Image URI too long");
// ... rest of mint logic
}
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 2 hours ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!