Personal Access Token scopes aren't honoured by GraphQL subscriptions
## Problem Any personal access token (no matter what scopes are selected) allow you to subscribe to anything in GraphQL regardless of whether or not the scope matches. This does not grant you access to resources your user cannot access. But it does expand the access of PATs that are meant to be scoped down to less data (eg. `read_user`). Specifically from our testing you can create a PAT with scope `read_user` and subscribe to updates for deleting notes on an issue (eg. `workItemNoteDeleted`) ## How to reproduce There are some scripts in https://gitlab.com/DylanGriffith/graphql-susbcription-client which is pending being moved to another home. It's private as we don't want to draw attention to this until it is fixed. You can ask for access or you can follow the steps below to set this up: Here is some node code for subscribing to the API: ```js // cable.js import WebSocket from 'ws' import { createCable } from '@anycable/core' export default createCable('ws://127.0.0.1:3000/-/cable', { websocketImplementation: WebSocket, websocketOptions: { headers: { 'Authorization': 'Bearer <YOUR_PAT_HERE>' }} }) ``` ```js // subscribe.js import cable from './cable.js' const subscription = cable.subscribeTo('GraphqlChannel', { "query":"subscription workItemNoteDeleted($noteableId: NoteableID) { workItemNoteDeleted(noteableId: $noteableId) { id discussionId lastDiscussionNote __typename }}","variables":{"noteableId":"gid://gitlab/WorkItem/609"},"operationName":"workItemNoteDeleted","nonce":"619ea684-343a-4060-b458-88cc4ceb028c" }); //const _ = await subscription.perform('speak', { msg: 'Hello' }) subscription.on('message', msg => { console.log(msg); }) ``` 1. Create these files in a directory 2. Run `yarn init` in that directory 3. Run `yarn add @anycable/core` 3. You may need to run `yarn add ws` 3. Update your GitLab instance with `config.action_cable.disable_request_forgery_protection = true` in your `config/initializers/action_cable.rb` (or figure out how to pass this request forgery check which probably requires some headers) 3. Update the above code with a URL for your GitLab instance (can be gitlab.com pending solving request forgery check above) 3. Update the above code with a correct `gid://gitlab/WorkItem` for a work item on your instance that you have access to read. You can find this by finding the noteable (e.g. Issue) on the rails console, then finding it's GID with `issue.to_global_id.to_s`, and then replace `Issue` in the string with `WorkItem` 3. Update the above code with `<YOUR_PAT_HERE>` with a PAT that has `read_user` scope only 4. Run the subscription code with `node subscribe.js` 5. Create a note on your work item 6. Delete a note from your work item 7. You will see log message showing the deleted note You shouldn't be able to see anything about notes or work items with a `read_user` scoped token. ## Introduced by In this MR we started allowing people to use PATs with graphql subscriptions. Prior to that only cookies worked so there was no such risk. https://gitlab.com/gitlab-org/gitlab/-/merge_requests/138084
issue