fix(graphql): disable introspection endpoint on production#2098
fix(graphql): disable introspection endpoint on production#2098derrabauke wants to merge 4 commits intomainfrom
Conversation
caluma/schema.py
Outdated
| if settings.DISABLE_INTROSPECTION: | ||
| validate = partial(validate, rules=(DisableIntrospection,)) | ||
|
|
||
| validation_errors = validate( | ||
| schema=schema.graphql_schema, | ||
| ) |
There was a problem hiding this comment.
That's not quite the right place to run the validation. This will run at startup, but you'll need to validate the actual GQL query coming in, not just the schema
The validate() function (sadly, not a method) used is in graphql.validation.validate, and uses a default list of rules from here: graphql.validation.specified_rules.specified_rules. Unfortunately there doesn't appear to be a "right way" to add more rules.
But - even though I don't think it's meant to do that way, if we'd extend that list by adding the DisableIntrospection rule, we'd probably get the desired effect.
Something like:
| if settings.DISABLE_INTROSPECTION: | |
| validate = partial(validate, rules=(DisableIntrospection,)) | |
| validation_errors = validate( | |
| schema=schema.graphql_schema, | |
| ) | |
| if settings.DISABLE_INTROSPECTION: | |
| from graphql.validation.specified_rules import specified_rules | |
| specified_rules.append(DisableIntrospection) |
Sadly I don't see any better way right now to inject the rule as needed - feel free to dig deeper :-)
There was a problem hiding this comment.
BTW the call traces along these lines:
caluma.caluma_user.views.AuthenticationGraphQLView.dispatch()graphene_django.views.GraphQLView.dispatch()graphene_django.views.GraphQLView.get_response()graphene_django.views.GraphQLView.execute_graphql_request()graphql.validation.validate.validate()<-- this could accept the rules param, but it's not passed, so the default noted in the previous comment is used
There was a problem hiding this comment.
Thanks for the review! My approach was a mix of this blobpost and the graphene doc
Otherwise we could implement it in the View by overwriting execute_graphql_request?!
There was a problem hiding this comment.
But your proposal of extending graphql.validation.specified_rules looks also good!
There was a problem hiding this comment.
Thanks for the review! My approach was a mix of this blobpost and the graphene doc
The first example is just showing the calling conventions of the validate() function.
Otherwise we could implement it in the View by overwriting
execute_graphql_request?!
Indeed, the "correct" way would be to overload execute_graphql_request(), but that contains a ton of code that we'd need to duplicate, just so we could then insert the correct call to validate(). Seems excessive and problematic, as we'd need to keep track of the upstream implementation. Thus my suggestion of extending the specified_rules.
After thinking about it some more, I think since this is only used for development mode, it's probably not too bad to mess with specified_rules - if the implementation changes, we'd notice relatively quickly, which might not be the case in the other variant.
There was a problem hiding this comment.
Oh, also note the way the blog post calls validate(), they're replacing the whole rules parameter with just one rule - so all the specified_rules get disabled, which may allow malformed requests to penetrate much deeper into the code, possibly opening up a ton of security problems. Iff we go that route, we should at least do rules=specified_rules+[DisableIntrospection]
There was a problem hiding this comment.
There seems to be an easier way in the making
There was a problem hiding this comment.
graphene-django 3.2.0 is out now so we can utilize the latest feature to do the job. 🎉
There was a problem hiding this comment.
Couldn't get it running locally with the new version. Could we have a small timed-boxed session @winged to hack this together?
0b39aca to
2879aa8
Compare
disable introspection via validation rules property
2879aa8 to
e9f837b
Compare
Interface types, such as `Question`, `Answer`, and `Task` have subclasses, and connections using those types may return a mix of specific object types. Examples are form -> questions, which may return different question types. For these interface types, the `graphene` internals require access to a `FooType.Meta._meta.registry` property, to access the type registry. Somehow, the graphene metaclass system does not automatically build this up correctly, so we have to help a little bit to make it work
No description provided.