# REST, GraphQL, gRPC, WebSocket, and SSE permission parity

Carry the same permission invariant across protocols, nested fields, streams, and alternate API versions.

## Investigation sequence

1. Inventory observed operations
2. Choose one permission invariant
3. Compare controlled identities
4. Test alternate protocol path
5. Check stream lifecycle
6. Record semantic result

## Used in these playbooks

[Web & API](https://bug-bounty-field-manual.kmahshooq.chatgpt.site/web-api.html) · [OSS & self-hosted](https://bug-bounty-field-manual.kmahshooq.chatgpt.site/oss.html) · [Mobile](https://bug-bounty-field-manual.kmahshooq.chatgpt.site/mobile.html) · [Desktop & extensions](https://bug-bounty-field-manual.kmahshooq.chatgpt.site/desktop.html) · [Cloud & containers](https://bug-bounty-field-manual.kmahshooq.chatgpt.site/cloud.html) · [AI, agents & MCP](https://bug-bounty-field-manual.kmahshooq.chatgpt.site/ai.html)

### Setup and invariant

Start from normal client traffic and supplied API schemas. Create two controlled accounts and private synthetic objects. Map REST paths, GraphQL operations/fields, gRPC methods, and WebSocket message types to the same business action. The invariant is: **changing transport, API version, batching, or connection lifetime does not widen an actor's permission**. Reuse the authorization matrix and session modules; protocol-specific testing adds delivery details rather than duplicating those foundations.

### Numbered test procedure

1. **Build a semantic inventory.** Record operation name, method/path, actor, tenant selector, object ID, request schema, response fields, and side effects. Observe one success and one forbidden controlled-object request per available protocol. Preserve response bodies, GraphQL errors, gRPC status/trailers, or socket messages; HTTP status alone does not define application success.
2. **Check REST parity.** Replay the same controlled-object rule through observed v1/v2 routes, nested resources, exports, and a two-item batch. Compare read, update, and server-controlled properties independently. An alternate route being live is not a vulnerability unless its behavior violates the intended permission or lifecycle rule.
3. **Check GraphQL resolvers and fields.** Begin with the client's existing query. As B, change only A's known object ID, then request one protected field omitted from B's normal response. Try the observed global-node route if present. Use at most two aliases as a bounded comparison of resolver behavior; do not use recursive depth or massive aliases. A schema or introspection response helps discovery but is not a confidentiality breach by itself. Where cookie authentication and alternate HTTP methods exist, check a reversible mutation through the observed transport: GraphQL GET is for queries, not mutations. If claiming CSRF, prove delivery in a real browser using the [browser-trust procedure](https://bug-bounty-field-manual.kmahshooq.chatgpt.site/modules/browser-trust.html). Persisted query IDs are operation selectors, not permission proofs. [GraphQL HTTP semantics](https://graphql.org/learn/serving-over-http/). [GraphQL testing](https://portswigger.net/web-security/graphql).
4. **Check gRPC context.** Use the provided `.proto` file or authorized reflection to identify one method. Compare channel identity, per-call credentials, tenant metadata, and the selected resource. Repeat a forbidden request with B's genuine credentials, not a token fabricated from decoded claims. For streaming methods, check whether a permitted start improperly authorizes later messages for another controlled resource. Channel credentials and call credentials serve different roles. If an HTTP/JSON transcoding gateway or gRPC-Web route is actually deployed, replay this same invariant there: a translated request can travel through different authentication middleware. Preserve the gateway path and backend method instead of inferring exposure from a `.proto` annotation. [HTTP/JSON transcoding](https://docs.cloud.google.com/endpoints/docs/grpc/transcoding). [gRPC authentication](https://grpc.io/docs/guides/auth/).
5. **Check WebSocket and SSE lifecycle.** Capture a normal handshake and one subscription message. In B's profile, request A's private room using its known ID; have A send a single harmless marker. Remove B from a previously shared room and test one further message on the existing socket. For cross-site hijacking, use a real controlled origin and actual browser cookies. An accepted forged Origin header from a nonbrowser client does not prove browser delivery. [OWASP WebSocket security](https://cheatsheetseries.owasp.org/cheatsheets/WebSocket_Security_Cheat_Sheet.html).
For server-sent events, use the observed event endpoint with A and B, then remove B and inspect one new synthetic event. A reconnect cursor or `Last-Event-ID` selects delivery position; test that it cannot select another controlled tenant's history. Close the browser EventSource during cleanup because it normally reconnects automatically. [SSE connection and reconnection](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events).

6. **Interpret and consolidate.** Read partial data as well as error arrays; an error on one field can coexist with leaked data elsewhere. Correlate socket subscription IDs and asynchronous messages. Group instances by shared root cause only when the evidence supports it, and name untested transports explicitly. Close sockets and remove test resources.

### Command card

Prepare protected `A.header` and `B.header` files as described in the [authorization module](https://bug-bounty-field-manual.kmahshooq.chatgpt.site/modules/authz-matrix.html). The example assumes the local schema exposes `document(id:)`. Replace it with the already observed query, not an invented production operation. The second request changes only identity.

```bash
mkdir -p evidence/protocols
cat > document-query.json <<'JSON'
{"query":"query($id:ID!){document(id:$id){id title}}","variables":{"id":"O1"}}
JSON
curl -q --noproxy '*' --max-time 10 --header @A.header \
  -H 'Content-Type: application/json' --data @document-query.json \
  -o evidence/protocols/a.json http://127.0.0.1:8000/graphql
curl -q --noproxy '*' --max-time 10 --header @B.header \
  -H 'Content-Type: application/json' --data @document-query.json \
  -o evidence/protocols/b.json http://127.0.0.1:8000/graphql
```

For a local socket, use the browser's Network → WS view to copy the observed message schema. Change one controlled room ID and send one message; protocol libraries often add framing, acknowledgements, or subscription IDs that a generic text frame would omit.

### Report threshold and checklist

Report forbidden controlled data/action, a protected-field leak, or unauthorized ongoing delivery after the applicable revocation point. Reflection, introspection, websocket connectivity, and partial errors without forbidden output are discovery observations.

- [ ] Normal client operation and schema preserved.
- [ ] Same permission rule compared across available transports.
- [ ] Application-level errors and partial output inspected.
- [ ] Authentication at connection and message levels distinguished.
- [ ] Every stream closed and fixture cleaned up.

## Vulnerability reverse references

- [Broken object-level authorization (BOLA / IDOR)](https://bug-bounty-field-manual.kmahshooq.chatgpt.site/vulnerabilities/bola-controlled-object-access.html) — An actor can access another owner’s protected object by supplying its identifier.
- [Broken function-level authorization (BFLA)](https://bug-bounty-field-manual.kmahshooq.chatgpt.site/vulnerabilities/bfla-restricted-function.html) — A lower-privilege actor invokes a function reserved for another role.
- [Broken property authorization: private response fields](https://bug-bounty-field-manual.kmahshooq.chatgpt.site/vulnerabilities/bopla-private-response-property.html) — An otherwise permitted object response includes fields the actor is not entitled to read.
- [Authentication or MFA state bypass](https://bug-bounty-field-manual.kmahshooq.chatgpt.site/vulnerabilities/authentication-incomplete-state-bypass.html) — A session that has not completed required authentication can perform protected operations.
- [Residual access after explicit session revocation](https://bug-bounty-field-manual.kmahshooq.chatgpt.site/vulnerabilities/session-revocation-residual-access.html) — A revoked credential or removed membership retains access beyond the product’s applicable revocation guarantee.
- [GraphQL resolver or field authorization failure](https://bug-bounty-field-manual.kmahshooq.chatgpt.site/vulnerabilities/graphql-resolver-field-authz.html) — A GraphQL path or protected field bypasses the permission enforced for the same object elsewhere.
- [WebSocket message or subscription authorization failure](https://bug-bounty-field-manual.kmahshooq.chatgpt.site/vulnerabilities/websocket-message-authorization.html) — A valid connection performs unauthorized per-message actions or receives a private subscription.
- [Cross-site WebSocket hijacking](https://bug-bounty-field-manual.kmahshooq.chatgpt.site/vulnerabilities/cross-site-websocket-hijacking.html) — A page on another origin opens an authenticated socket and reads or performs protected operations.
- [Enforced SSO bypass through an alternate login path](https://bug-bounty-field-manual.kmahshooq.chatgpt.site/vulnerabilities/enforced-sso-alternate-login-bypass.html) — A protected tenant requiring federation remains accessible through a local or alternate authentication path outside the documented exceptions.
- [Unbounded resource allocation or request amplification](https://bug-bounty-field-manual.kmahshooq.chatgpt.site/vulnerabilities/resource-allocation-limit-failure.html) — An operation consumes an unconstrained protected resource through input size, fan-out, queued work, output size or retries.
