add tests and docs for disable introspection rule

This commit is contained in:
Aryan Iyappan 2021-08-14 08:41:24 +05:30
parent ec982ac50b
commit 4e32dac251
6 changed files with 73 additions and 29 deletions

View File

@ -16,7 +16,7 @@ queries. It takes in the following arguments.
- ``ignore`` Stops recursive depth checking based on a field name. Either a string or regexp to match the name, or a function that returns a boolean
- ``callback`` Called each time validation runs. Receives an Object which is a map of the depths for each operation.
Example
Usage
-------
Here is how you would implement depth-limiting on your schema.
@ -33,7 +33,7 @@ Here is how you would implement depth-limiting on your schema.
schema = Schema(query=MyQuery)
# Queries which have a depth more than 20
# queries which have a depth more than 20
# will not be executed.
validation_errors = validate(
@ -47,6 +47,39 @@ Here is how you would implement depth-limiting on your schema.
)
Disable Introspection
---------------------
the disable introspection validation rule ensures that your schema cannot be introspected.
This is a useful security measure in production environments.
Usage
-------
Here is how you would disable introspection for your schema.
.. code:: python
from graphql import validate, parse
from graphene import ObjectType, Schema, String
from graphene.validation import DisableIntrospection
class MyQuery(ObjectType):
name = String(required=True)
schema = Schema(query=MyQuery)
# introspection queries will not be executed.
validation_errors = validate(
schema=schema,
document_ast=parse('THE QUERY'),
rules=(
DisableIntrospection,
)
)
Implementing custom validators
------------------------------
All custom query validators should extend the `ValidationRule <https://github.com/graphql-python/graphql-core/blob/v3.0.5/src/graphql/validation/rules/__init__.py#L37>`_
@ -56,7 +89,7 @@ perform validation, your validator class should define one or more of enter_* an
enter/leave items as well as details on function documentation, please see contents of the visitor module. To make
validation fail, you should call validator's report_error method with the instance of GraphQLError describing failure
reason. Here is an example query validator that visits field definitions in GraphQL query and fails query validation
if any of those fields are blacklisted fields:
if any of those fields are blacklisted:
.. code:: python
from graphql import GraphQLError
@ -70,7 +103,7 @@ if any of those fields are blacklisted fields:
def is_blacklisted_field(field_name: str):
return key.lower() in my_blacklist
return field_name.lower() in my_blacklist
class BlackListRule(ValidationRule):

View File

@ -1,8 +1,8 @@
from .depth_limit import depth_limit_validator
from .disable_introspection import disable_introspection
from .disable_introspection import DisableIntrospection
__all__ = [
"depth_limit_validator",
"disable_introspection"
"DisableIntrospection",
"depth_limit_validator"
]

View File

@ -116,7 +116,7 @@ def determine_depth(
if depth_so_far > max_depth:
context.report_error(
GraphQLError(
f"'{operation_name}' exceeds maximum operation depth of {max_depth}",
f"'{operation_name}' exceeds maximum operation depth of {max_depth}.",
[node],
)
)
@ -172,7 +172,7 @@ def determine_depth(
)
)
else:
raise Exception(f"Depth crawler cannot handle: {node.kind}") # pragma: no cover
raise Exception(f"Depth crawler cannot handle: {node.kind}.") # pragma: no cover
def is_ignored(node: FieldNode, ignore: Optional[List[IgnoreType]] = None) -> bool:
@ -191,6 +191,6 @@ def is_ignored(node: FieldNode, ignore: Optional[List[IgnoreType]] = None) -> bo
if rule(field_name):
return True
else:
raise ValueError(f"Invalid ignore option: {rule}")
raise ValueError(f"Invalid ignore option: {rule}.")
return False

View File

@ -5,18 +5,15 @@ from graphql.validation import ValidationRule
from ..utils.is_introspection_key import is_introspection_key
def disable_introspection():
class DisableIntrospection(ValidationRule):
def enter_field(self, node: FieldNode, *_args):
field_name = node.name.value
if not is_introspection_key(field_name):
return
class DisableIntrospection(ValidationRule):
def enter_field(self, node: FieldNode, *_args):
field_name = node.name.value
if not is_introspection_key(field_name):
return
self.report_error(
GraphQLError(
f"Cannot query '{field_name}': introspection is disabled.",
node,
)
self.report_error(
GraphQLError(
f"Cannot query '{field_name}': introspection is disabled.",
node,
)
return DisableIntrospection
)

View File

@ -235,7 +235,7 @@ def test_should_catch_very_deep_query():
errors, result = run_query(query, 4)
assert len(errors) == 1
assert errors[0].message == "'anonymous' exceeds maximum operation depth of 4"
assert errors[0].message == "'anonymous' exceeds maximum operation depth of 4."
def test_should_ignore_field():

View File

@ -1,7 +1,7 @@
from graphql import parse, validate
from ...types import Schema, ObjectType, String
from ..disable_introspection import disable_introspection
from ..disable_introspection import DisableIntrospection
class Query(ObjectType):
@ -9,6 +9,10 @@ class Query(ObjectType):
required=True
)
@staticmethod
def resolve_name(root, info):
return "Hello world!"
schema = Schema(query=Query)
@ -16,14 +20,24 @@ schema = Schema(query=Query)
def run_query(query: str):
document = parse(query)
result = None
errors = validate(
schema=schema.graphql_schema,
document_ast=document,
rules=(
disable_introspection(),
DisableIntrospection,
),
)
return errors, result
return errors
def test_disallows_introspection_queries():
errors = run_query("{ __schema { queryType { name } } }")
assert len(errors) == 1
assert errors[0].message == "Cannot query '__schema': introspection is disabled."
def test_allows_non_introspection_queries():
errors = run_query("{ name }")
assert len(errors) == 0