Skip to content

Harden Developer API read endpoints against abuse #8713

Description

@Git-on-my-level

Summary

Developer API read endpoints need abuse-prevention hardening. During a dev Cloud Run investigation, one client appeared to repeatedly poll the first page of a user's Developer API conversations for many hours/days using a valid authenticated API surface.

This issue tracks product/security improvements to prevent, detect, and respond to Developer API read abuse without exposing raw credentials or user content in logs.

Incident signal that motivated this

Observed in dev Cloud Run backend request logs:

  • Source IP: 45.92.45.62
  • User agent: Python-urllib/3.12
  • Endpoint: GET /v1/dev/user/conversations?limit=20
  • Status: 200
  • Response size: consistently 21034
  • No offset observed in sampled requests
  • Roughly ~1.3k successful reads/hour for sustained periods
  • Jun 30 sample window covered about 2026-06-30T07:31:10Z through 2026-06-30T22:48:24Z
  • Additional day-level checks suggested similar volume on Jun 25-30 in dev
  • Same IP/UA pattern was not observed in prod Developer API logs for Jun 30 in the checked query

Current interpretation: likely one compromised or abused Developer API credential repeatedly polling the same conversations page. Request logs alone cannot prove the exact uid, app_id, or key_id because the Cloud Run request log does not include sanitized auth identity.

Current gaps

Code areas:

  • backend/routers/developer.py
  • backend/dependencies.py
  • backend/database/dev_api_key.py
  • backend/utils/rate_limit_config.py
  • backend/utils/other/endpoints.py

Gaps found:

  1. Developer API conversation reads are scope-gated but not sufficiently rate-limited.
    • GET /v1/dev/user/conversations depends on get_uid_with_conversations_read.
    • POST /v1/dev/user/conversations already uses with_rate_limit(..., "dev:conversations").
    • Existing "dev:conversations": (25, 3600) policy is applied to writes, not list reads.
  2. Read dependencies collapse Developer API auth context down to uid.
    • API key auth has uid, scopes, app_id, and key_id.
    • Route dependencies often return only uid, so route code and rate-limit keys lose app_id/key_id attribution.
  3. Request logs do not contain enough sanitized identity to identify the abused key/account after the fact.
  4. No alerting/dashboard appears to detect repeated same-page Developer API polling.
  5. Sensitive read options such as full transcript access should have separate, stricter controls.

Proposed improvements

1. Add read rate limits for Developer API endpoints

Add separate policies for sensitive reads, for example:

  • dev:conversations_read
  • dev:conversation_detail_read
  • dev:conversation_transcript_read
  • dev:memories_read
  • dev:action_items_read
  • dev:goals_read

Apply them to all GET /v1/dev/user/* routes that return user data, not only writes.

2. Rate-limit by Developer API key identity

Prefer a rate-limit key like:

  • app_id:key_id when Developer API auth is present
  • fallback to uid only when key identity is unavailable

This prevents one user/app/key from consuming an entire account-level bucket and makes abuse attribution precise.

3. Preserve sanitized auth context through dependencies

Add auth dependencies that return ApiKeyAuth / product authorization context instead of only uid for Developer API routes that need:

  • uid
  • app_id
  • key_id
  • scopes

Keep existing uid-only dependencies where they are still appropriate, but reads that need audit/rate-limit attribution should receive the full sanitized context.

4. Add sanitized audit logs for Developer API calls

Log structured fields such as:

  • operation/path
  • status
  • uid
  • app_id
  • key_id
  • remote IP
  • user agent
  • limit/offset
  • include_transcript
  • returned count / coarse response metadata

Never log:

  • raw API keys
  • authorization headers
  • raw transcript text
  • raw memory/conversation/action item content
  • full response bodies

5. Add alerts and dashboards

Alert on suspicious Developer API read behavior, for example:

  • high read volume per key_id, app_id, uid, or IP
  • repeated polling of the same endpoint/query (limit=20 with no offset)
  • unusually constant response size over many successful requests
  • high include_transcript=true volume
  • Python/urllib or other generic scripted clients making sustained reads

6. Add emergency response controls

Provide an operator runbook and/or tooling to:

  • identify the implicated Developer API key from sanitized logs
  • revoke/rotate a specific key without affecting unrelated keys
  • temporarily deny an IP/user-agent at the edge if still active
  • communicate impact based on uid/app_id/key_id evidence

7. Tighten sensitive data access semantics

Consider separate scopes and stricter limits for:

  • conversation metadata reads
  • conversation detail reads
  • transcript reads (include_transcript=true)
  • bulk export-like access

Also consider maximum page size / export-window controls where needed.

8. Add regression tests

Add tests that ensure:

  • every Developer API user-data read route has a rate-limit policy
  • conversation transcript reads consume a stricter policy
  • rate-limit keys prefer app_id:key_id
  • route-level audit helpers do not log raw content or credentials
  • dependency changes preserve scope enforcement

Acceptance criteria

  • User-data Developer API GET endpoints are rate-limited.
  • Rate limits are keyed by Developer API key identity when available.
  • Logs include sanitized uid/app_id/key_id attribution for Developer API reads.
  • Transcript/full-content reads have stricter rate limits than metadata reads.
  • Alerts detect sustained same-page polling and high-frequency reads.
  • A runbook documents key identification, revocation, and temporary blocking.
  • Regression tests cover the policy wiring and auth-context preservation.

Notes

A candidate local patch was prototyped in a separate worktree/branch named dev-api-read-limits, but this issue should be treated as the tracking item for the full prevention/remediation work.

Metadata

Metadata

Assignees

No one assigned

    Labels

    SecuritybackendBackend Task (python)p1Priority: Critical (score 22-29)

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions