From 78ce9cf3fa2a20e845d2d4e75bbd81515e96d0d8 Mon Sep 17 00:00:00 2001 From: Daniel Rearden Date: Mon, 1 Jun 2020 15:55:52 -0400 Subject: [PATCH] Add NoIntrospectionFieldsRule --- .../NoIntrospectionFieldsRule-test.js | 100 ++++++++++++++++++ src/validation/index.js | 3 + .../rules/NoIntrospectionFieldsRule.d.ts | 13 +++ .../rules/NoIntrospectionFieldsRule.js | 35 ++++++ 4 files changed, 151 insertions(+) create mode 100644 src/validation/__tests__/NoIntrospectionFieldsRule-test.js create mode 100644 src/validation/rules/NoIntrospectionFieldsRule.d.ts create mode 100644 src/validation/rules/NoIntrospectionFieldsRule.js diff --git a/src/validation/__tests__/NoIntrospectionFieldsRule-test.js b/src/validation/__tests__/NoIntrospectionFieldsRule-test.js new file mode 100644 index 00000000000..b66bd0b8a3a --- /dev/null +++ b/src/validation/__tests__/NoIntrospectionFieldsRule-test.js @@ -0,0 +1,100 @@ +// @flow strict + +import { describe, it } from 'mocha'; + +import { NoIntrospectionFieldsRule } from '../rules/NoIntrospectionFieldsRule'; + +import { expectValidationErrors } from './harness'; + +function expectErrors(queryStr) { + return expectValidationErrors(NoIntrospectionFieldsRule, queryStr); +} + +function expectValid(queryStr) { + expectErrors(queryStr).to.deep.equal([]); +} + +describe('Validate: No introspection fields', () => { + it('ignores valid fields including __typename', () => { + expectValid(` + { + dog { + __typename + name + } + } + `); + }); + + it('ignores valid fields to be aliased as __schema or __type', () => { + expectValid(` + { + __schema: dog + __type: cat + } + `); + }); + + it('ignores __schema or __type fields not on the root query type', () => { + expectValid(` + { + someField { + __schema + __type + } + } + `); + }); + + it('reports error when __schema field is requested', () => { + expectErrors(` + { + __schema { + queryType { + name + } + } + } + `).to.deep.equal([ + { + message: + 'GraphQL introspection has been disabled, but the requested query contained the field "__schema".', + locations: [{ line: 3, column: 9 }], + }, + ]); + }); + + it('reports error when __type field is requested', () => { + expectErrors(` + { + __type(name: "Query") { + name + } + } + `).to.deep.equal([ + { + message: + 'GraphQL introspection has been disabled, but the requested query contained the field "__type".', + locations: [{ line: 3, column: 9 }], + }, + ]); + }); + + it('reports error when an introspection field is requested and aliased', () => { + expectErrors(` + { + s: __schema { + queryType { + name + } + } + } + `).to.deep.equal([ + { + message: + 'GraphQL introspection has been disabled, but the requested query contained the field "__schema".', + locations: [{ line: 3, column: 9 }], + }, + ]); + }); +}); diff --git a/src/validation/index.js b/src/validation/index.js index 906b31e43c2..c8114c92ebd 100644 --- a/src/validation/index.js +++ b/src/validation/index.js @@ -94,3 +94,6 @@ export { UniqueEnumValueNamesRule } from './rules/UniqueEnumValueNamesRule'; export { UniqueFieldDefinitionNamesRule } from './rules/UniqueFieldDefinitionNamesRule'; export { UniqueDirectiveNamesRule } from './rules/UniqueDirectiveNamesRule'; export { PossibleTypeExtensionsRule } from './rules/PossibleTypeExtensionsRule'; + +// Optional rules not defined by the GraphQL Specification +export { NoIntrospectionFieldsRule } from './rules/NoIntrospectionFieldsRule'; diff --git a/src/validation/rules/NoIntrospectionFieldsRule.d.ts b/src/validation/rules/NoIntrospectionFieldsRule.d.ts new file mode 100644 index 00000000000..7df27be8cef --- /dev/null +++ b/src/validation/rules/NoIntrospectionFieldsRule.d.ts @@ -0,0 +1,13 @@ +import { ASTVisitor } from '../../language/visitor'; +import { ValidationContext } from '../ValidationContext'; + +/** + * No introspection fields + * + * A GraphQL document is only valid if all fields selected are not + * fields that return an introspection type. Note: This rule is not + * part of the Validation section of the GraphQL Specification. + */ +export function NoIntrospectionFieldsRule( + context: ValidationContext, +): ASTVisitor; diff --git a/src/validation/rules/NoIntrospectionFieldsRule.js b/src/validation/rules/NoIntrospectionFieldsRule.js new file mode 100644 index 00000000000..7c425bc903a --- /dev/null +++ b/src/validation/rules/NoIntrospectionFieldsRule.js @@ -0,0 +1,35 @@ +// @flow strict + +import { GraphQLError } from '../../error/GraphQLError'; + +import { type FieldNode } from '../../language/ast'; +import { type ASTVisitor } from '../../language/visitor'; + +import { type ValidationContext } from '../ValidationContext'; + +/** + * No introspection fields + * + * A GraphQL document is only valid if all fields selected are not + * fields that return an introspection type. Note: This rule is not + * part of the Validation section of the GraphQL Specification. + */ +export function NoIntrospectionFieldsRule( + context: ValidationContext, +): ASTVisitor { + return { + Field(node: FieldNode) { + if ( + context.getSchema().getQueryType() === context.getParentType() && + (node.name.value === '__schema' || node.name.value === '__type') + ) { + context.reportError( + new GraphQLError( + `GraphQL introspection has been disabled, but the requested query contained the field "${node.name.value}".`, + node, + ), + ); + } + }, + }; +}