Weather Witness

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

Missing HTTP Response Validation in `GetWeather.js`

Description

Off-chain JavaScript logic in Chainlink Functions does not validate HTTP status codes or verify that expected fields (e.g., lat, lon, weather) exist and are correctly typed in API responses. Consequently, malformed or non-200 responses can slip through, leading to reverted transactions, denial-of-service (DoS), or incorrect metadata updates.

Vulnerability Details

In the GetWeather.js script, geocoding and weather API calls are made as follows:

// Geocoding request
const geoCodingRequest = Functions.makeHttpRequest({
url: "http://api.openweathermap.org/geo/1.0/zip",
method: "GET",
params: { zip: `${args[0]},${args[1]}`, appid: secrets.apiKey }
});
const geoCodingResponse = await geoCodingRequest;
if (geoCodingResponse.error) throw Error("Request failed…");
//<< No checks for geoCodingResponse.status or for data.lat / data.lon >>
// Weather request
const weatherRequest = Functions.makeHttpRequest({ /* … */ });
const weatherResponse = await weatherRequest;
if (weatherResponse.error) throw Error("Request failed…");
// << No checks for weatherResponse.status nor for payload integrity >>

No HTTP status validation.
The code only inspects response.error, which remains null for non-200 status codes.

No payload validation
It assumes that data.lat, data.lon, and data.weather[0].id always exist and have the correct types.

Impact

  • Denial-of-Service (DoS):
    Non-200 or malformed responses will cause the Chainlink callback to revert. Repeated failures consume LINK and gas, potentially exhausting resources and halting future updates or mints.

  • Funds Loss:
    Users may pay ETH for a mint, but if the callback reverts afterward, the NFT never mints and the ETH is retained.

  • Incorrect Metadata:
    Invalid values (e.g., lat = undefined) could be coerced into default or fallback states, causing NFTs to display misleading weather conditions.

  • Resource Exhaustion:
    Every failed callback burns gas and LINK, weakening protocol resilience over time.

Likelihood

Medium to High. Public APIs frequently return non-200 statuses (e.g., 429 rate limits, 404 invalid ZIP codes, 5xx server errors). Without explicit checks, failures will inevitably occur as usage scales—or can be triggered maliciously.

Proof of Concept

Supply an invalid ZIP code (e.g., "BADZIP,XX") to trigger a 404:

const geoCodingResponse = await Functions.makeHttpRequest({
url: "http://api.openweathermap.org/geo/1.0/zip",
method: "GET",
params: { zip: "BADZIP,XX", appid: secrets.apiKey }
});
console.log("Status:", geoCodingResponse.status); // 404
console.log("Error field:", geoCodingResponse.error); // null
console.log("Data:", geoCodingResponse.data); // { message: "not found" }

Because geoCodingResponse.error is null, the script continues with invalid coordinates.

The on-chain callback fails during decoding, consuming LINK and gas, and causes the mint or update to revert.

Recommended Mitigation

Add explicit HTTP status and payload validation to GetWeather.js, immediately after each request in geoCodingRequest and weatherRequest:

const geoCodingResponse = await geoCodingRequest;
- if (geoCodingResponse.error) throw Error("Request failed, try checking the params provided")
+ if (geoCodingResponse.error) {
+ throw new Error(`Geocoding error: ${geoCodingResponse.error}`);
+ }
+ // Added HTTP status check
+ if (geoCodingResponse.status !== 200) {
+ throw new Error(`Geocoding HTTP status ${geoCodingResponse.status}`);
+ }
+ const geoData = geoCodingResponse.data;
+ // Validate payload fields
+ if (
+ !geoData ||
+ typeof geoData.lat !== "number" ||
+ typeof geoData.lon !== "number"
+ ) {
+ throw new Error("Invalid geocoding payload");
+ }
.
.
.
const weatherResponse = await weatherRequest;
- if (weatherResponse.error) throw Error("Request failed, try checking the params provided")
+ if (weatherResponse.error) {
+ throw new Error(`Weather error: ${weatherResponse.error}`);
+ }
+ // Added HTTP status check
+ if (weatherResponse.status !== 200) {
+ throw new Error(`Weather HTTP status ${weatherResponse.status}`);
+ }
+ const wData = weatherResponse.data;
+ // Validate weather payload
+ if (
+ !wData.weather ||
+ !Array.isArray(wData.weather) ||
+ typeof wData.weather[0].id !== "number"
+ ) {
+ throw new Error("Invalid weather payload");
+ }

Status checks guard against unexpected HTTP statuses (404, 429, 500).

Type assertions ensure lat, lon, and weather[0].id exist and are correctly typed.

Early throws prevent downstream logic from executing on malformed inputs, saving gas and LINK while preserving correct on-chain behavior.

Implementing these checks will make the protocol robust against API anomalies, rate limits, and malicious inputs—safeguarding user funds and LINK budgets.

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.