Protects graphene, graphql or strawberry against malicious queries
Project description
What does this project solve?
It provides protection against malicious grapqhl requests (resource exhaustion). Despite its name it can be used with graphql (pure), graphene, strawberry. It is implemented via a custom ValidationRule, supports error reporting and early bail out strategies as well as limits for single fields
Installation
pip install graphene-protector
Integration
Django
This adds to django the following setting:
- GRAPHENE_PROTECTOR_DEPTH_LIMIT: max depth
- GRAPHENE_PROTECTOR_SELECTIONS_LIMIT: max selections
- GRAPHENE_PROTECTOR_COMPLEXITY_LIMIT: max (depth * selections)
- GRAPHENE_PROTECTOR_PATH_INGORE_PATTERN: ignore fields in calculation (but still traverse them)
Integrate with:
graphene:
# schema.py
# replace normal Schema import with:
from graphene_protector.django.graphene import Schema
schema = Schema(query=Query, mutation=Mutation)
and add in django settings to GRAPHENE
GRAPHENE = {
...
"SCHEMA": "path.to.schema",
}
or strawberry:
# schema.py
# replace normal Schema import with:
from graphene_protector.django.strawberry import Schema
schema = Schema(query=Query, mutation=Mutation)
manual way (note: import from django for including defaults from settings)
from graphene_protector.django.graphene import Schema
# or
# from graphene_protector.django.strawberry import Schema
schema = Schema(query=Query)
result = schema.execute(query_string)
manual way with custom default Limits
from graphene_protector import Limits
from graphene_protector.django.graphene import Schema
# or
# from graphene_protector.django.strawberry import Schema
schema = graphene.Schema(query=Query, limits=Limits(complexity=None))
result = schema.execute(
query_string
)
Graphene & Strawberry
limits keyword with Limits object is supported.
from graphene_protector import Limits
from graphene_protector.graphene import Schema
# or
# from graphene_protector.strawberry import Schema
schema = Schema(query=Query, limits=Limits(depth=20, selections=None, complexity=100))
result = schema.execute(query_string)
pure graphql
from graphene_protector import LimitsValidationRule
from graphql.type.schema import Schema
schema = Schema(
query=Query,
)
query_ast = parse("{ hello }")
self.assertFalse(validate(schema, query_ast, [LimitsValidationRule]))
or with custom defaults
from graphene_protector import Limits, LimitsValidationRule
from graphql.type.schema import Schema
class CustomLimitsValidationRule(LimitsValidationRule):
default_limits = Limits(depth=20, selections=None, complexity=100)
schema = Schema(
query=Query,
)
query_ast = parse("{ hello }")
self.assertFalse(validate(schema, query_ast, [LimitsValidationRule]))
strawberry extension variant
from graphene_protector import Limits
from graphene_protector.strawberry import CustomGrapheneProtector
from strawberry import Schema
schema = Schema(query=Query, extensions=[CustomGrapheneProtector(Limits(depth=20, selections=None, complexity=100))])
result = schema.execute(query_string)
or with custom defaults via Mixin
from graphene_protector import Limits, SchemaMixin, LimitsValidationRule
from graphql.type.schema import Schema
class CustomSchema(SchemaMixin, Schema):
protector_default_limits = Limits(depth=20, selections=None, complexity=100)
schema = CustomSchema(
query=Query,
)
query_ast = parse("{ hello }")
self.assertFalse(validate(schema, query_ast, [LimitsValidationRule]))
strawberry variant with mixin
from graphene_protector import Limits, SchemaMixin, default_path_ignore_pattern
from strawberry import Schema
class CustomSchema(SchemaMixin, Schema):
protector_default_limits = Limits(depth=20, selections=None, complexity=100)
protector_path_ignore_pattern = default_path_ignore_pattern
schema = CustomSchema(query=Query)
result = schema.execute(query_string)
Note: for the mixin method all variables are prefixed in schema with protector_
. Internally the get_protector_
methods are used and mapped on the validation context
Limits
A Limits object has following attributes:
- depth: max depth (default: 20, None disables feature)
- selections: max selections (default: None, None disables feature)
- complexity: max (depth subtree * selections subtree) (default: 100, None disables feature)
they overwrite django settings if specified.
decorating single fields
Sometimes single fields should have different limits:
person1 = Limits(depth=10)(graphene.Field(Person))
Limits are inherited for unspecified parameters
one-time disable limit checks
to disable checks for one operation use check_limits=False (works for: execute, execute_async (if available), subscribe (if available)):
from graphene_protector import Limits
from graphene_protector.graphene import Schema
schema = Schema(query=Query, limits=Limits(depth=20, selections=None, complexity=100))
result = schema.execute(query_string, check_limits=False)
Path ignoring
This is a feature for ignoring some path parts in calculation but still traversing them.
It is useful for e.g. relay which inflates the depth significant and can cause problems with complexity
Currently it is set to edges/node$
which reduces the depth of connections by one
Other examples are:
node$|id$
for ignoring id fields in selection/complexity count and reducing the depth by 1 when seeing a node fieldpage_info|pageInfo
for ignoring page info in calculation (Note: you need only one)
Development
I am open for new ideas. If you want some new or better algorithms integrated just make a PR
related projects:
- secure-graphene: lacks django integration, some features and has a not so easy findable name. But I accept: it is the "not invented here"-syndrome
TODO
- test path_ignore_pattern
- keep an eye on the performance impact of the new path regex checking
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file graphene_protector-0.10.0.tar.gz
.
File metadata
- Download URL: graphene_protector-0.10.0.tar.gz
- Upload date:
- Size: 11.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.3.2 CPython/3.10.9 Linux/6.1.7-arch1-1
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 00d8a284f18e483606f4d62f762447d244da6530f22f8252488f2fa3ab0cb4e3 |
|
MD5 | b069efbf39f9d7180762e50ff7d99377 |
|
BLAKE2b-256 | ab3479fe36606128b5d5dfa3fbef6055346b548ef1f55953ab5cd930451e5dd4 |
File details
Details for the file graphene_protector-0.10.0-py3-none-any.whl
.
File metadata
- Download URL: graphene_protector-0.10.0-py3-none-any.whl
- Upload date:
- Size: 11.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.3.2 CPython/3.10.9 Linux/6.1.7-arch1-1
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | f3f773277b6f4e3d3b80758570470728b1aeb166b51a90b939e52576e6a0188c |
|
MD5 | e8f570c6b70dc6f0411c32be9211a29c |
|
BLAKE2b-256 | 2cbf3d3a46eb74a23b0ccbfe16c630fca6740dfc9a6ee11808720831c63f2fce |