Skip to content

Add optional maxDepth and maxAliases execution options#4666

Open
eddieran wants to merge 1 commit intographql:16.x.xfrom
eddieran:fix/execution-depth-alias-limits
Open

Add optional maxDepth and maxAliases execution options#4666
eddieran wants to merge 1 commit intographql:16.x.xfrom
eddieran:fix/execution-depth-alias-limits

Conversation

@eddieran
Copy link
Copy Markdown

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 > maxDepth is encountered, a GraphQLError is raised and the parent field resolves to null (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, a GraphQLError is raised before the selection set executes.

Both options are undefined by default — no limits are applied and existing behavior is fully preserved. They are passed via ExecutionArgs.options alongside the existing maxCoercionErrors:

const result = await execute({
  schema,
  document,
  options: {
    maxDepth: 10,
    maxAliases: 50,
  },
});

Implementation details

  • Depth checking happens in executeField by walking the Path linked list (only counting string keys, not list indices)
  • Alias counting happens in executeOperation (root fields) and completeObjectValue (sub-selections) after field collection
  • Errors follow standard GraphQL error propagation — nullable parent fields are nulled, non-null fields propagate upward
  • 12 new tests covering: within-limit queries, exceeded limits, no-op when unset, list index handling, nested alias limits, combined usage

Test plan

  • All 12 new tests pass
  • Full test suite (1972 tests) passes with no regressions
  • TypeScript compilation clean
  • ESLint clean

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
@linux-foundation-easycla
Copy link
Copy Markdown

CLA Not Signed

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 14, 2026

@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.

Copy link
Copy Markdown
Contributor

@yaacovCR yaacovCR left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could consider adding a fieldDepth property to FieldDetails of type number? Then we wouldn't have to keep recalculating the path length?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Security] No built-in query depth/complexity limit + alias bombing DoS

2 participants