Skip to content

sec(api): RI exchange session approve still leaks missing (404) vs not-pending (409) to approve-own callers #1428

Closed

Description

@IAmJSD

Follow-up that cristim asked for in his review of #1175 (VERDICT: APPROVE 8999bc2, first nit): "Track both in one follow-up issue so the 404 half is not lost."

What happens

POST /api/ri-exchange/approve/{id} with a signed-in session that holds approve rights goes through fetchAndAuthorizeRIExchange (internal/api/handler_ri_exchange.go:2534-2565 on main at 6b586cc). That function decides two things before it checks whether the caller may approve this particular exchange:

Only after that does authorizeSessionApproveRIExchange (line 2561) refuse an approve-own caller who did not create the exchange. That refusal is a 403, which falls through to the email-token check when a token is present.

So a signed-in user who holds only approve-own on purchases can tell, for any exchange id, whether it exists and whether it is still pending, and read its status, even for exchanges they did not create. #1175 closes this oracle for callers using only the token (#601), but not for this session path.

The impact is low. The caller is authenticated and already holds approve rights, the ids are random UUIDs, and the same-account scope check (#93) runs first. It is the same leak #172 fixed for purchase executions.

Suggested fix

In fetchAndAuthorizeRIExchange, for a session without approve-any, give a missing record and another user's record the same answer as the ownership refusal, and check ownership before the status:

  1. fetch the record; on a missing record, return the same error as "not yours" (so it also falls through to the token, as another user's record does today);
  2. run authorizeSessionApproveRIExchange;
  3. only then answer 409 for a non-pending status.

Approve-any sessions and the admin key can keep the 404/409 distinction, since they may approve any exchange anyway.

Acceptance criteria

  • For an approve-own session, a missing id, another user's pending exchange and another user's non-pending exchange produce the same status and body, with and without a token.
  • The creator of the exchange still gets 409 for a non-pending exchange, and approve-any still gets 404/409.
  • A table test through HandleRequest pins this, in the style of fix(api): answer a missing RI exchange like a wrong token on approve and reject #1175's oracle test.

Refs #601, #1175, #1172, #172.

Filed by IAmJSD's agent at IAmJSD's request (acting owner while the maintainers are away).

Activity

  1. IAmJSD commented on Oct 26, 2026

    @IAmJSD
    CollaboratorAuthor

    Claiming: working on this

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions