# GraphQL resolver missing permission checks

GraphQL fields or mutations returning protected data without the scope, role, or tenant check the UI enforces

_Area: Web access control and IDOR. Seen in 4 disclosed reports; 3 confirmed bounties, median $500, range $100-$800; latest 2023-03-10. Updated 2026-10-23._

## How it works
A GraphQL schema exposes fields and mutations that resolve data directly. The assumption that breaks is that the UI or REST path is the only way to reach an object, so the permission gate lives there. When a resolver is added or a feature migrates to GraphQL, the resolver returns the object without re-checking the scope, role, or tenant the UI required. Any caller who can name the field gets the data.

## Where it shows up
- A field is scope-gated but adjacent event, activity, or message objects on the same node embed the same data in human-readable strings [E1](https://bugbountylibrary.com/r/h1%3A1392032.md).
- A mutation exists for an action the UI hides from some roles; its authorization checks a weaker property (assignee status) than the UI (role or authorship) [E2](https://bugbountylibrary.com/r/h1%3A762271.md).
- A query resolves across all tenants or programs and is reachable unauthenticated [E3](https://bugbountylibrary.com/r/h1%3A1868473.md).
- A feature migrated from a REST/UI route to a new resolver, especially nested fields on a parent object [E4](https://bugbountylibrary.com/r/h1%3A528940.md).
- Newly shipped features: append the feature path to an object URL and inspect the operationName.

## How to review
For each resolver, identify the object it returns and the exact permission the UI or sibling API requires for that object and field. The invariant: every resolver enforces the same scope, role, and tenant check as the most restrictive path to the same data, on the object itself, not on a proxy property like assignee status. Check nested fields and event/message sub-objects, not just top-level fields. Confirm unauthenticated and low-role callers are rejected. Confirm cross-tenant queries filter by the caller's tenant.

## Variants seen
- Protected data leaked through event messages on a permitted node, then reused against a second lookup that skipped the check [E1](https://bugbountylibrary.com/r/h1%3A1392032.md).
- Mutation authorized by assignee status instead of role or authorship [E2](https://bugbountylibrary.com/r/h1%3A762271.md).
- Unauthenticated cross-tenant query returning private program scopes [E3](https://bugbountylibrary.com/r/h1%3A1868473.md).
- Migrated feature whose new resolver dropped the old UI permission gate [E4](https://bugbountylibrary.com/r/h1%3A528940.md).

## Impact and severity
Reads exposed order details, private program scopes, and activity feeds; one mutation let a non-member change confidentiality. Severity ranged from low-role data exposure to unauthenticated cross-tenant disclosure. Worse when the resolver is unauthenticated, spans tenants, or chains into a second unchecked lookup [E1](https://bugbountylibrary.com/r/h1%3A1392032.md)[E3](https://bugbountylibrary.com/r/h1%3A1868473.md).

## Evidence
- E1: https://bugbountylibrary.com/r/h1%3A1392032.md
- E2: https://bugbountylibrary.com/r/h1%3A762271.md
- E3: https://bugbountylibrary.com/r/h1%3A1868473.md
- E4: https://bugbountylibrary.com/r/h1%3A528940.md
