Created
June 3, 2012 23:16
-
-
Save jphalip/2865381 to your computer and use it in GitHub Desktop.
Patch for Django ticket #18379
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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