GraphQL Missing Depth/Complexity Limit

ID

graphql_missing_depth_limit

Severity

low

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Family

API4:2023 - Unrestricted Resource Consumption

CWE

CWE-770

Resource

configuration

Language

any

Description

Fires once per GraphQL service when the project constructs a GraphQL server but shows no evidence of a query depth or complexity limiter — graphql-depth-limit, graphql-query-complexity, graphql-validation-complexity, GraphQL Armor, graphql-java MaxQueryDepthInstrumentation / MaxQueryComplexityInstrumentation, Strawberry QueryDepthLimiter, Hot Chocolate AddMaxExecutionDepthRule, or an explicit maxDepth/max_depth configuration.

Evidence patterns are deliberately generous — an unrelated match only silences the finding — and gateway/proxy-level enforcement is not visible to static analysis, so the finding is advisory until reviewed against the deployment. SDL-only repositories are not flagged.

Rationale

Unlike REST, a single GraphQL request describes an arbitrary computation over the graph. Without a depth/complexity ceiling, deeply-nested queries, alias fan-out, and circular fragments over connected types (user → friends → friends → …) let one request multiply into unbounded resolver and database work — the GraphQL flavor of OWASP API4:2023, Unrestricted Resource Consumption (CWE-770). This is the standard vector for GraphQL denial-of-service and denial-of-wallet, and it also amplifies enumeration attacks that per-request rate limits would otherwise slow down.

Remediation

Add a query depth and/or complexity ceiling appropriate to your stack:

  • GraphQL.js / Apollo: validationRules: [depthLimit(8)] from graphql-depth-limit, or graphql-query-complexity with a cost estimator

  • GraphQL Armor: enables maxDepth/cost guards out of the box

  • graphql-java: new MaxQueryDepthInstrumentation(10) / MaxQueryComplexityInstrumentation

  • Strawberry: extensions=[QueryDepthLimiter(max_depth=10)]

  • Hot Chocolate: .AddMaxExecutionDepthRule(10)

Pick the limit from your deepest legitimate query plus margin, and monitor/alert on rejected queries to catch probing.