mirror of
https://github.com/graphql-python/graphene-django.git
synced 2025-07-17 11:42:33 +03:00
Add configurable GraphQL error masking with whitelist support
- Introduce GRAPHENE_ERRORS settings for masking error messages in GraphQL responses - Support masking sensitive errors while allowing whitelisted exceptions to show full details - Enable customization of generic error message for better user experience and security
This commit is contained in:
parent
c52cf2b045
commit
1b78a6f638
49
docs/error-masking.rst
Normal file
49
docs/error-masking.rst
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
Custom GraphQL Error Masking
|
||||||
|
============================
|
||||||
|
|
||||||
|
This project includes a custom error formatting function for GraphQL
|
||||||
|
responses that masks sensitive error details from clients.
|
||||||
|
|
||||||
|
Purpose
|
||||||
|
-------
|
||||||
|
|
||||||
|
- Prevent exposing internal error details for security and user experience.
|
||||||
|
- Allow whitelisting of exception classes that should be exposed as-is.
|
||||||
|
- Return a generic error message for all other exceptions.
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
-------------
|
||||||
|
|
||||||
|
You can control the behavior using the ``GRAPHENE_ERRORS`` setting in your
|
||||||
|
Django settings file under the ``GRAPHENE`` namespace:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
GRAPHENE = {
|
||||||
|
"GRAPHENE_ERRORS": {
|
||||||
|
"MASK_EXCEPTIONS": True, # Enable or disable masking
|
||||||
|
"ERROR_MESSAGE": "A custom error message.", # Defaults to "Something went wrong. Please try again later."
|
||||||
|
"WHITELISTED_EXCEPTIONS": [
|
||||||
|
"ValidationError", # Whitelist by class name
|
||||||
|
"django.core.exceptions.ValidationError", # Whitelist by full module path
|
||||||
|
"myapp.custom_exceptions.MyCustomException", # Custom exception whitelist by full path
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior
|
||||||
|
--------
|
||||||
|
|
||||||
|
- If ``MASK_EXCEPTIONS`` is False, all errors are returned fully formatted.
|
||||||
|
- If True, errors not in the whitelist will return only the generic message.
|
||||||
|
- Whitelisted exceptions are returned with full error details.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
The masking is automatically applied to the error responses of GraphQL
|
||||||
|
queries and mutations through a custom error formatter method.
|
||||||
|
|
||||||
|
You can modify or extend the whitelisted exceptions as needed to suit your
|
||||||
|
project's error handling policy.
|
||||||
|
|
|
@ -46,6 +46,7 @@ DEFAULTS = {
|
||||||
"ATOMIC_MUTATIONS": False,
|
"ATOMIC_MUTATIONS": False,
|
||||||
"TESTING_ENDPOINT": "/graphql",
|
"TESTING_ENDPOINT": "/graphql",
|
||||||
"MAX_VALIDATION_ERRORS": None,
|
"MAX_VALIDATION_ERRORS": None,
|
||||||
|
"GRAPHENE_ERRORS": {},
|
||||||
}
|
}
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
|
|
|
@ -228,9 +228,32 @@ class GraphQLView(View):
|
||||||
|
|
||||||
if execution_result.errors:
|
if execution_result.errors:
|
||||||
set_rollback()
|
set_rollback()
|
||||||
response["errors"] = [
|
|
||||||
self.format_error(e) for e in execution_result.errors
|
def safe_format(error):
|
||||||
]
|
config = getattr(graphene_settings, "GRAPHENE_ERRORS", {})
|
||||||
|
mask_exceptions = config.get("MASK_EXCEPTIONS", False)
|
||||||
|
error_message= config.get("ERROR_MESSAGE", "Something went wrong. Please try again later.")
|
||||||
|
whitelist = config.get("WHITELISTED_EXCEPTIONS", [])
|
||||||
|
|
||||||
|
if not mask_exceptions:
|
||||||
|
return self.format_error(error)
|
||||||
|
|
||||||
|
original_error = getattr(error, "original_error", None)
|
||||||
|
if not original_error:
|
||||||
|
return {"message": error_message}
|
||||||
|
|
||||||
|
error_class = type(original_error)
|
||||||
|
class_name = error_class.__name__
|
||||||
|
full_path = f"{error_class.__module__}.{class_name}"
|
||||||
|
|
||||||
|
if class_name in whitelist or full_path in whitelist:
|
||||||
|
return self.format_error(error)
|
||||||
|
|
||||||
|
formatted = self.format_error(error)
|
||||||
|
formatted["message"] = error_message
|
||||||
|
return formatted
|
||||||
|
|
||||||
|
response["errors"] = [safe_format(e) for e in execution_result.errors]
|
||||||
|
|
||||||
if execution_result.errors and any(
|
if execution_result.errors and any(
|
||||||
not getattr(e, "path", None) for e in execution_result.errors
|
not getattr(e, "path", None) for e in execution_result.errors
|
||||||
|
|
Loading…
Reference in New Issue
Block a user