GraphQL resolvers often check the top-level object and skip the nested ones
A common GraphQL setup puts the permission check in the root resolver. project(id: 7) checks that you're a member of project 7 and returns it. Every nested field resolves on its own after that. So a query like project(i
A common GraphQL setup puts the permission check in the root resolver. project(id: 7) checks that you're a member of project 7 and returns it. Every nested field resolves on its own after that.
So a query like project(id: 7) { linkedProjects { name members { email } } } can walk right out of the project you were allowed to see. A linked project might belong to another team, and its members resolver never asked whether you can see that team.
What fixes most of it:
- Put the check in each type's resolver, or in the data loader that fetches that type, keyed on the object being returned.
- Treat any field that returns another object as a new authorization question, even when the parent passed.
To test it, sign in as a user who belongs to one project and write a query that follows every relation two or three levels deep. Any object from outside their projects that comes back is a hole. Introspection gives you the full list of relations to try.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.