Summary
No Edge Function validates the polygon coordinate input before passing it to Google Earth Engine. There are no limits on vertex count, coordinate range, or geographic area. A malicious request with thousands of coordinates, an invalid bounding box, or coordinates outside the Earth's surface will trigger expensive GEE computation attempts before failing.
Evidence
supabase/functions/analyze-field/index.ts:
const body = await req.json();
if (body.polygon) {
const { polygon } = body;
let coords: [number, number][];
if (polygon.type === "Polygon" && Array.isArray(polygon.coordinates)) {
coords = polygon.coordinates[0]; // no vertex count check
} else if (Array.isArray(polygon) && polygon.length >= 3) {
coords = polygon; // accepts any array with ≥ 3 elements
} else {
throw new Error("Invalid polygon");
}
// Immediately calls GEE with unvalidated coords:
const token = await getGeeAccessToken();
...
Same pattern in gee-analytics/index.ts, gee-ndvi-tiles/index.ts, and ndvi-timeseries/index.ts. None of them check:
- Maximum number of vertices (GEE performance degrades with complex polygons)
- Coordinate value ranges (longitude: -180 to 180, latitude: -90 to 90)
- Minimum or maximum polygon area (prevents trivially tiny or planet-scale requests)
- GeoJSON ring closure (first and last coordinate must be identical)
- Degenerate geometries (self-intersecting, zero-area polygons)
The GEE call itself sets maxPixels: { constantValue: 1000000000 } — 1 billion pixels — which is a very high limit that does not protect against resource exhaustion before the GEE server rejects the request.
Why this matters
-
Resource exhaustion without auth: Each malformed or enormous polygon triggers a GEE OAuth token exchange, a GEE API call, and potentially seconds of GEE compute time before failing. Without auth, an attacker can flood these endpoints with arbitrarily large polygons.
-
Error message disclosure: GEE errors are caught and re-thrown with the GEE status code and partial response text:
throw new Error(\GEE compute failed (${resp.status}): ${t}`)` — these details leak into the 500 response to the caller.
-
Unexpected behavior with degenerate geometries: A polygon with 2 points, a self-intersecting ring, or coordinates outside [-180,180]/[-90,90] will produce undefined GEE behavior that may silently return wrong results rather than an error.
Root cause
The input validation layer was skipped entirely. The assumption is that coordinates come from the frontend map drawing tool (Mapbox GL Draw), which produces valid GeoJSON. This trust boundary is invalidated the moment a caller can POST arbitrary JSON to the endpoint.
Recommended fix
Add a validation function before any GEE call:
function validatePolygon(coords: [number, number][]): void {
if (coords.length < 4) throw new Error("Polygon must have at least 4 coordinate pairs (3 vertices + closure)");
if (coords.length > 500) throw new Error("Polygon exceeds maximum vertex count (500)");
for (const [lng, lat] of coords) {
if (typeof lng !== "number" || typeof lat !== "number") throw new Error("Non-numeric coordinate");
if (lng < -180 || lng > 180) throw new Error(`Longitude out of range: ${lng}`);
if (lat < -90 || lat > 90) throw new Error(`Latitude out of range: ${lat}`);
}
// Area check: compute approximate bounding box area, reject > ~50,000 km² (country-scale)
const lngs = coords.map(c => c[0]);
const lats = coords.map(c => c[1]);
const dLng = Math.max(...lngs) - Math.min(...lngs);
const dLat = Math.max(...lats) - Math.min(...lats);
if (dLng * dLat > 20) throw new Error("Polygon bounding box exceeds maximum allowed area");
}
Also reduce maxPixels from 1_000_000_000 to a realistic value (e.g., 100_000_000 for field-scale analysis).
Acceptance criteria
Suggested labels
security, bug
Priority
P2
Severity
Medium — enables cost amplification (more targeted than the auth issue alone) and potential information disclosure via error messages. Lower severity because fixing auth mitigates anonymous abuse.
Confidence
Confirmed — all four GEE-calling functions parse and use polygon coordinates with no validation beyond checking that the input is an array with ≥ 3 elements.
Summary
No Edge Function validates the polygon coordinate input before passing it to Google Earth Engine. There are no limits on vertex count, coordinate range, or geographic area. A malicious request with thousands of coordinates, an invalid bounding box, or coordinates outside the Earth's surface will trigger expensive GEE computation attempts before failing.
Evidence
supabase/functions/analyze-field/index.ts:Same pattern in
gee-analytics/index.ts,gee-ndvi-tiles/index.ts, andndvi-timeseries/index.ts. None of them check:The GEE call itself sets
maxPixels: { constantValue: 1000000000 }— 1 billion pixels — which is a very high limit that does not protect against resource exhaustion before the GEE server rejects the request.Why this matters
throw new Error(\GEE compute failed (${resp.status}): ${t}`)` — these details leak into the 500 response to the caller.Root cause
The input validation layer was skipped entirely. The assumption is that coordinates come from the frontend map drawing tool (Mapbox GL Draw), which produces valid GeoJSON. This trust boundary is invalidated the moment a caller can POST arbitrary JSON to the endpoint.
Recommended fix
Add a validation function before any GEE call:
Also reduce
maxPixelsfrom1_000_000_000to a realistic value (e.g.,100_000_000for field-scale analysis).Acceptance criteria
getGeeAccessToken()maxPixelsis set to a value that reflects the maximum expected field sizeSuggested labels
security, bug
Priority
P2
Severity
Medium — enables cost amplification (more targeted than the auth issue alone) and potential information disclosure via error messages. Lower severity because fixing auth mitigates anonymous abuse.
Confidence
Confirmed — all four GEE-calling functions parse and use polygon coordinates with no validation beyond checking that the input is an array with ≥ 3 elements.