Ticket #19453: 19453-sensitive-variables.diff

File 19453-sensitive-variables.diff, 11.6 KB (added by Julien Phalip, 11 years ago)
  • django/views/debug.py

    diff --git a/django/views/debug.py b/django/views/debug.py
    index aaa7e40..e5f4c70 100644
    a b class SafeExceptionReporterFilter(ExceptionReporterFilter):  
    172172                break
    173173            current_frame = current_frame.f_back
    174174
    175         cleansed = []
     175        cleansed = {}
    176176        if self.is_active(request) and sensitive_variables:
    177177            if sensitive_variables == '__ALL__':
    178178                # Cleanse all variables
    179179                for name, value in tb_frame.f_locals.items():
    180                     cleansed.append((name, CLEANSED_SUBSTITUTE))
    181                 return cleansed
     180                    cleansed[name] = CLEANSED_SUBSTITUTE
    182181            else:
    183182                # Cleanse specified variables
    184183                for name, value in tb_frame.f_locals.items():
    class SafeExceptionReporterFilter(ExceptionReporterFilter):  
    187186                    elif isinstance(value, HttpRequest):
    188187                        # Cleanse the request's POST parameters.
    189188                        value = self.get_request_repr(value)
    190                     cleansed.append((name, value))
    191                 return cleansed
     189                    cleansed[name] = value
    192190        else:
    193191            # Potentially cleanse only the request if it's one of the frame variables.
    194192            for name, value in tb_frame.f_locals.items():
    195193                if isinstance(value, HttpRequest):
    196194                    # Cleanse the request's POST parameters.
    197195                    value = self.get_request_repr(value)
    198                 cleansed.append((name, value))
    199             return cleansed
     196                cleansed[name] = value
     197
     198        if (tb_frame.f_code.co_name == 'sensitive_variables_wrapper'
     199            and 'sensitive_variables_wrapper' in tb_frame.f_locals):
     200            # For good measure, obfuscate the decorated function's arguments in
     201            # the sensitive_variables decorator's frame, in case the variables
     202            # associated with those arguments were meant to be obfuscated from
     203            # the decorated function's frame.
     204            cleansed['func_args'] = CLEANSED_SUBSTITUTE
     205            cleansed['func_kwargs'] = CLEANSED_SUBSTITUTE
     206
     207        return cleansed.items()
    200208
    201209class ExceptionReporter(object):
    202210    """
  • django/views/decorators/debug.py

    diff --git a/django/views/decorators/debug.py b/django/views/decorators/debug.py
    index 5c22296..78ae6b1 100644
    a b def sensitive_variables(*variables):  
    2626    """
    2727    def decorator(func):
    2828        @functools.wraps(func)
    29         def sensitive_variables_wrapper(*args, **kwargs):
     29        def sensitive_variables_wrapper(*func_args, **func_kwargs):
    3030            if variables:
    3131                sensitive_variables_wrapper.sensitive_variables = variables
    3232            else:
    3333                sensitive_variables_wrapper.sensitive_variables = '__ALL__'
    34             return func(*args, **kwargs)
     34            return func(*func_args, **func_kwargs)
    3535        return sensitive_variables_wrapper
    3636    return decorator
    3737
  • tests/regressiontests/views/tests/debug.py

    diff --git a/tests/regressiontests/views/tests/debug.py b/tests/regressiontests/views/tests/debug.py
    index 4fdaad5..0e36948b 100644
    a b import inspect  
    77import os
    88import sys
    99
    10 from django.conf import settings
    1110from django.core import mail
    1211from django.core.files.uploadedfile import SimpleUploadedFile
    1312from django.core.urlresolvers import reverse
    from django.views.debug import ExceptionReporter  
    1918
    2019from .. import BrokenException, except_args
    2120from ..views import (sensitive_view, non_sensitive_view, paranoid_view,
    22     custom_exception_reporter_filter_view, sensitive_method_view)
     21    custom_exception_reporter_filter_view, sensitive_method_view,
     22    sensitive_args_function_caller, sensitive_kwargs_function_caller)
    2323
    2424
    2525@override_settings(DEBUG=True, TEMPLATE_DEBUG=True)
    class ExceptionReportTestMixin(object):  
    306306            response = view(request)
    307307            self.assertEqual(len(mail.outbox), 1)
    308308            email = mail.outbox[0]
     309
    309310            # Frames vars are never shown in plain text email reports.
    310             body = force_text(email.body)
    311             self.assertNotIn('cooked_eggs', body)
    312             self.assertNotIn('scrambled', body)
    313             self.assertNotIn('sauce', body)
    314             self.assertNotIn('worcestershire', body)
     311            body_plain = force_text(email.body)
     312            self.assertNotIn('cooked_eggs', body_plain)
     313            self.assertNotIn('scrambled', body_plain)
     314            self.assertNotIn('sauce', body_plain)
     315            self.assertNotIn('worcestershire', body_plain)
     316
     317            # Frames vars are shown in html email reports.
     318            body_html = force_text(email.alternatives[0][0])
     319            self.assertIn('cooked_eggs', body_html)
     320            self.assertIn('scrambled', body_html)
     321            self.assertIn('sauce', body_html)
     322            self.assertIn('worcestershire', body_html)
     323
    315324            if check_for_POST_params:
    316325                for k, v in self.breakfast_data.items():
    317326                    # All POST parameters are shown.
    318                     self.assertIn(k, body)
    319                     self.assertIn(v, body)
     327                    self.assertIn(k, body_plain)
     328                    self.assertIn(v, body_plain)
     329                    self.assertIn(k, body_html)
     330                    self.assertIn(v, body_html)
    320331
    321332    def verify_safe_email(self, view, check_for_POST_params=True):
    322333        """
    class ExceptionReportTestMixin(object):  
    328339            response = view(request)
    329340            self.assertEqual(len(mail.outbox), 1)
    330341            email = mail.outbox[0]
     342
    331343            # Frames vars are never shown in plain text email reports.
    332             body = force_text(email.body)
    333             self.assertNotIn('cooked_eggs', body)
    334             self.assertNotIn('scrambled', body)
    335             self.assertNotIn('sauce', body)
    336             self.assertNotIn('worcestershire', body)
     344            body_plain = force_text(email.body)
     345            self.assertNotIn('cooked_eggs', body_plain)
     346            self.assertNotIn('scrambled', body_plain)
     347            self.assertNotIn('sauce', body_plain)
     348            self.assertNotIn('worcestershire', body_plain)
     349
     350            # Frames vars are shown in html email reports.
     351            body_html = force_text(email.alternatives[0][0])
     352            self.assertIn('cooked_eggs', body_html)
     353            self.assertIn('scrambled', body_html)
     354            self.assertIn('sauce', body_html)
     355            self.assertNotIn('worcestershire', body_html)
     356
    337357            if check_for_POST_params:
    338358                for k, v in self.breakfast_data.items():
    339359                    # All POST parameters' names are shown.
    340                     self.assertIn(k, body)
     360                    self.assertIn(k, body_plain)
    341361                # Non-sensitive POST parameters' values are shown.
    342                 self.assertIn('baked-beans-value', body)
    343                 self.assertIn('hash-brown-value', body)
     362                self.assertIn('baked-beans-value', body_plain)
     363                self.assertIn('hash-brown-value', body_plain)
     364                self.assertIn('baked-beans-value', body_html)
     365                self.assertIn('hash-brown-value', body_html)
    344366                # Sensitive POST parameters' values are not shown.
    345                 self.assertNotIn('sausage-value', body)
    346                 self.assertNotIn('bacon-value', body)
     367                self.assertNotIn('sausage-value', body_plain)
     368                self.assertNotIn('bacon-value', body_plain)
     369                self.assertNotIn('sausage-value', body_html)
     370                self.assertNotIn('bacon-value', body_html)
    347371
    348372    def verify_paranoid_email(self, view):
    349373        """
    class ExceptionReporterFilterTests(TestCase, ExceptionReportTestMixin):  
    445469            self.verify_safe_email(sensitive_method_view,
    446470                                   check_for_POST_params=False)
    447471
     472    def test_sensitive_function_arguments(self):
     473        """
     474        Ensure that sensitive variables don't leak in the sensitive_variables
     475        decorator's frame, when those variables are passed as arguments to the
     476        decorated function.
     477        Refs #19453.
     478        """
     479        with self.settings(DEBUG=True):
     480            self.verify_unsafe_response(sensitive_args_function_caller)
     481            self.verify_unsafe_email(sensitive_args_function_caller)
     482
     483        with self.settings(DEBUG=False):
     484            self.verify_safe_response(sensitive_args_function_caller, check_for_POST_params=False)
     485            self.verify_safe_email(sensitive_args_function_caller, check_for_POST_params=False)
     486
     487    def test_sensitive_function_keyword_arguments(self):
     488        """
     489        Ensure that sensitive variables don't leak in the sensitive_variables
     490        decorator's frame, when those variables are passed as keyword arguments
     491        to the decorated function.
     492        Refs #19453.
     493        """
     494        with self.settings(DEBUG=True):
     495            self.verify_unsafe_response(sensitive_kwargs_function_caller)
     496            self.verify_unsafe_email(sensitive_kwargs_function_caller)
     497
     498        with self.settings(DEBUG=False):
     499            self.verify_safe_response(sensitive_kwargs_function_caller, check_for_POST_params=False)
     500            self.verify_safe_email(sensitive_kwargs_function_caller, check_for_POST_params=False)
     501
    448502
    449503class AjaxResponseExceptionReporterFilter(TestCase, ExceptionReportTestMixin):
    450504    """
  • tests/regressiontests/views/views.py

    diff --git a/tests/regressiontests/views/views.py b/tests/regressiontests/views/views.py
    index ed9d611..748f076 100644
    a b def send_log(request, exc_info):  
    132132        ][0]
    133133    orig_filters = admin_email_handler.filters
    134134    admin_email_handler.filters = []
     135    admin_email_handler.include_html = True
    135136    logger.error('Internal Server Error: %s', request.path,
    136137        exc_info=exc_info,
    137138        extra={
    def paranoid_view(request):  
    184185        send_log(request, exc_info)
    185186        return technical_500_response(request, *exc_info)
    186187
     188def sensitive_args_function_caller(request):
     189    try:
     190        sensitive_args_function(''.join(['w', 'o', 'r', 'c', 'e', 's', 't', 'e', 'r', 's', 'h', 'i', 'r', 'e']))
     191    except Exception:
     192        exc_info = sys.exc_info()
     193        send_log(request, exc_info)
     194        return technical_500_response(request, *exc_info)
     195
     196@sensitive_variables('sauce')
     197def sensitive_args_function(sauce):
     198    # Do not just use plain strings for the variables' values in the code
     199    # so that the tests don't return false positives when the function's source
     200    # is displayed in the exception report.
     201    cooked_eggs = ''.join(['s', 'c', 'r', 'a', 'm', 'b', 'l', 'e', 'd'])
     202    raise Exception
     203
     204def sensitive_kwargs_function_caller(request):
     205    try:
     206        sensitive_kwargs_function(''.join(['w', 'o', 'r', 'c', 'e', 's', 't', 'e', 'r', 's', 'h', 'i', 'r', 'e']))
     207    except Exception:
     208        exc_info = sys.exc_info()
     209        send_log(request, exc_info)
     210        return technical_500_response(request, *exc_info)
     211
     212@sensitive_variables('sauce')
     213def sensitive_kwargs_function(sauce=None):
     214    # Do not just use plain strings for the variables' values in the code
     215    # so that the tests don't return false positives when the function's source
     216    # is displayed in the exception report.
     217    cooked_eggs = ''.join(['s', 'c', 'r', 'a', 'm', 'b', 'l', 'e', 'd'])
     218    raise Exception
     219
    187220class UnsafeExceptionReporterFilter(SafeExceptionReporterFilter):
    188221    """
    189222    Ignores all the filtering done by its parent class.
Back to Top