Puppy Raffle

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

Invalid JSON in tokenURI() Metadata — Unquoted Rarity Value

Root + Impact

Description

  • tokenURI() constructs JSON metadata for NFTs, but the rarity value in the attributes array is not properly quoted:

abi.encodePacked(
'{"name":"', name(),
'", "description":"An adorable puppy!", ',
'"attributes": [{"trait_type": "rarity", "value": ',
rareName, // @audit Not quoted — produces invalid JSON
'}], "image":"', imageURI, '"}'
)
  • This produces: "value": common instead of the valid JSON "value": "common".

  • Per RFC 8259, string values in JSON must be enclosed in double quotes. The output is invalid JSON and will break:

    • NFT marketplace displays (OpenSea, Rarible, LooksRare)

    • Any JSON parser consuming the tokenURI response

    • Metadata indexers and aggregators

Risk

Likelihood:

  • Every minted NFT has broken metadata — 100% occurrence rate

Impact:

  • Low — no fund loss, but NFT display is broken across all marketplaces

  • NFTs may appear without attributes or fail to render entirely on marketplaces

Proof of Concept

How the issue manifests:

  1. A raffle completes and selectWinner() mints an NFT to the winner via _safeMint()

  2. When any marketplace or application calls tokenURI(tokenId), it receives a base64-encoded JSON string

  3. After decoding, the JSON contains "value": common (unquoted string) which is invalid per RFC 8259

  4. Standard JSON parsers (JSON.parse(), Python json.loads()) throw a parse error, and the NFT metadata fails to display

Vulnerable code (src/PuppyRaffle.sol:196-214):

abi.encodePacked(
'{"name":"', name(),
'", "description":"An adorable puppy!", ',
'"attributes": [{"trait_type": "rarity", "value": ',
rareName, // Missing quotes around value
'}], "image":"', imageURI, '"}'
)
// Output: "value": common (invalid JSON)
// Should: "value": "common" (valid JSON)

Expected outcome: Every minted NFT produces invalid JSON metadata, causing display failures across all NFT marketplaces and metadata indexers.

Recommended Mitigation

The root cause is that rareName (a string) is concatenated into the JSON without wrapping it in double quotes. Per RFC 8259, JSON string values must be enclosed in "...". The fix is straightforward: add quote delimiters around the interpolated value.

Primary fix — Quote the rarity value:

// Before (invalid JSON):
'"attributes": [{"trait_type": "rarity", "value": ',
rareName, // Produces: "value": common ← invalid
'}]'
// After (valid JSON):
'"attributes": [{"trait_type": "rarity", "value": "',
rareName, // Produces: "value": "common" ← valid
'"}]'

Complete corrected tokenURI metadata block:

string memory json = Base64.encode(
bytes(
abi.encodePacked(
'{"name":"', name(),
'", "description":"An adorable puppy!", ',
'"attributes": [{"trait_type": "rarity", "value": "',
rareName,
'"}], "image":"', imageURI, '"}'
)
)
);
return string(abi.encodePacked("data:application/json;base64,", json));

Why this works: The added " characters before and after rareName ensure the output conforms to the JSON specification. All standard JSON parsers (JSON.parse() in JavaScript, json.loads() in Python) and NFT marketplace indexers (OpenSea, Rarible) will correctly parse the metadata.

Verification: After the fix, the decoded JSON for a common rarity NFT should be:

{
"name": "PuppyRaffle",
"description": "An adorable puppy!",
"attributes": [{"trait_type": "rarity", "value": "common"}],
"image": "ipfs://..."
}

Additional consideration: If rareName could ever contain characters that need JSON escaping (e.g., ", \, newlines), a proper JSON encoding library should be used. For the current codebase where rareName is one of four hardcoded strings ("common", "rare", "legendary", "ultra rare"), simple string concatenation with quotes is sufficient.

Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge 8 days 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!