Skip to content

Commit 1b78a6f

Browse files
committed
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
1 parent c52cf2b commit 1b78a6f

File tree

3 files changed

+76
-3
lines changed

3 files changed

+76
-3
lines changed

docs/error-masking.rst

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
Custom GraphQL Error Masking
2+
============================
3+
4+
This project includes a custom error formatting function for GraphQL
5+
responses that masks sensitive error details from clients.
6+
7+
Purpose
8+
-------
9+
10+
- Prevent exposing internal error details for security and user experience.
11+
- Allow whitelisting of exception classes that should be exposed as-is.
12+
- Return a generic error message for all other exceptions.
13+
14+
Configuration
15+
-------------
16+
17+
You can control the behavior using the ``GRAPHENE_ERRORS`` setting in your
18+
Django settings file under the ``GRAPHENE`` namespace:
19+
20+
.. code-block:: python
21+
22+
GRAPHENE = {
23+
"GRAPHENE_ERRORS": {
24+
"MASK_EXCEPTIONS": True, # Enable or disable masking
25+
"ERROR_MESSAGE": "A custom error message.", # Defaults to "Something went wrong. Please try again later."
26+
"WHITELISTED_EXCEPTIONS": [
27+
"ValidationError", # Whitelist by class name
28+
"django.core.exceptions.ValidationError", # Whitelist by full module path
29+
"myapp.custom_exceptions.MyCustomException", # Custom exception whitelist by full path
30+
],
31+
}
32+
}
33+
34+
Behavior
35+
--------
36+
37+
- If ``MASK_EXCEPTIONS`` is False, all errors are returned fully formatted.
38+
- If True, errors not in the whitelist will return only the generic message.
39+
- Whitelisted exceptions are returned with full error details.
40+
41+
Usage
42+
-----
43+
44+
The masking is automatically applied to the error responses of GraphQL
45+
queries and mutations through a custom error formatter method.
46+
47+
You can modify or extend the whitelisted exceptions as needed to suit your
48+
project's error handling policy.
49+

graphene_django/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"ATOMIC_MUTATIONS": False,
4747
"TESTING_ENDPOINT": "/graphql",
4848
"MAX_VALIDATION_ERRORS": None,
49+
"GRAPHENE_ERRORS": {},
4950
}
5051

5152
if settings.DEBUG:

graphene_django/views.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -228,9 +228,32 @@ def get_response(self, request, data, show_graphiql=False):
228228

229229
if execution_result.errors:
230230
set_rollback()
231-
response["errors"] = [
232-
self.format_error(e) for e in execution_result.errors
233-
]
231+
232+
def safe_format(error):
233+
config = getattr(graphene_settings, "GRAPHENE_ERRORS", {})
234+
mask_exceptions = config.get("MASK_EXCEPTIONS", False)
235+
error_message= config.get("ERROR_MESSAGE", "Something went wrong. Please try again later.")
236+
whitelist = config.get("WHITELISTED_EXCEPTIONS", [])
237+
238+
if not mask_exceptions:
239+
return self.format_error(error)
240+
241+
original_error = getattr(error, "original_error", None)
242+
if not original_error:
243+
return {"message": error_message}
244+
245+
error_class = type(original_error)
246+
class_name = error_class.__name__
247+
full_path = f"{error_class.__module__}.{class_name}"
248+
249+
if class_name in whitelist or full_path in whitelist:
250+
return self.format_error(error)
251+
252+
formatted = self.format_error(error)
253+
formatted["message"] = error_message
254+
return formatted
255+
256+
response["errors"] = [safe_format(e) for e in execution_result.errors]
234257

235258
if execution_result.errors and any(
236259
not getattr(e, "path", None) for e in execution_result.errors

0 commit comments

Comments
 (0)