Skip to content

No rate limiting on any Edge Function — ndvi-timeseries amplifies one HTTP request to 40 GEE API calls #6

@tg12

Description

@tg12

Summary

No Edge Function implements rate limiting, per-IP quotas, or request throttling. Several endpoints are expensive in compute units per call: ndvi-timeseries fires up to 40 parallel GEE API requests per HTTP request; gee-analytics fires 4–8 GEE computations; analyze-field and crop-planning invoke Gemini 2.5 Pro/Flash. Any caller can drive unbounded API spend with a trivial loop.

Evidence

supabase/functions/ndvi-timeseries/index.ts — 40 GEE calls per request:

const imageCount = Math.min(count, 20);
const promises: Promise<...>[] = [];

for (let i = 0; i < imageCount; i++) {
  const { timestamp, ndviVal } = buildImageNdviAtIndex(listExpr, i, geometry);
  promises.push(
    Promise.all([
      computeValue(token, projectId, timestamp),  // GEE call 1
      computeValue(token, projectId, ndviVal),    // GEE call 2
    ])...
  );
}
const rawTimeseries = await Promise.all(promises);  // all 40 fired at once

supabase/functions/gee-analytics/index.ts — up to 4 parallel sub-analyses, each with multiple computeValue calls (e.g., computeVegetationIndices fires 3 parallel GEE calls, computeLandSuitability fires 4).

Neither function, nor any other function in the project, contains:

  • Rate limiting headers or logic
  • Request counter / token bucket
  • Per-user or per-IP throttle
  • Supabase Edge Function concurrency limit configuration
  • Any cost-guard check before invoking the external API

supabase/config.toml has no [functions.<name>] max_requests_per_second or equivalent setting.

Why this matters

Cost amplification attack: a single HTTP request to ndvi-timeseries generates 40 GEE compute-unit requests. Sending 100 HTTP requests/second (trivial for a bot) generates 4,000 GEE API calls per second against the project owner's account. GEE's non-commercial usage tier has strict quotas; commercial overages are billed to the Google Cloud project. The same applies to Lovable AI gateway (per-token billing for Gemini 2.5 Pro).

Attack or failure scenario

  1. Attacker discovers the public Supabase URL from the committed .env.
  2. Runs: while true; do curl -X POST .../functions/v1/ndvi-timeseries -d '{"polygon":...}' & done
  3. Each iteration spawns 40 parallel GEE API calls.
  4. GEE quota exhausted for the day or Google Cloud billing spikes within minutes.
  5. All legitimate users are denied GEE service for the rest of the quota period.

Root cause

The application was built without a budget constraint mindset. The assumption is that callers are legitimate app users, not adversarial bots. With no auth (see related issue) and no rate limiting, cost-amplification attacks require zero sophistication.

Recommended fix

Immediate: Fix the auth issue first — requiring a Supabase JWT removes anonymous abuse. Authenticated users are rate-limited by their session identity.

Additional hardening:

  1. Implement a server-side request counter using Supabase's Redis-compatible pg_redis or a simple Postgres counter with a time window, keyed by authenticated user ID.
  2. Set per-user daily limits for GEE and AI calls (e.g., 10 GEE time-series analyses per user per day).
  3. For ndvi-timeseries, cap imageCount to a lower value (e.g., 10) and add a per-field cooldown to prevent re-fetching the same data within a short window.
  4. Consider Supabase Edge Runtime's built-in request rate limiting via the dashboard (Project Settings → Edge Functions → Rate Limits).
  5. Set maxPixels to a realistic value rather than 1_000_000_000 — this allows GEE to fail fast on excessively large polygons rather than running to quota exhaustion.

Acceptance criteria

  • Authenticated users are subject to a per-user daily limit on GEE and AI API calls
  • ndvi-timeseries caps parallel image processing to ≤ 10 images
  • Unauthenticated requests are rejected before any external API call is made
  • A runbook documents GEE/AI quota limits and what to do when they are hit

Suggested labels

security, bug

Priority

P1

Severity

High — cost-amplification attack possible with zero credentials. Severity is High rather than Critical only because fixing the auth issue substantially mitigates the attack surface.

Confidence

Confirmed — no rate limiting code exists in any Edge Function; ndvi-timeseries makes up to 40 GEE API calls per HTTP request.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions