Skip to content

Commit 4290500

Browse files
committed
validation: restrict maximum number of errors to 100 by default
Replicates graphql/graphql-js@cb48918
1 parent 9f2b23c commit 4290500

File tree

2 files changed

+25
-3
lines changed

2 files changed

+25
-3
lines changed

src/graphql/validation/validate.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,20 @@ def validate(
3535
a ValidationContext (see the language/visitor API). Visitor methods are expected to
3636
return GraphQLErrors, or lists of GraphQLErrors when invalid.
3737
38+
Validate will stop validation after a ``max_errors`` limit has been reached.
39+
Attackers can send pathologically invalid queries to induce a DoS attack,
40+
so by default ``max_errors`` set to 100 errors.
41+
3842
Providing a custom TypeInfo instance is deprecated and will be removed in v3.3.
3943
"""
4044
if not document_ast or not isinstance(document_ast, DocumentNode):
4145
raise TypeError("Must provide document.")
4246
# If the schema used for validation is invalid, throw an error.
4347
assert_valid_schema(schema)
48+
if max_errors is None:
49+
max_errors = 100
50+
elif not isinstance(max_errors, int):
51+
raise TypeError("The maximum number of errors must be passed as an int.")
4452
if type_info is None:
4553
type_info = TypeInfo(schema)
4654
elif not isinstance(type_info, TypeInfo):
@@ -53,13 +61,11 @@ def validate(
5361
raise TypeError(
5462
"Rules must be specified as a collection of ASTValidationRule subclasses."
5563
)
56-
if max_errors is not None and not isinstance(max_errors, int):
57-
raise TypeError("The maximum number of errors must be passed as an int.")
5864

5965
errors: List[GraphQLError] = []
6066

6167
def on_error(error: GraphQLError) -> None:
62-
if max_errors is not None and len(errors) >= max_errors:
68+
if len(errors) >= max_errors: # type: ignore
6369
errors.append(
6470
GraphQLError(
6571
"Too many validation errors, error limit reached."

tests/validation/test_validation.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,22 @@ def when_max_errors_is_less_than_number_of_errors():
180180
},
181181
]
182182

183+
def limits_to_100_when_max_errors_is_not_passed():
184+
errors = validate(
185+
test_schema,
186+
parse(
187+
"{" + " ".join(f"unknownField{n}" for n in range(120)) + "}",
188+
no_location=True,
189+
),
190+
)
191+
assert len(errors) == 101
192+
assert errors[0] == _invalid_field_error("unknownField0")
193+
assert errors[-2] == _invalid_field_error("unknownField99")
194+
assert errors[-1] == {
195+
"message": "Too many validation errors, error limit reached."
196+
" Validation aborted."
197+
}
198+
183199
def pass_through_exceptions_from_rules():
184200
class CustomRule(ValidationRule):
185201
def enter_field(self, *_args):

0 commit comments

Comments
 (0)