Skip to content

sec(api): planned-purchase DELETE checks update-any on the key owner, not the API key's effective permissions #979

Closed

Description

@cristim

Found while independently reviewing #978, which closes #678 and is stacked on #975.

Problem

authorizePlannedPurchaseCancel (internal/api/handler_purchases.go, the DELETE /api/purchases/planned/{id} route) grants full scope to update-any with h.auth.HasPermissionAPI(ctx, session.UserID, ...). That checks the owning user's permissions, not the API key's effective permissions.

So a user-API-key session whose key holds only a narrow verb (for example cancel-own or delete on purchases) can cancel any user's planned purchase whenever the key's owner holds update-any. The key's own scope is ignored for that branch.

This is an API-key scope escalation, separate from the missing-Constraints class that #961 tracks:

Expected

Every verb that authorizes this route should be evaluated against the session's effective permissions, the same way #957 and the revoke and cancel paths do. For a user-API-key session that means the key's permissions, intersected with the owner's. The checks should also apply that grant's Constraints (#961).

Suggested test

  • A user-API-key session whose key has only cancel-own on purchases, and whose owner holds update-any, must get 403 or 404 deleting another user's planned purchase.
  • A control: the same session can delete its own.

Related: #961, #678, #975, #978, #956.

Activity

  1. IAmJSD commented on Oct 16, 2026

    @IAmJSD
    Collaborator

    Claiming: working on this

  2. self-assigned this
    on Oct 16, 2026
  3. added 2 commits that reference this issue on Oct 16, 2026
    a6b985c
    3c726d2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions