[1.4.X] Fixed #18379 -- Made the sensitive_variables decorator work with object methods.
This commit is contained in:
parent
0f69a16785
commit
4d2fdd4185
|
@ -155,9 +155,20 @@ class SafeExceptionReporterFilter(ExceptionReporterFilter):
|
||||||
Replaces the values of variables marked as sensitive with
|
Replaces the values of variables marked as sensitive with
|
||||||
stars (*********).
|
stars (*********).
|
||||||
"""
|
"""
|
||||||
func_name = tb_frame.f_code.co_name
|
# Loop through the frame's callers to see if the sensitive_variables
|
||||||
func = tb_frame.f_globals.get(func_name)
|
# decorator was used.
|
||||||
sensitive_variables = getattr(func, 'sensitive_variables', [])
|
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 = []
|
cleansed = []
|
||||||
if self.is_active(request) and sensitive_variables:
|
if self.is_active(request) and sensitive_variables:
|
||||||
if sensitive_variables == '__ALL__':
|
if sensitive_variables == '__ALL__':
|
||||||
|
|
|
@ -26,13 +26,13 @@ def sensitive_variables(*variables):
|
||||||
"""
|
"""
|
||||||
def decorator(func):
|
def decorator(func):
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def wrapper(*args, **kwargs):
|
def sensitive_variables_wrapper(*args, **kwargs):
|
||||||
if variables:
|
if variables:
|
||||||
wrapper.sensitive_variables = variables
|
sensitive_variables_wrapper.sensitive_variables = variables
|
||||||
else:
|
else:
|
||||||
wrapper.sensitive_variables = '__ALL__'
|
sensitive_variables_wrapper.sensitive_variables = '__ALL__'
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
return wrapper
|
return sensitive_variables_wrapper
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
@ -61,11 +61,11 @@ def sensitive_post_parameters(*parameters):
|
||||||
"""
|
"""
|
||||||
def decorator(view):
|
def decorator(view):
|
||||||
@functools.wraps(view)
|
@functools.wraps(view)
|
||||||
def wrapper(request, *args, **kwargs):
|
def sensitive_post_parameters_wrapper(request, *args, **kwargs):
|
||||||
if parameters:
|
if parameters:
|
||||||
request.sensitive_post_parameters = parameters
|
request.sensitive_post_parameters = parameters
|
||||||
else:
|
else:
|
||||||
request.sensitive_post_parameters = '__ALL__'
|
request.sensitive_post_parameters = '__ALL__'
|
||||||
return view(request, *args, **kwargs)
|
return view(request, *args, **kwargs)
|
||||||
return wrapper
|
return sensitive_post_parameters_wrapper
|
||||||
return decorator
|
return decorator
|
||||||
|
|
|
@ -10,13 +10,12 @@ from django.test import TestCase, RequestFactory
|
||||||
from django.test.utils import (setup_test_template_loader,
|
from django.test.utils import (setup_test_template_loader,
|
||||||
restore_template_loaders)
|
restore_template_loaders)
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.template import TemplateSyntaxError
|
|
||||||
from django.views.debug import ExceptionReporter
|
from django.views.debug import ExceptionReporter
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
|
|
||||||
from .. import BrokenException, except_args
|
from .. import BrokenException, except_args
|
||||||
from ..views import (sensitive_view, non_sensitive_view, paranoid_view,
|
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):
|
class DebugViewTests(TestCase):
|
||||||
|
@ -238,7 +237,8 @@ class ExceptionReportTestMixin(object):
|
||||||
'hash-brown-key': 'hash-brown-value',
|
'hash-brown-key': 'hash-brown-value',
|
||||||
'bacon-key': 'bacon-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.
|
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, 'scrambled', status_code=500)
|
||||||
self.assertContains(response, 'sauce', status_code=500)
|
self.assertContains(response, 'sauce', status_code=500)
|
||||||
self.assertContains(response, 'worcestershire', 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():
|
def verify_safe_response(self, view, check_for_vars=True,
|
||||||
# All POST parameters are shown.
|
check_for_POST_params=True):
|
||||||
self.assertContains(response, k, status_code=500)
|
|
||||||
self.assertContains(response, v, status_code=500)
|
|
||||||
|
|
||||||
def verify_safe_response(self, view, check_for_vars=True):
|
|
||||||
"""
|
"""
|
||||||
Asserts that certain sensitive info are not displayed in the response.
|
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.
|
# Sensitive variable's name is shown but not its value.
|
||||||
self.assertContains(response, 'sauce', status_code=500)
|
self.assertContains(response, 'sauce', status_code=500)
|
||||||
self.assertNotContains(response, 'worcestershire', 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():
|
def verify_paranoid_response(self, view, check_for_vars=True,
|
||||||
# All POST parameters' names are shown.
|
check_for_POST_params=True):
|
||||||
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):
|
|
||||||
"""
|
"""
|
||||||
Asserts that no variables or POST parameters are displayed in the response.
|
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.assertNotContains(response, 'scrambled', status_code=500)
|
||||||
self.assertContains(response, 'sauce', status_code=500)
|
self.assertContains(response, 'sauce', status_code=500)
|
||||||
self.assertNotContains(response, 'worcestershire', 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():
|
def verify_unsafe_email(self, view, check_for_POST_params=True):
|
||||||
# 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):
|
|
||||||
"""
|
"""
|
||||||
Asserts that potentially sensitive info are displayed in the email report.
|
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('scrambled', email.body)
|
||||||
self.assertNotIn('sauce', email.body)
|
self.assertNotIn('sauce', email.body)
|
||||||
self.assertNotIn('worcestershire', email.body)
|
self.assertNotIn('worcestershire', email.body)
|
||||||
for k, v in self.breakfast_data.items():
|
if check_for_POST_params:
|
||||||
# All POST parameters are shown.
|
for k, v in self.breakfast_data.items():
|
||||||
self.assertIn(k, email.body)
|
# All POST parameters are shown.
|
||||||
self.assertIn(v, email.body)
|
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.
|
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('scrambled', email.body)
|
||||||
self.assertNotIn('sauce', email.body)
|
self.assertNotIn('sauce', email.body)
|
||||||
self.assertNotIn('worcestershire', email.body)
|
self.assertNotIn('worcestershire', email.body)
|
||||||
for k, v in self.breakfast_data.items():
|
if check_for_POST_params:
|
||||||
# All POST parameters' names are shown.
|
for k, v in self.breakfast_data.items():
|
||||||
self.assertIn(k, email.body)
|
# All POST parameters' names are shown.
|
||||||
# Non-sensitive POST parameters' values are shown.
|
self.assertIn(k, email.body)
|
||||||
self.assertIn('baked-beans-value', email.body)
|
# Non-sensitive POST parameters' values are shown.
|
||||||
self.assertIn('hash-brown-value', email.body)
|
self.assertIn('baked-beans-value', email.body)
|
||||||
# Sensitive POST parameters' values are not shown.
|
self.assertIn('hash-brown-value', email.body)
|
||||||
self.assertNotIn('sausage-value', email.body)
|
# Sensitive POST parameters' values are not shown.
|
||||||
self.assertNotIn('bacon-value', email.body)
|
self.assertNotIn('sausage-value', email.body)
|
||||||
|
self.assertNotIn('bacon-value', email.body)
|
||||||
|
|
||||||
def verify_paranoid_email(self, view):
|
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_response(custom_exception_reporter_filter_view)
|
||||||
self.verify_unsafe_email(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):
|
class AjaxResponseExceptionReporterFilter(TestCase, ExceptionReportTestMixin):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -2,7 +2,6 @@ from __future__ import absolute_import
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from django import forms
|
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.core.urlresolvers import get_resolver
|
from django.core.urlresolvers import get_resolver
|
||||||
from django.http import HttpResponse, HttpResponseRedirect
|
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 django.utils.log import getLogger
|
||||||
|
|
||||||
from . import BrokenException, except_args
|
from . import BrokenException, except_args
|
||||||
from .models import Article
|
|
||||||
|
|
||||||
|
|
||||||
def index_page(request):
|
def index_page(request):
|
||||||
|
@ -228,3 +227,23 @@ def custom_exception_reporter_filter_view(request):
|
||||||
exc_info = sys.exc_info()
|
exc_info = sys.exc_info()
|
||||||
send_log(request, exc_info)
|
send_log(request, exc_info)
|
||||||
return technical_500_response(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