Skip to content

Commit

Permalink
Add ProhibitIntrospectionQueriesRule
Browse files Browse the repository at this point in the history
  • Loading branch information
danielrearden committed Jun 3, 2020
1 parent 4f35752 commit 9bdde6f
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 0 deletions.
153 changes: 153 additions & 0 deletions src/validation/__tests__/ProhibitIntrospectionQueriesRule-test.js
@@ -0,0 +1,153 @@
// @flow strict

import { describe, it } from 'mocha';

import {
GraphQLSchema,
GraphQLObjectType,
GraphQLString,
__EnumValue,
} from '../../type/index';

import { ProhibitIntrospectionQueriesRule } from '../rules/optional/ProhibitIntrospectionQueriesRule';

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: __EnumValue },
},
}),
},
},
}),
});

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 an introspection type 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 }],
},
{
message:
'GraphQL introspection has been disabled, but the requested query contained the field "queryType".',
locations: [{ line: 4, column: 11 }],
},
]);
});

it('reports error when a field with an introspection type 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 }],
},
{
message:
'GraphQL introspection has been disabled, but the requested query contained the field "queryType".',
locations: [{ line: 4, column: 11 }],
},
]);
});

it('reports error when using a fragment with a field with an introspection type', () => {
expectErrors(`
{
...QueryFragment
}
fragment QueryFragment on Query {
__schema {
queryType {
name
}
}
}
`).to.deep.equal([
{
message:
'GraphQL introspection has been disabled, but the requested query contained the field "__schema".',
locations: [{ line: 7, column: 9 }],
},
{
message:
'GraphQL introspection has been disabled, but the requested query contained the field "queryType".',
locations: [{ line: 8, column: 11 }],
},
]);
});

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 }],
},
]);
});
});
3 changes: 3 additions & 0 deletions src/validation/index.d.ts
Expand Up @@ -90,3 +90,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';
3 changes: 3 additions & 0 deletions src/validation/index.js
Expand Up @@ -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';
@@ -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;
39 changes: 39 additions & 0 deletions src/validation/rules/optional/ProhibitIntrospectionQueriesRule.js
@@ -0,0 +1,39 @@
// @flow strict

import { GraphQLError } from '../../../error/GraphQLError';

import { type FieldNode } from '../../../language/ast';
import { type ASTVisitor } from '../../../language/visitor';

import { getNamedType } from '../../../type/definition';
import { isIntrospectionType } from '../../../type/introspection';

import { type 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 {
return {
Field(node: FieldNode) {
const type = getNamedType(context.getType());
if (type && isIntrospectionType(type)) {
context.reportError(
new GraphQLError(
`GraphQL introspection has been disabled, but the requested query contained the field "${node.name.value}".`,
node,
),
);
}
},
};
}

0 comments on commit 9bdde6f

Please sign in to comment.