Skip to content

d-exclaimation/graphql-depth-limit

Repository files navigation

GraphQL Depth Limit

Dead-simple defense against unbounded GraphQL queries. Limit the complexity of the queries solely by their depth.

Implementation of GraphQL Depth Limit for GraphQLSwift/GraphQL

Why?

Suppose you have an User type that has a list of Items.

type User {
  id: Int!
  items: [Item!]!
}

type Item {
  id: Int!
  label: String!
  owner: User!
}

and you would normally expect query that look something like:

{
  user(id: 10) {
    id
    items {
      label
    }
  }
}

Given that the Item allow you to get back up to User, that opens your server to the possibility of a cyclical query!

query Evil {
  user(id: 10) {
    items {
      owner {
        items {
          owner {
            items {
              owner {
                items {
                  owner {
                    items {
                      owner {
                        items {
                          owner {
                            items {
                              owner {
                                # and so on...
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

This will likely become a very expensive operation, at some point pinning a CPU on the server or perhaps the database, which opens up the possiblility of DOS attacks. Therefore, you might want a way to validate the complexity of incoming queries.

This implementation lets you limit the total depth of each operation.

Usage

Add this package to your Package.swift

.package(url: "https://github.com/d-exclaimation/graphql-depth-limit", from: "0.1.0")

It works without any additional libraries outside GraphQLSwift/GraphQL

Usage without additional libraries

import GraphQL
import GraphQLDepthLimit

let query: String = "..."
let ast = try parse(source: .init(body: query))
try await graphql(validationRules: [depthLimit(ast, max: 10)], schema: schema, request: query, eventLoopGroup: eventLoopGroup).get()

Usage with Pioneer

import Vapor
import Pioneer
import GraphQLDepthLimit

let app = try Application(.detect())
let server = Pioneer(
    schema: schema,
    resolver: resolver,
    contextBuilder: { req, res in

    },
    validationRules: .computed({ gql in
        [depthLimit(gql.ast, max: 10)]
    })
)

server.applyMiddleware(on: app)

Now the query above should result in an error response:

{
  "errors": [
    {
      "message": "Operation 'Evil' exceeds maximum operation depth of 10",
      "locations": [
        {
          "line": 12,
          "column": 25
        }
      ]
    }
  ]
}

About

Dead-simple defense against unbounded GraphQL queries

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages