From e95cc70d66327228c011efe3df6f4651e0e93c1a Mon Sep 17 00:00:00 2001 From: Daniel Rearden Date: Mon, 1 Jun 2020 15:55:52 -0400 Subject: [PATCH] Add ProhibitIntrospectionQueriesRule --- .vscode/settings.json | 3 + .../ProhibitIntrospectionQueriesRule-test.js | 146 ++++++++++++++++++ src/validation/index.js | 3 + .../ProhibitIntrospectionQueriesRule.d.ts | 16 ++ .../ProhibitIntrospectionQueriesRule.js | 41 +++++ 5 files changed, 209 insertions(+) create mode 100644 .vscode/settings.json create mode 100644 src/validation/__tests__/ProhibitIntrospectionQueriesRule-test.js create mode 100644 src/validation/rules/optional/ProhibitIntrospectionQueriesRule.d.ts create mode 100644 src/validation/rules/optional/ProhibitIntrospectionQueriesRule.js diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000000..f9a4c680027 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "javascript.validate.enable": false +} diff --git a/src/validation/__tests__/ProhibitIntrospectionQueriesRule-test.js b/src/validation/__tests__/ProhibitIntrospectionQueriesRule-test.js new file mode 100644 index 00000000000..0b4b7b2a51f --- /dev/null +++ b/src/validation/__tests__/ProhibitIntrospectionQueriesRule-test.js @@ -0,0 +1,146 @@ +// @flow strict + +import { describe, it } from 'mocha'; + +import { ProhibitIntrospectionQueriesRule } from '../rules/optional/ProhibitIntrospectionQueriesRule'; + +import { + GraphQLSchema, + GraphQLObjectType, + GraphQLString, + __Schema, +} from '../../type/index'; + +import { expectValidationErrorsWithSchema } from './harness'; + +const schema = new GraphQLSchema({ + query: new GraphQLObjectType({ + name: 'Query', + fields: { + someQuery: { + type: new GraphQLObjectType({ + name: 'SomeType', + fields: { + someField: { type: GraphQLString }, + introspectionField: { type: __Schema }, + }, + }), + }, + }, + }), +}); + +function expectErrors(queryStr) { + return expectValidationErrorsWithSchema( + schema, + ProhibitIntrospectionQueriesRule, + queryStr, + ); +} + +function expectValid(queryStr) { + expectErrors(queryStr).to.deep.equal([]); +} + +describe('Validate: Prohibit introspection queries', () => { + it('ignores valid fields including __typename', () => { + expectValid(` + { + someQuery { + __typename + someField + } + } + `); + }); + + it('ignores fields not in the schema', () => { + expectValid(` + { + __introspect + } + `); + }); + + it('reports error when a field with introspection type is requested', () => { + expectErrors(` + { + __schema { + queryType { + name + } + } + } + `).to.deep.equal([ + { + locations: [ + { + column: 9, + line: 3, + }, + ], + message: + 'GraphQL introspection has been disabled, but the requested query contained the field "__schema".', + }, + { + locations: [ + { + column: 11, + line: 4, + }, + ], + message: + 'GraphQL introspection has been disabled, but the requested query contained the field "queryType".', + }, + ]); + }); + + it('reports error when a field with an introspection type is requested and aliased', () => { + expectErrors(` + { + s: __schema { + queryType { + name + } + } + } + `).to.deep.equal([ + { + locations: [ + { + column: 9, + line: 3, + }, + ], + message: + 'GraphQL introspection has been disabled, but the requested query contained the field "__schema".', + }, + { + locations: [ + { + column: 11, + line: 4, + }, + ], + message: + 'GraphQL introspection has been disabled, but the requested query contained the field "queryType".', + }, + ]); + }); + + it('reports error for non-standard introspection fields', () => { + expectErrors(` + { + someQuery { + introspectionField + } + } + `).to.deep.equal([ + { + message: + 'GraphQL introspection has been disabled, but the requested query contained the field "introspectionField".', + locations: [{ line: 4, column: 11 }], + }, + ]); + }); +}); diff --git a/src/validation/index.js b/src/validation/index.js index 906b31e43c2..bdbaf117708 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 { ProhibitIntrospectionQueriesRule } from './rules/optional/ProhibitIntrospectionQueriesRule'; diff --git a/src/validation/rules/optional/ProhibitIntrospectionQueriesRule.d.ts b/src/validation/rules/optional/ProhibitIntrospectionQueriesRule.d.ts new file mode 100644 index 00000000000..2ed98eed7c7 --- /dev/null +++ b/src/validation/rules/optional/ProhibitIntrospectionQueriesRule.d.ts @@ -0,0 +1,16 @@ +import { ASTVisitor } from '../../../language/visitor'; +import { ValidationContext } from '../../ValidationContext'; + +/** + * Prohibit introspection queries + * + * A GraphQL document is only valid if all fields selected are not fields that + * return an introspection type. + * + * Note: This rule is optional and is not part of the Validation section of the + * GraphQL Specification. This rule effectively disables introspection, which + * does not reflect best practices and should only be done if absolutely necessary. + */ +export function ProhibitIntrospectionQueriesRule( + context: ValidationContext, +): ASTVisitor; diff --git a/src/validation/rules/optional/ProhibitIntrospectionQueriesRule.js b/src/validation/rules/optional/ProhibitIntrospectionQueriesRule.js new file mode 100644 index 00000000000..3dd4c73859c --- /dev/null +++ b/src/validation/rules/optional/ProhibitIntrospectionQueriesRule.js @@ -0,0 +1,41 @@ +// @flow strict + +import { GraphQLError } from '../../../error/GraphQLError'; + +import { type FieldNode } from '../../../language/ast'; +import { type ASTVisitor } from '../../../language/visitor'; + +import { type ValidationContext } from '../../ValidationContext'; + +import { getNamedType } from '../../../type/definition'; +import { isIntrospectionType } from '../../../type/introspection'; + +/** + * Prohibit introspection queries + * + * A GraphQL document is only valid if all fields selected are not fields that + * return an introspection type. + * + * Note: This rule is optional and is not part of the Validation section of the + * GraphQL Specification. This rule effectively disables introspection, which + * does not reflect best practices and should only be done if absolutely necessary. + */ +export function ProhibitIntrospectionQueriesRule( + context: ValidationContext, +): ASTVisitor { + return { + Field(node: FieldNode) { + const fieldType = context.getType(); + if (fieldType) { + if (isIntrospectionType(getNamedType(fieldType))) { + context.reportError( + new GraphQLError( + `GraphQL introspection has been disabled, but the requested query contained the field "${node.name.value}".`, + node, + ), + ); + } + } + }, + }; +}