Skip to content

Commit 4e32dac

Browse files
committed
add tests and docs for disable introspection rule
1 parent ec982ac commit 4e32dac

File tree

6 files changed

+73
-29
lines changed

6 files changed

+73
-29
lines changed

docs/execution/queryvalidation.rst

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ queries. It takes in the following arguments.
1616
- ``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
1717
- ``callback`` Called each time validation runs. Receives an Object which is a map of the depths for each operation.
1818

19-
Example
19+
Usage
2020
-------
2121

2222
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.
3333
3434
schema = Schema(query=MyQuery)
3535
36-
# Queries which have a depth more than 20
36+
# queries which have a depth more than 20
3737
# will not be executed.
3838
3939
validation_errors = validate(
@@ -47,6 +47,39 @@ Here is how you would implement depth-limiting on your schema.
4747
)
4848
4949
50+
Disable Introspection
51+
---------------------
52+
the disable introspection validation rule ensures that your schema cannot be introspected.
53+
This is a useful security measure in production environments.
54+
55+
Usage
56+
-------
57+
58+
Here is how you would disable introspection for your schema.
59+
60+
.. code:: python
61+
from graphql import validate, parse
62+
from graphene import ObjectType, Schema, String
63+
from graphene.validation import DisableIntrospection
64+
65+
66+
class MyQuery(ObjectType):
67+
name = String(required=True)
68+
69+
70+
schema = Schema(query=MyQuery)
71+
72+
# introspection queries will not be executed.
73+
74+
validation_errors = validate(
75+
schema=schema,
76+
document_ast=parse('THE QUERY'),
77+
rules=(
78+
DisableIntrospection,
79+
)
80+
)
81+
82+
5083
Implementing custom validators
5184
------------------------------
5285
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
5689
enter/leave items as well as details on function documentation, please see contents of the visitor module. To make
5790
validation fail, you should call validator's report_error method with the instance of GraphQLError describing failure
5891
reason. Here is an example query validator that visits field definitions in GraphQL query and fails query validation
59-
if any of those fields are blacklisted fields:
92+
if any of those fields are blacklisted:
6093

6194
.. code:: python
6295
from graphql import GraphQLError
@@ -70,7 +103,7 @@ if any of those fields are blacklisted fields:
70103
71104
72105
def is_blacklisted_field(field_name: str):
73-
return key.lower() in my_blacklist
106+
return field_name.lower() in my_blacklist
74107
75108
76109
class BlackListRule(ValidationRule):

graphene/validation/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
from .depth_limit import depth_limit_validator
2-
from .disable_introspection import disable_introspection
2+
from .disable_introspection import DisableIntrospection
33

44

55
__all__ = [
6-
"depth_limit_validator",
7-
"disable_introspection"
6+
"DisableIntrospection",
7+
"depth_limit_validator"
88
]

graphene/validation/depth_limit.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ def determine_depth(
116116
if depth_so_far > max_depth:
117117
context.report_error(
118118
GraphQLError(
119-
f"'{operation_name}' exceeds maximum operation depth of {max_depth}",
119+
f"'{operation_name}' exceeds maximum operation depth of {max_depth}.",
120120
[node],
121121
)
122122
)
@@ -172,7 +172,7 @@ def determine_depth(
172172
)
173173
)
174174
else:
175-
raise Exception(f"Depth crawler cannot handle: {node.kind}") # pragma: no cover
175+
raise Exception(f"Depth crawler cannot handle: {node.kind}.") # pragma: no cover
176176

177177

178178
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
191191
if rule(field_name):
192192
return True
193193
else:
194-
raise ValueError(f"Invalid ignore option: {rule}")
194+
raise ValueError(f"Invalid ignore option: {rule}.")
195195

196196
return False

graphene/validation/disable_introspection.py

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,15 @@
55
from ..utils.is_introspection_key import is_introspection_key
66

77

8-
def disable_introspection():
9-
class DisableIntrospection(ValidationRule):
10-
def enter_field(self, node: FieldNode, *_args):
11-
field_name = node.name.value
12-
if not is_introspection_key(field_name):
13-
return
8+
class DisableIntrospection(ValidationRule):
9+
def enter_field(self, node: FieldNode, *_args):
10+
field_name = node.name.value
11+
if not is_introspection_key(field_name):
12+
return
1413

15-
self.report_error(
16-
GraphQLError(
17-
f"Cannot query '{field_name}': introspection is disabled.",
18-
node,
19-
)
14+
self.report_error(
15+
GraphQLError(
16+
f"Cannot query '{field_name}': introspection is disabled.",
17+
node,
2018
)
21-
22-
return DisableIntrospection
19+
)

graphene/validation/tests/test_depth_limit_validator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ def test_should_catch_very_deep_query():
235235
errors, result = run_query(query, 4)
236236

237237
assert len(errors) == 1
238-
assert errors[0].message == "'anonymous' exceeds maximum operation depth of 4"
238+
assert errors[0].message == "'anonymous' exceeds maximum operation depth of 4."
239239

240240

241241
def test_should_ignore_field():
Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,43 @@
11
from graphql import parse, validate
22

33
from ...types import Schema, ObjectType, String
4-
from ..disable_introspection import disable_introspection
4+
from ..disable_introspection import DisableIntrospection
55

66

77
class Query(ObjectType):
88
name = String(
99
required=True
1010
)
1111

12+
@staticmethod
13+
def resolve_name(root, info):
14+
return "Hello world!"
15+
1216

1317
schema = Schema(query=Query)
1418

1519

1620
def run_query(query: str):
1721
document = parse(query)
1822

19-
result = None
20-
2123
errors = validate(
2224
schema=schema.graphql_schema,
2325
document_ast=document,
2426
rules=(
25-
disable_introspection(),
27+
DisableIntrospection,
2628
),
2729
)
2830

29-
return errors, result
31+
return errors
32+
33+
34+
def test_disallows_introspection_queries():
35+
errors = run_query("{ __schema { queryType { name } } }")
36+
37+
assert len(errors) == 1
38+
assert errors[0].message == "Cannot query '__schema': introspection is disabled."
39+
40+
41+
def test_allows_non_introspection_queries():
42+
errors = run_query("{ name }")
43+
assert len(errors) == 0

0 commit comments

Comments
 (0)