Skip to content

fix: six mutating routes accept unauthenticated callers #86

Open

Description

@numarasSigmaSoftware

Symptom

Six POST routes declare no auth dependency, and there is no router-level or middleware guard in front of them:

  • POST /deals (routers/deals.py, generate_deal)
  • POST /agentic-audience/match (routers/deals.py, agentic_audience_match)
  • POST /api/v1/deals/curated (routers/deals.py, create_curated_deal)
  • POST /api/v1/deals/{deal_id}/deprecate (routers/deals.py, deprecate_deal)
  • POST /sessions/{session_id}/messages (routers/sessions.py, send_session_message)
  • POST /sessions/{session_id}/close (routers/sessions.py, close_session_endpoint)

An anonymous caller can mint a curated deal, deprecate any deal, or close any buyer's session.

Reproduction

Three anonymous requests, no Authorization or X-Api-Key header, against the app with an empty in-memory store:

POST /api/v1/deals/DEMO-NOPE/deprecate  {"reason": "probe"}   -> 404 deal_not_found
POST /sessions/nope/close                                     -> 200
POST /api/v1/deals/curated  {"curator_id": "agent-range", "deal_type": "PMP"}  -> 201, deal minted

A 401 would mean an auth check ran. The 404 shows the deprecate handler executed and looked the deal up; the 200 shows a session can be "closed" by anyone, even one that does not exist; the 201 is an anonymous caller minting a curated deal with a curator fee attached. The other three routes were confirmed by reading their signatures: none declares a dependency, and routers/deals.py and routers/sessions.py both construct APIRouter() with no dependencies.

Environment

seller-agent main at 8dfd5e0 (after #78), installed with uv sync --locked: Python 3.12.12, fastapi 0.139.0, iab-agentic-primitives==0.5.1. macOS 26.6. Reproduced in-process against the FastAPI app through httpx.ASGITransport, storage replaced by an in-memory double, so the numbers above are what the routes return, not what an ad server does.

Context

#39 required operator credentials on the admin surface and recorded which buyer-facing reads deliberately stay public. None of the six routes above is on that list, so their openness reads as an omission rather than a decision. #77 later closed the same gap on POST /api/v1/deals.

Expected

Deprecate and curated-deal creation are operator actions and should carry deps._require_operator_api_key_record. Session messages and close should require the buyer key that created the session, or an operator key. POST /deals and /agentic-audience/match need a stated decision either way.

Found while checking auth parity for a new list route; pre-existing on main at 8dfd5e0.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions