Weather Witness

First Flight #40
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: high
Valid

Lack of Access Control in performUpkeep

Lack of access control in performUpkeep function might drain the LINK token user deposited

Description

  • The performUpkeep function is intended to be called by Chainlink Keepers (Automation) to request updated weather data for NFTs when their heartbeat period is met. However, the function is declared as external with no modifiers or internal checks that would restrict who can call it to only the Chainlink Keeper Registry. As a result, any malicious actor can repeatedly call performUpkeep for multiple NFTs, even if they are not the legitimate Chainlink Keeper, thereby continuously triggering Chainlink Functions requests. Each such request consumes LINK tokens from the contract's associated Chainlink Functions subscription.

function performUpkeep(bytes calldata performData) external override
//@> external function and no access control
uint256 _tokenId = abi.decode(performData, (uint256));
uint256 upkeepId = s_weatherNftInfo[_tokenId].upkeepId;
s_weatherNftInfo[_tokenId].lastFulfilledAt = block.timestamp;
// make functions request
string memory pincode = s_weatherNftInfo[_tokenId].pincode;
string memory isoCode = s_weatherNftInfo[_tokenId].isoCode;
bytes32 _reqId = _sendFunctionsWeatherFetchRequest(pincode, isoCode);
s_funcReqIdToTokenIdUpdate[_reqId] = _tokenId;
emit NftWeatherUpdateRequestSend(_tokenId, _reqId, upkeepId);
}

Risk

Likelihood: High

  • The performUpkeep function is external and directly callable by any Ethereum address.

  • An attacker can easily identify NFTs for which checkUpkeep would return true (i.e., their update heartbeat period has passed) and repeatedly call performUpkeep with the encoded tokenId for those NFTs.

Impact:

  • Depletion of the contract owner's LINK tokens that fund the Chainlink Functions subscription

  • Denial of service for legitimate weather updates. Once the subscription's LINK balance is depleted, valid Chainlink Keepers will no longer be able to execute performUpkeep calls, preventing the weather data for NFTs from being updated, leading to stale or inaccurate information.

Recommended Mitigation

To prevent unauthorized users from triggering Chainlink Functions requests via performUpkeep, you can restrict access to this function by verifying that msg.sender is either the trusted Chainlink Automation Registry address or the owner of the specific NFT being updated.

function performUpkeep(bytes calldata performData) external override {
uint256 _tokenId = abi.decode(performData, (uint256));
+ address currentNFTOwner = _ownerOf(_tokenId); // Get the owner of the specific NFT
+
+ // The function should only be callable by the Chainlink Keeper Registry (for automation)
+ // OR by the current owner of the specific NFT being updated (for manual triggering).
+ require(
+ msg.sender == s_keeperRegistry || msg.sender == currentNFTOwner,
+ "not valid caller"
+ );
uint256 upkeepId = s_weatherNftInfo[_tokenId].upkeepId;
s_weatherNftInfo[_tokenId].lastFulfilledAt = block.timestamp;
// make functions request
string memory pincode = s_weatherNftInfo[_tokenId].pincode;
string memory isoCode = s_weatherNftInfo[_tokenId].isoCode;
bytes32 _reqId = _sendFunctionsWeatherFetchRequest(pincode, isoCode);
s_funcReqIdToTokenIdUpdate[_reqId] = _tokenId;
emit NftWeatherUpdateRequestSend(_tokenId, _reqId, upkeepId);
}
Updates

Appeal created

bube Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Anyone can call `performUpkeep` function

The `performUpkeep` function should be called by the Chainlink keepers or owners of the NFT. But there is no access control and anyone can call the function. This leads to malicious consumption of the user's LINK deposit.

Support

FAQs

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