From c5817f21eb30a8e4a19070a9a67f65784158c622 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Mon, 3 Jan 2022 17:09:40 +0100 Subject: [PATCH] Add property for disabling GraphQL schema introspection Prior to this commit, the GraphQL schema assembled by the auto-configuration would provide no option for disabling the field introspection. While this feature is essential for many tools (including GraphiQL), some prefer disabling it because this allows clients to gather information about types and schema easily. This commit introduces a new `spring.graphql.schema.introspection.enabled` configuration property. Because potential attackers can still gather this information and this feature is a core concern in the GraphQL spec, introspection is enabled by default for Spring Boot applications. Closes gh-29248 --- .../graphql/GraphQlAutoConfiguration.java | 5 ++++ .../graphql/GraphQlProperties.java | 23 +++++++++++++++++++ .../GraphQlAutoConfigurationTests.java | 21 +++++++++++++++++ .../src/docs/asciidoc/web/spring-graphql.adoc | 2 ++ 4 files changed, 51 insertions(+) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/GraphQlAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/GraphQlAutoConfiguration.java index 6b2327d2d0a..5cb3f77b0bf 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/GraphQlAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/GraphQlAutoConfiguration.java @@ -24,6 +24,7 @@ import java.util.stream.Collectors; import graphql.GraphQL; import graphql.execution.instrumentation.Instrumentation; +import graphql.schema.visibility.NoIntrospectionGraphqlFieldVisibility; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -77,6 +78,10 @@ public class GraphQlAutoConfiguration { .schemaResources(schemaResources.toArray(new Resource[0])) .exceptionResolvers(exceptionResolversProvider.orderedStream().collect(Collectors.toList())) .instrumentation(instrumentationsProvider.orderedStream().collect(Collectors.toList())); + if (!properties.getSchema().getIntrospection().isEnabled()) { + builder.configureRuntimeWiring((wiring) -> wiring + .fieldVisibility(NoIntrospectionGraphqlFieldVisibility.NO_INTROSPECTION_FIELD_VISIBILITY)); + } wiringConfigurers.orderedStream().forEach(builder::configureRuntimeWiring); sourceCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); try { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/GraphQlProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/GraphQlProperties.java index 3df070e7458..02df4a30958 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/GraphQlProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/GraphQlProperties.java @@ -73,6 +73,8 @@ public class GraphQlProperties { */ private String[] fileExtensions = new String[] { ".graphqls", ".gqls" }; + private final Introspection introspection = new Introspection(); + private final Printer printer = new Printer(); public String[] getLocations() { @@ -96,10 +98,31 @@ public class GraphQlProperties { .toArray(String[]::new); } + public Introspection getIntrospection() { + return this.introspection; + } + public Printer getPrinter() { return this.printer; } + public static class Introspection { + + /** + * Whether field introspection should be enabled at the schema level. + */ + private boolean enabled = true; + + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + } + public static class Printer { /** diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/GraphQlAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/GraphQlAutoConfigurationTests.java index 5f1f388f9a1..e769425c764 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/GraphQlAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/GraphQlAutoConfigurationTests.java @@ -21,6 +21,8 @@ import graphql.execution.instrumentation.ChainedInstrumentation; import graphql.execution.instrumentation.Instrumentation; import graphql.schema.GraphQLSchema; import graphql.schema.idl.RuntimeWiring; +import graphql.schema.visibility.DefaultGraphqlFieldVisibility; +import graphql.schema.visibility.NoIntrospectionGraphqlFieldVisibility; import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.Test; @@ -145,6 +147,25 @@ class GraphQlAutoConfigurationTests { }); } + @Test + void fieldIntrospectionShouldBeEnabledByDefault() { + this.contextRunner.run((context) -> { + GraphQlSource graphQlSource = context.getBean(GraphQlSource.class); + GraphQLSchema schema = graphQlSource.schema(); + assertThat(schema.getCodeRegistry().getFieldVisibility()).isInstanceOf(DefaultGraphqlFieldVisibility.class); + }); + } + + @Test + void shouldDisableFieldIntrospection() { + this.contextRunner.withPropertyValues("spring.graphql.schema.introspection.enabled:false").run((context) -> { + GraphQlSource graphQlSource = context.getBean(GraphQlSource.class); + GraphQLSchema schema = graphQlSource.schema(); + assertThat(schema.getCodeRegistry().getFieldVisibility()) + .isInstanceOf(NoIntrospectionGraphqlFieldVisibility.class); + }); + } + @Configuration(proxyBeanMethods = false) static class CustomGraphQlBuilderConfiguration { diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/spring-graphql.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/spring-graphql.adoc index 66ea980ac8a..e197774c326 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/spring-graphql.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/spring-graphql.adoc @@ -40,6 +40,8 @@ In the following sections, we'll consider this sample GraphQL schema, defining t include::{docs-resources}/graphql/schema.graphqls[] ---- +NOTE: By default, https://spec.graphql.org/draft/#sec-Introspection[field introspection] will be allowed on the schema as it is required for tools such as GraphiQL. +If you wish to not expose information about the schema, you can disable introspection by setting configprop:spring.graphql.schema.introspection.enabled[] to `false`. [[web.graphql.runtimewiring]] === GraphQL RuntimeWiring