Skip to content

Instantly share code, notes, and snippets.

@jphalip
Created June 3, 2012 23:16
Show Gist options
  • Save jphalip/2865381 to your computer and use it in GitHub Desktop.
Save jphalip/2865381 to your computer and use it in GitHub Desktop.
Patch for Django ticket #18379
diff --git a/django/views/debug.py b/django/views/debug.py
index 7bdf0d2..d95cd62 100644
--- a/django/views/debug.py
+++ b/django/views/debug.py
@@ -155,9 +155,20 @@ class SafeExceptionReporterFilter(ExceptionReporterFilter):
Replaces the values of variables marked as sensitive with
stars (*********).
"""
- func_name = tb_frame.f_code.co_name
- func = tb_frame.f_globals.get(func_name)
- sensitive_variables = getattr(func, 'sensitive_variables', [])
+ # Loop through the frame's callers to see if the sensitive_variables
+ # decorator was used.
+ current_frame = tb_frame.f_back
+ sensitive_variables = None
+ while current_frame is not None:
+ if (current_frame.f_code.co_name == 'sensitive_variables_wrapper'
+ and 'sensitive_variables_wrapper' in current_frame.f_locals):
+ # The sensitive_variables decorator was used, so we take note
+ # of the sensitive variables' names.
+ wrapper = current_frame.f_locals['sensitive_variables_wrapper']
+ sensitive_variables = getattr(wrapper, 'sensitive_variables', None)
+ break
+ current_frame = current_frame.f_back
+
cleansed = []
if self.is_active(request) and sensitive_variables:
if sensitive_variables == '__ALL__':
diff --git a/django/views/decorators/debug.py b/django/views/decorators/debug.py
index d04967e..5c22296 100644
--- a/django/views/decorators/debug.py
+++ b/django/views/decorators/debug.py
@@ -26,13 +26,13 @@ def sensitive_variables(*variables):
"""
def decorator(func):
@functools.wraps(func)
- def wrapper(*args, **kwargs):
+ def sensitive_variables_wrapper(*args, **kwargs):
if variables:
- wrapper.sensitive_variables = variables
+ sensitive_variables_wrapper.sensitive_variables = variables
else:
- wrapper.sensitive_variables = '__ALL__'
+ sensitive_variables_wrapper.sensitive_variables = '__ALL__'
return func(*args, **kwargs)
- return wrapper
+ return sensitive_variables_wrapper
return decorator
@@ -61,11 +61,11 @@ def sensitive_post_parameters(*parameters):
"""
def decorator(view):
@functools.wraps(view)
- def wrapper(request, *args, **kwargs):
+ def sensitive_post_parameters_wrapper(request, *args, **kwargs):
if parameters:
request.sensitive_post_parameters = parameters
else:
request.sensitive_post_parameters = '__ALL__'
return view(request, *args, **kwargs)
- return wrapper
+ return sensitive_post_parameters_wrapper
return decorator
diff --git a/tests/regressiontests/views/tests/debug.py b/tests/regressiontests/views/tests/debug.py
index c8358d3..e8a7d49 100644
--- a/tests/regressiontests/views/tests/debug.py
+++ b/tests/regressiontests/views/tests/debug.py
@@ -10,13 +10,12 @@ from django.test import TestCase, RequestFactory
from django.test.utils import (setup_test_template_loader,
restore_template_loaders)
from django.core.urlresolvers import reverse
-from django.template import TemplateSyntaxError
from django.views.debug import ExceptionReporter
from django.core import mail
from .. import BrokenException, except_args
from ..views import (sensitive_view, non_sensitive_view, paranoid_view,
- custom_exception_reporter_filter_view)
+ custom_exception_reporter_filter_view, sensitive_method_view)
class DebugViewTests(TestCase):
@@ -238,7 +237,8 @@ class ExceptionReportTestMixin(object):
'hash-brown-key': 'hash-brown-value',
'bacon-key': 'bacon-value',}
- def verify_unsafe_response(self, view, check_for_vars=True):
+ def verify_unsafe_response(self, view, check_for_vars=True,
+ check_for_POST_params=True):
"""
Asserts that potentially sensitive info are displayed in the response.
"""
@@ -250,13 +250,14 @@ class ExceptionReportTestMixin(object):
self.assertContains(response, 'scrambled', status_code=500)
self.assertContains(response, 'sauce', status_code=500)
self.assertContains(response, 'worcestershire', status_code=500)
+ if check_for_POST_params:
+ for k, v in self.breakfast_data.items():
+ # All POST parameters are shown.
+ self.assertContains(response, k, status_code=500)
+ self.assertContains(response, v, status_code=500)
- for k, v in self.breakfast_data.items():
- # All POST parameters are shown.
- self.assertContains(response, k, status_code=500)
- self.assertContains(response, v, status_code=500)
-
- def verify_safe_response(self, view, check_for_vars=True):
+ def verify_safe_response(self, view, check_for_vars=True,
+ check_for_POST_params=True):
"""
Asserts that certain sensitive info are not displayed in the response.
"""
@@ -269,18 +270,19 @@ class ExceptionReportTestMixin(object):
# Sensitive variable's name is shown but not its value.
self.assertContains(response, 'sauce', status_code=500)
self.assertNotContains(response, 'worcestershire', status_code=500)
+ if check_for_POST_params:
+ for k, v in self.breakfast_data.items():
+ # All POST parameters' names are shown.
+ self.assertContains(response, k, status_code=500)
+ # Non-sensitive POST parameters' values are shown.
+ self.assertContains(response, 'baked-beans-value', status_code=500)
+ self.assertContains(response, 'hash-brown-value', status_code=500)
+ # Sensitive POST parameters' values are not shown.
+ self.assertNotContains(response, 'sausage-value', status_code=500)
+ self.assertNotContains(response, 'bacon-value', status_code=500)
- for k, v in self.breakfast_data.items():
- # All POST parameters' names are shown.
- self.assertContains(response, k, status_code=500)
- # Non-sensitive POST parameters' values are shown.
- self.assertContains(response, 'baked-beans-value', status_code=500)
- self.assertContains(response, 'hash-brown-value', status_code=500)
- # Sensitive POST parameters' values are not shown.
- self.assertNotContains(response, 'sausage-value', status_code=500)
- self.assertNotContains(response, 'bacon-value', status_code=500)
-
- def verify_paranoid_response(self, view, check_for_vars=True):
+ def verify_paranoid_response(self, view, check_for_vars=True,
+ check_for_POST_params=True):
"""
Asserts that no variables or POST parameters are displayed in the response.
"""
@@ -292,14 +294,14 @@ class ExceptionReportTestMixin(object):
self.assertNotContains(response, 'scrambled', status_code=500)
self.assertContains(response, 'sauce', status_code=500)
self.assertNotContains(response, 'worcestershire', status_code=500)
+ if check_for_POST_params:
+ for k, v in self.breakfast_data.items():
+ # All POST parameters' names are shown.
+ self.assertContains(response, k, status_code=500)
+ # No POST parameters' values are shown.
+ self.assertNotContains(response, v, status_code=500)
- for k, v in self.breakfast_data.items():
- # All POST parameters' names are shown.
- self.assertContains(response, k, status_code=500)
- # No POST parameters' values are shown.
- self.assertNotContains(response, v, status_code=500)
-
- def verify_unsafe_email(self, view):
+ def verify_unsafe_email(self, view, check_for_POST_params=True):
"""
Asserts that potentially sensitive info are displayed in the email report.
"""
@@ -314,12 +316,13 @@ class ExceptionReportTestMixin(object):
self.assertNotIn('scrambled', email.body)
self.assertNotIn('sauce', email.body)
self.assertNotIn('worcestershire', email.body)
- for k, v in self.breakfast_data.items():
- # All POST parameters are shown.
- self.assertIn(k, email.body)
- self.assertIn(v, email.body)
+ if check_for_POST_params:
+ for k, v in self.breakfast_data.items():
+ # All POST parameters are shown.
+ self.assertIn(k, email.body)
+ self.assertIn(v, email.body)
- def verify_safe_email(self, view):
+ def verify_safe_email(self, view, check_for_POST_params=True):
"""
Asserts that certain sensitive info are not displayed in the email report.
"""
@@ -334,15 +337,16 @@ class ExceptionReportTestMixin(object):
self.assertNotIn('scrambled', email.body)
self.assertNotIn('sauce', email.body)
self.assertNotIn('worcestershire', email.body)
- for k, v in self.breakfast_data.items():
- # All POST parameters' names are shown.
- self.assertIn(k, email.body)
- # Non-sensitive POST parameters' values are shown.
- self.assertIn('baked-beans-value', email.body)
- self.assertIn('hash-brown-value', email.body)
- # Sensitive POST parameters' values are not shown.
- self.assertNotIn('sausage-value', email.body)
- self.assertNotIn('bacon-value', email.body)
+ if check_for_POST_params:
+ for k, v in self.breakfast_data.items():
+ # All POST parameters' names are shown.
+ self.assertIn(k, email.body)
+ # Non-sensitive POST parameters' values are shown.
+ self.assertIn('baked-beans-value', email.body)
+ self.assertIn('hash-brown-value', email.body)
+ # Sensitive POST parameters' values are not shown.
+ self.assertNotIn('sausage-value', email.body)
+ self.assertNotIn('bacon-value', email.body)
def verify_paranoid_email(self, view):
"""
@@ -425,6 +429,24 @@ class ExceptionReporterFilterTests(TestCase, ExceptionReportTestMixin):
self.verify_unsafe_response(custom_exception_reporter_filter_view)
self.verify_unsafe_email(custom_exception_reporter_filter_view)
+ def test_sensitive_method(self):
+ """
+ Ensure that the sensitive_variables decorator works with object
+ methods.
+ Refs #18379.
+ """
+ with self.settings(DEBUG=True):
+ self.verify_unsafe_response(sensitive_method_view,
+ check_for_POST_params=False)
+ self.verify_unsafe_email(sensitive_method_view,
+ check_for_POST_params=False)
+
+ with self.settings(DEBUG=False):
+ self.verify_safe_response(sensitive_method_view,
+ check_for_POST_params=False)
+ self.verify_safe_email(sensitive_method_view,
+ check_for_POST_params=False)
+
class AjaxResponseExceptionReporterFilter(TestCase, ExceptionReportTestMixin):
"""
diff --git a/tests/regressiontests/views/views.py b/tests/regressiontests/views/views.py
index 8e530cd..2836d1b 100644
--- a/tests/regressiontests/views/views.py
+++ b/tests/regressiontests/views/views.py
@@ -2,7 +2,6 @@ from __future__ import absolute_import
import sys
-from django import forms
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import get_resolver
from django.http import HttpResponse, HttpResponseRedirect
@@ -14,7 +13,7 @@ from django.views.decorators.debug import (sensitive_post_parameters,
from django.utils.log import getLogger
from . import BrokenException, except_args
-from .models import Article
+
def index_page(request):
@@ -209,3 +208,23 @@ def custom_exception_reporter_filter_view(request):
exc_info = sys.exc_info()
send_log(request, exc_info)
return technical_500_response(request, *exc_info)
+
+
+class Klass(object):
+
+ @sensitive_variables('sauce')
+ def method(self, request):
+ # Do not just use plain strings for the variables' values in the code
+ # so that the tests don't return false positives when the function's
+ # source is displayed in the exception report.
+ cooked_eggs = ''.join(['s', 'c', 'r', 'a', 'm', 'b', 'l', 'e', 'd'])
+ sauce = ''.join(['w', 'o', 'r', 'c', 'e', 's', 't', 'e', 'r', 's', 'h', 'i', 'r', 'e'])
+ try:
+ raise Exception
+ except Exception:
+ exc_info = sys.exc_info()
+ send_log(request, exc_info)
+ return technical_500_response(request, *exc_info)
+
+def sensitive_method_view(request):
+ return Klass().method(request)
\ No newline at end of file
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment