diff --git a/server/server.go b/server/server.go index 7bb382226096ea11d1e903f26beeb7c89b17c394..936afcea3d8cd0c861bd29b1b42362ead9c4b187 100644 --- a/server/server.go +++ b/server/server.go @@ -56,6 +56,8 @@ router chi.Router service string queues []*work.Queue email *work.Queue + + MaxComplexity int } // Creates a new common server context for a SourceHut GraphQL daemon. @@ -81,22 +83,19 @@ func (server *Server) WithSchema( schema graphql.ExecutableSchema, scopes []string) *Server { server.Schema = schema - var ( - complexity int - err error - ) + var err error if limit, ok := server.conf.Get( server.service+"::api", "max-complexity"); ok { - complexity, err = strconv.Atoi(limit) + server.MaxComplexity, err = strconv.Atoi(limit) if err != nil { panic(err) } } else { - complexity = 250 + server.MaxComplexity = 250 } srv := handler.GraphQL(schema, - handler.ComplexityLimit(complexity), + handler.ComplexityLimit(server.MaxComplexity), handler.RecoverFunc(EmailRecover), handler.UploadMaxSize(1073741824)) // 1 GiB (TODO: configurable?) @@ -234,6 +233,7 @@ ctx = config.Context(ctx, server.conf, server.service) ctx = database.Context(ctx, server.db) ctx = redis.Context(ctx, server.redis) ctx = email.Context(ctx, server.email) + ctx = context.WithValue(ctx, serverCtxKey, server) server.queues = append(server.queues, queues...) for _, queue := range queues { diff --git a/webhooks/context.go b/webhooks/context.go index 029b683ce983470533f8f5f50046b211852fce23..082f7d2da8efca92934bd638939e7f1c339c0592 100644 --- a/webhooks/context.go +++ b/webhooks/context.go @@ -7,6 +7,7 @@ "encoding/json" "errors" "fmt" + "github.com/99designs/gqlgen/complexity" "github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql/executor" "github.com/google/uuid" @@ -64,7 +65,6 @@ // want to communicate this to the user. return nil, err } - // TODO: Set complexity limit exec := executor.New(schema) params := graphql.RawParams{ Query: sub.Query, @@ -79,6 +79,15 @@ if errors != nil { panic(errors) } rc.RecoverFunc = server.EmailRecover + + op := rc.Doc.Operations.ForName(rc.OperationName) + complexity := complexity.Calculate(schema, op, rc.Variables) + srv := server.ForContext(ctx) + if complexity > srv.MaxComplexity { + // TODO: This doesn't bubble up to the user well + return nil, fmt.Errorf("operation has complexity %d, which exceeds the maximum of %d", + complexity, srv.MaxComplexity) + } var resp graphql.ResponseHandler ctx = graphql.WithOperationContext(ctx, rc)