Fixed #17138 -- Made the sensitive_variables decorator work with object methods.
This commit is contained in:
parent
f29234167a
commit
f699641161
|
@ -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__':
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
@ -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)
|
Loading…
Reference in New Issue