Weather Witness

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

Critical Weather Data Validation Missing in Update Function

Description

The _fulfillWeatherUpdate function accepts and stores weather data without validating if it falls within the valid enum range.

Risk

Severity: High
Likelihood: Medium

Summary

Weather data from external sources is stored without validation, potentially corrupting NFT metadata with invalid weather states.

Vulnerability Details

Root Cause:

uint8 weather = abi.decode(response, (uint8));
s_tokenIdToWeather[tokenId] = Weather(weather); // No range validation

Initial State:

  1. NFT exists with valid weather state

  2. Weather update requested via Chainlink

Attack Scenario:

  1. Oracle returns corrupted/malicious data

  2. Invalid weather value (e.g., 255) received

  3. Value blindly cast to Weather enum

  4. Invalid state stored permanently

Proof of Concept

// Test file demonstrating the vulnerability
contract WeatherNftTest {
function testInvalidWeatherData() public {
// Setup NFT with valid weather
uint256 tokenId = 1;
bytes32 requestId = bytes32(uint256(1));
// Simulate oracle response with invalid weather
bytes memory invalidResponse = abi.encode(uint8(255)); // Invalid weather
// Update weather
vm.prank(address(oracle));
weatherNft.fulfillRequest(
requestId,
invalidResponse,
""
);
// Assert: Invalid weather stored
Weather storedWeather = weatherNft.s_tokenIdToWeather(tokenId);
// Will store invalid enum value
assertEq(uint8(storedWeather), 255);
}
}

Impact

  • Corrupted NFT metadata

  • Invalid weather states stored

  • System reliability compromised

  • Potential system crashes when accessing invalid enum values

  • Loss of NFT value

Tools Used

  • Manual Review

Recommendations

Add weather data validation:

contract WeatherNft {
error WeatherNft__InvalidWeatherValue(uint8 weather);
function isValidWeather(uint8 weather) internal pure returns (bool) {
return weather <= uint8(Weather.SNOW); // Assuming SNOW is highest enum value
}
function _fulfillWeatherUpdate(bytes32 requestId, bytes memory response, bytes memory err) internal {
if (response.length == 0 || err.length > 0) {
emit WeatherUpdateFailed(requestId, err);
return;
}
uint256 tokenId = s_funcReqIdToTokenIdUpdate[requestId];
uint8 weather = abi.decode(response, (uint8));
if (!isValidWeather(weather)) {
emit InvalidWeatherReceived(requestId, weather);
revert WeatherNft__InvalidWeatherValue(weather);
}
s_weatherNftInfo[tokenId].lastFulfilledAt = block.timestamp;
s_tokenIdToWeather[tokenId] = Weather(weather);
emit NftWeatherUpdated(tokenId, Weather(weather));
}
}

Alternative Implementation with Fallback:

contract WeatherNft {
Weather public constant DEFAULT_WEATHER = Weather.SUNNY;
function _handleInvalidWeather(uint256 tokenId, uint8 invalidWeather) internal {
// Log invalid data
emit InvalidWeatherReceived(tokenId, invalidWeather);
// Use last known valid weather or default
Weather currentWeather = s_tokenIdToWeather[tokenId];
if (currentWeather == Weather(0)) {
s_tokenIdToWeather[tokenId] = DEFAULT_WEATHER;
}
// Keep existing weather if already set
}
}
Updates

Appeal created

bube Lead Judge 11 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.