Description
The requestMintWeatherNFT
function accepts parameters without proper validation, potentially leading to invalid or malformed data being stored on-chain.
Risk
Severity: Medium
Likelihood: High
Summary
Missing input validation for pincode, isoCode, and heartbeat parameters could lead to system instability and increased oracle costs.
Vulnerability Details
Root Cause: Lack of input validation checks in the minting function.
Initial State: System accepts any string/number inputs without validation.
Attack Scenario:
Attacker provides invalid pincode format
System attempts to fetch weather data with invalid location
Oracle calls fail but consume resources
Invalid data gets stored on-chain
Proof of Concept
function testInvalidInputs() public {
weatherNft.requestMintWeatherNFT{value: 1 ether}(
"",
"XXX",
false,
0,
0
);
weatherNft.requestMintWeatherNFT{value: 1 ether}(
"!@#$%",
"USAA",
false,
1,
0
);
}
Impact
-
Oracle calls fail but consume resources
-
Invalid location data stored on-chain
-
Increased gas costs for users
-
Unreliable weather data updates
-
System credibility compromised
Tools Used
-
Manual Review
-
Static Analysis
-
Fuzzing Tests
Recommendations
Add comprehensive input validation:
contract WeatherNft {
uint256 private constant MIN_HEARTBEAT = 1 hours;
uint256 private constant MAX_HEARTBEAT = 30 days;
error InvalidPincode(string pincode);
error InvalidIsoCode(string isoCode);
error InvalidHeartbeat(uint256 heartbeat);
function validateInputs(
string memory _pincode,
string memory _isoCode,
uint256 _heartbeat
) internal pure {
bytes memory pincodeBytes = bytes(_pincode);
if(pincodeBytes.length != 6) revert InvalidPincode(_pincode);
for(uint i = 0; i < pincodeBytes.length; i++) {
if(uint8(pincodeBytes[i]) < 48 || uint8(pincodeBytes[i]) > 57)
revert InvalidPincode(_pincode);
}
bytes memory isoBytes = bytes(_isoCode);
if(isoBytes.length != 2) revert InvalidIsoCode(_isoCode);
for(uint i = 0; i < isoBytes.length; i++) {
if(uint8(isoBytes[i]) < 65 || uint8(isoBytes[i]) > 90)
revert InvalidIsoCode(_isoCode);
}
if(_heartbeat < MIN_HEARTBEAT || _heartbeat > MAX_HEARTBEAT)
revert InvalidHeartbeat(_heartbeat);
}
function requestMintWeatherNFT(
string memory _pincode,
string memory _isoCode,
bool _registerKeeper,
uint256 _heartbeat,
uint256 _initLinkDeposit
) external payable returns (bytes32 _reqId) {
validateInputs(_pincode, _isoCode, _heartbeat);
}
}
Alternative Implementation with Regex Support:
library InputValidator {
function validatePincode(string memory pincode) internal pure returns (bool) {
bytes memory b = bytes(pincode);
if(b.length != 6) return false;
for(uint i; i < b.length; i++) {
if(uint8(b[i]) < 48 || uint8(b[i]) > 57) return false;
}
return true;
}
function validateISOCode(string memory isoCode) internal pure returns (bool) {
bytes memory b = bytes(isoCode);
if(b.length != 2) return false;
for(uint i; i < b.length; i++) {
if(uint8(b[i]) < 65 || uint8(b[i]) > 90) return false;
}
return true;
}
}