From 52791a1a7b49377c56f6145bef8225594aa7661c Mon Sep 17 00:00:00 2001 From: gohan Date: Mon, 4 Aug 2025 22:27:45 +0900 Subject: [PATCH 1/2] graphql: document depth limit --- graphql/graphql_test.go | 34 ++++++++++++++++++++++++++++++++++ graphql/service.go | 7 ++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/graphql/graphql_test.go b/graphql/graphql_test.go index 0f6ba10b902c..ca864d5fb2fb 100644 --- a/graphql/graphql_test.go +++ b/graphql/graphql_test.go @@ -430,6 +430,40 @@ func TestWithdrawals(t *testing.T) { } } +// TestGraphQLMaxDepth ensures that queries exceeding the configured maximum depth +// are rejected to prevent resource exhaustion from deeply nested operations. +func TestGraphQLMaxDepth(t *testing.T) { + stack := createNode(t) + defer stack.Close() + + h, err := newHandler(stack, nil, nil, []string{}, []string{}) + if err != nil { + t.Fatalf("could not create graphql service: %v", err) + } + + var b strings.Builder + for i := 0; i < maxQueryDepth+1; i++ { + b.WriteString("ommers{") + } + b.WriteString("number") + for i := 0; i < maxQueryDepth+1; i++ { + b.WriteString("}") + } + query := fmt.Sprintf("{block{%s}}", b.String()) + + res := h.Schema.Exec(context.Background(), query, "", nil) + var found bool + for _, err := range res.Errors { + if err.Rule == "MaxDepthExceeded" { + found = true + break + } + } + if !found { + t.Fatalf("expected max depth exceeded error, got %v", res.Errors) + } +} + func createNode(t *testing.T) *node.Node { stack, err := node.New(&node.Config{ HTTPHost: "127.0.0.1", diff --git a/graphql/service.go b/graphql/service.go index 584165bdb802..d16e85ef2646 100644 --- a/graphql/service.go +++ b/graphql/service.go @@ -32,6 +32,11 @@ import ( gqlErrors "github.com/graph-gophers/graphql-go/errors" ) +// maxQueryDepth limits the maximum field nesting depth allowed in GraphQL queries. +// Without this bound, deeply nested queries could exhaust resources and lead to DoS. +// See https://github.com/ethereum/go-ethereum/issues/26026 for more details. +const maxQueryDepth = 20 + type handler struct { Schema *graphql.Schema } @@ -116,7 +121,7 @@ func New(stack *node.Node, backend ethapi.Backend, filterSystem *filters.FilterS func newHandler(stack *node.Node, backend ethapi.Backend, filterSystem *filters.FilterSystem, cors, vhosts []string) (*handler, error) { q := Resolver{backend, filterSystem} - s, err := graphql.ParseSchema(schema, &q) + s, err := graphql.ParseSchema(schema, &q, graphql.MaxDepth(maxQueryDepth)) if err != nil { return nil, err } From f485b23602f7e5bf59dedc2842e0df9d27558c95 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 7 Aug 2025 20:28:18 +0200 Subject: [PATCH 2/2] Update service.go --- graphql/service.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/graphql/service.go b/graphql/service.go index d16e85ef2646..9381a51da6d9 100644 --- a/graphql/service.go +++ b/graphql/service.go @@ -33,8 +33,6 @@ import ( ) // maxQueryDepth limits the maximum field nesting depth allowed in GraphQL queries. -// Without this bound, deeply nested queries could exhaust resources and lead to DoS. -// See https://github.com/ethereum/go-ethereum/issues/26026 for more details. const maxQueryDepth = 20 type handler struct {