Weather Witness

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

Unvalidated Heartbeat Parameter Allows Zero-Value Submission in `WeatherNft::requestMintWeatherNFT``

Description

The requestMintWeatherNFT function accepts a user-supplied heartbeat interval but fails to enforce a minimum value. If a caller specifies heartbeat = 0, the on-chain upkeep check will always evaluate to true immediately after minting. This permits continuous or spammed Chainlink Functions callbacks.

Vulnerability Details

Location:

function requestMintWeatherNFT(
string memory _pincode,
string memory _isoCode,
bool _registerKeeper,
uint256 _heartbeat,
uint256 _initLinkDeposit
) external payable returns (bytes32 _reqId) {
// …
}

CheckUpkeep Logic:

upkeepNeeded = (
block.timestamp
>= s_weatherNftInfo[_tokenId].lastFulfilledAt
+ s_weatherNftInfo[_tokenId].heartbeat
);

With heartbeat == 0, this condition is true immediately after any prior timestamp is set.

Impact

  • Excessive Oracle Calls & Gas Drain

Chainlink Keeper network will repeatedly trigger performUpkeep, flooding the system with requests.

Rapid LINK depletion and high gas consumption by the registry.

  • Denial-of-Service (DoS)

Legitimate upkeep operations may fail once LINK funds are exhausted or the registry suspends the upkeep.

  • Economic Loss

Unexpected LINK and gas expenditures.

Potential for malicious actors to exploit zero-heartbeat mints, causing resource overconsumption.

Likelihood

High:

No on-chain validation prevents heartbeat = 0.

Any user can mint with a zero interval and immediately exploit the loophole.

Proof of Concept

Mint an NFT with heartbeat = 0.

Observe that checkUpkeep returns true immediately.

Chainlink Keepers continuously invoke performUpkeep.

The LINK balance depletes rapidly, disabling all further upkeep.

Recommended Mitigation

Add a require for a minimum heartbeat in requestMintWeatherNFT:

function requestMintWeatherNFT(
string memory _pincode,
string memory _isoCode,
bool _registerKeeper,
uint256 _heartbeat,
uint256 _initLinkDeposit
) external payable returns (bytes32 _reqId) {
+ require(_heartbeat > 0, "Heartbeat must be greater than zero");
// …
}

Optionally, set a higher minimum (e.g., MIN_HEARTBEAT = 60 seconds):

+ uint256 constant MIN_HEARTBEAT = 60;
+ require(_heartbeat >= MIN_HEARTBEAT, "Heartbeat must be at least 60 seconds");

This ensures:

Prevention of sub-second or zero intervals that cause excessive upkeep calls.

Protection against rapid LINK and gas consumption.

Updates

Appeal created

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

[Invalid] Lack of input validation in `requestMintWeatherNFT`

This is informational. It is user's responsibility to provide correct input arguments. If the user provides incorrect arguments, it will lead to incorrect results, lost funds or failed transaction.

Support

FAQs

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