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:
DrillableBit 2025-06-14 16:15:34 +02:00
parent c52cf2b045
commit 1b78a6f638
3 changed files with 76 additions and 3 deletions

49
docs/error-masking.rst Normal file
View 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.

View File

@ -46,6 +46,7 @@ DEFAULTS = {
"ATOMIC_MUTATIONS": False,
"TESTING_ENDPOINT": "/graphql",
"MAX_VALIDATION_ERRORS": None,
"GRAPHENE_ERRORS": {},
}
if settings.DEBUG:

View File

@ -228,9 +228,32 @@ class GraphQLView(View):
if execution_result.errors:
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(
not getattr(e, "path", None) for e in execution_result.errors