From 0e3e4c27b221adf003ec70eb900e4d51806a9ce8 Mon Sep 17 00:00:00 2001 From: Ben Kraft Date: Mon, 6 Jun 2022 16:46:31 -0700 Subject: [PATCH] Validate that the operation's root type is valid (#225) Turns out neither we (nor anyone else) were validating this, because the spec didn't say explicitly to validate it. So you could do `mutation { bogus }` even if the schema has no mutation types, or worse, any syntactically valid query if the schema is totally empty. In graphql/graphql-spec#955 I'm adding it to the spec, and in graphql/graphql-js#3592 someone else is adding it to graphql-js. So we should too, once those land! At that point we should also likely reimport the relevant tests, instead of the ones I wrote here, but I figured I'd put the PR up now so folks can review the non-test parts if you like. Fixes #221. --- validator/imported/spec/schemas.yml | 1 + validator/rules/known_root_type.go | 35 +++++++++++++++++++++++ validator/spec/KnownRootTypeRule.spec.yml | 19 ++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 validator/rules/known_root_type.go create mode 100644 validator/spec/KnownRootTypeRule.spec.yml diff --git a/validator/imported/spec/schemas.yml b/validator/imported/spec/schemas.yml index e7db1f3c..5eeafed3 100644 --- a/validator/imported/spec/schemas.yml +++ b/validator/imported/spec/schemas.yml @@ -487,3 +487,4 @@ } scalar Any +- "" diff --git a/validator/rules/known_root_type.go b/validator/rules/known_root_type.go new file mode 100644 index 00000000..4c60c8d8 --- /dev/null +++ b/validator/rules/known_root_type.go @@ -0,0 +1,35 @@ +package validator + +import ( + "fmt" + + "github.com/vektah/gqlparser/v2/ast" + . "github.com/vektah/gqlparser/v2/validator" +) + +func init() { + AddRule("KnownRootType", func(observers *Events, addError AddErrFunc) { + // A query's root must be a valid type. Surprisingly, this isn't + // checked anywhere else! + observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) { + var def *ast.Definition + switch operation.Operation { + case ast.Query, "": + def = walker.Schema.Query + case ast.Mutation: + def = walker.Schema.Mutation + case ast.Subscription: + def = walker.Schema.Subscription + default: + // This shouldn't even parse; if it did we probably need to + // update this switch block to add the new operation type. + panic(fmt.Sprintf(`got unknown operation type "%s"`, operation.Operation)) + } + if def == nil { + addError( + Message(`Schema does not support operation type "%s"`, operation.Operation), + At(operation.Position)) + } + }) + }) +} diff --git a/validator/spec/KnownRootTypeRule.spec.yml b/validator/spec/KnownRootTypeRule.spec.yml new file mode 100644 index 00000000..651e6582 --- /dev/null +++ b/validator/spec/KnownRootTypeRule.spec.yml @@ -0,0 +1,19 @@ +- name: Known root type + rule: KnownRootType + schema: 0 + query: | + query { dog { name } } +- name: Valid root type but not in schema + rule: KnownRootType + schema: 0 + query: | + mutation { dog { name } } + errors: + - message: Schema does not support operation type "mutation" +- name: Valid root type but schema is entirely empty + rule: KnownRootType + schema: 20 + query: | + { dog { name } } + errors: + - message: Schema does not support operation type "query"