Weather Witness

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

Missing Enum Validation Allows Invalid Weather States

Root + Impact

Normal Behavior:

When a Chainlink Functions request completes, the contract decodes the weather condition returned by the oracle as a uint8, which is then cast into a Weather enum. This value is stored and used to determine the correct image URI for the NFT’s metadata, allowing dynamic visual updates tied to real-world weather.

Issue:

The contract does not validate that the decoded uint8 value falls within the valid range of the Weather enum (0–5). As a result, it is possible to store an undefined enum value (e.g., Weather(6)), which can cause the NFT metadata to contain an invalid or empty image URI. This breaks NFT rendering and introduces semantic corruption into the contract state.

https://github.com/CodeHawks-Contests/2025-05-weather-witness/blob/5af7436e8f9fb0466631d1f53e22bf39a9aad4da/src/WeatherNft.sol#L46

Risk

Likelihood:

  • Reason 1: This will occur when the Chainlink Functions script (e.g., GetWeather.js) returns a weather code that exceeds the defined enum range, either due to a logic bug, misconfiguration, or unhandled edge case in the OpenWeather API response.

  • Reason 2: This will also occur when a malicious or compromised Chainlink node returns an intentionally incorrect uint8 value as part of its response.

Impact:

  • Impact 1: The NFT metadata will point to an empty or invalid image URI, causing it to break visually on marketplaces like OpenSea and user interfaces that rely on the image field.

  • Impact 2: Invalid enum values introduce undefined behavior into the contract state, making it difficult to reason about system correctness and potentially breaking future contract logic that assumes only valid Weather values.

Proof of Concept

Assumptions
• Weather enum is defined with values 0–5.
• A malicious Chainlink response returns uint8(9).
bytes memory maliciousResponse = abi.encode(uint8(9)); // Simulates a bad off-chain response
uint8 weather = abi.decode(maliciousResponse, (uint8));
s_tokenIdToWeather[123] = Weather(weather); // Stores Weather(9) silently
Effect:
• No revert occurs.
• s_tokenIdToWeather[123] is now Weather(9) — which is undefined.
• Calling tokenURI(123) will result in:
{
"name": "Weather NFT",
"user": "0x...",
"image": "" // Missing or broken image
}

Recommended Mitigation

  • uint8 weather = abi.decode(response, (uint8));
    s_tokenIdToWeather[tokenId] = Weather(weather);

  • function _validateWeatherEnum(uint8 value) internal pure returns (Weather) {
    require(value <= uint8(Weather.SNOW), "Invalid weather enum");
    return Weather(value);
    }

  • Weather weather = _validateWeatherEnum(abi.decode(response, (uint8)));
    s_tokenIdToWeather[tokenId] = weather;

Updates

Appeal created

bube Lead Judge 23 days ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

[Invalid] Weather enum is not checked

The implementation to get the current weather is written in `GetWeather.js`. The `weather_enum` will be always in the expected range.

Support

FAQs

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