Add optional maxDepth and maxAliases execution options#4666
Add optional maxDepth and maxAliases execution options#4666eddieran wants to merge 2 commits intographql:16.x.xfrom
Conversation
Add opt-in depth and alias limits to the execution engine to mitigate denial-of-service attacks via deeply nested queries and alias bombing. - maxDepth: limits the field nesting depth during execution. When a field exceeds the configured depth, a GraphQLError is raised and the parent field resolves to null (standard error handling). - maxAliases: limits the number of response keys (including aliases) per selection set. When exceeded, a GraphQLError is raised before the selection set is executed. Both options are undefined by default, preserving full backwards compatibility. They are passed via ExecutionArgs.options alongside the existing maxCoercionErrors option. Fixes graphql#4662
|
|
@eddieran is attempting to deploy a commit to the The GraphQL Foundation Team on Vercel. A member of the Team first needs to authorize it. |
yaacovCR
left a comment
There was a problem hiding this comment.
Maybe we could consider adding a fieldDepth property to FieldDetails of type number? Then we wouldn't have to keep recalculating the path length?
Addresses review feedback: rather than walking the Path linked list at every executeField invocation, thread the current field depth as an explicit parameter through executeFields/executeField/completeValue and its helpers. completeObjectValue increments depth when descending into sub-selections; list items and abstract-type resolution keep the same depth as their parent field. This changes the depth check from O(depth) per field resolution to O(1), while preserving identical semantics (list indices still do not count toward depth).
|
Thanks for the review, @yaacovCR! Since This turns the depth check into O(1) per field resolution instead of O(depth) walk of the Path linked list, while preserving identical semantics (list indices still don't count). All 12 new tests and the full 1972-test suite still pass. Pushed in f9d67dc. Happy to take further suggestions, e.g. if you'd prefer a different naming or placement. |
Summary
Fixes #4662 — the execution engine has no built-in depth or complexity limits, allowing deep recursive queries and alias-bombing to cause denial-of-service via resolver amplification.
This PR adds two opt-in execution options:
maxDepth— limits the field nesting depth during execution. When a field at depth >maxDepthis encountered, aGraphQLErroris raised and the parent field resolves tonull(standard nullable error handling). List indices do not count toward depth.maxAliases— limits the number of response keys (including aliases) per selection set. This prevents alias-bombing attacks where thousands of aliases for the same field bypass depth-based defenses. When exceeded, aGraphQLErroris raised before the selection set executes.Both options are
undefinedby default — no limits are applied and existing behavior is fully preserved. They are passed viaExecutionArgs.optionsalongside the existingmaxCoercionErrors:Implementation details
executeFieldby walking thePathlinked list (only counting string keys, not list indices)executeOperation(root fields) andcompleteObjectValue(sub-selections) after field collectionTest plan