Ticket #19453: 19453-sensitive-variables.diff
File 19453-sensitive-variables.diff, 11.6 KB (added by , 12 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): 172 172 break 173 173 current_frame = current_frame.f_back 174 174 175 cleansed = []175 cleansed = {} 176 176 if self.is_active(request) and sensitive_variables: 177 177 if sensitive_variables == '__ALL__': 178 178 # Cleanse all variables 179 179 for name, value in tb_frame.f_locals.items(): 180 cleansed.append((name, CLEANSED_SUBSTITUTE)) 181 return cleansed 180 cleansed[name] = CLEANSED_SUBSTITUTE 182 181 else: 183 182 # Cleanse specified variables 184 183 for name, value in tb_frame.f_locals.items(): … … class SafeExceptionReporterFilter(ExceptionReporterFilter): 187 186 elif isinstance(value, HttpRequest): 188 187 # Cleanse the request's POST parameters. 189 188 value = self.get_request_repr(value) 190 cleansed.append((name, value)) 191 return cleansed 189 cleansed[name] = value 192 190 else: 193 191 # Potentially cleanse only the request if it's one of the frame variables. 194 192 for name, value in tb_frame.f_locals.items(): 195 193 if isinstance(value, HttpRequest): 196 194 # Cleanse the request's POST parameters. 197 195 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() 200 208 201 209 class ExceptionReporter(object): 202 210 """ -
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): 26 26 """ 27 27 def decorator(func): 28 28 @functools.wraps(func) 29 def sensitive_variables_wrapper(* args, **kwargs):29 def sensitive_variables_wrapper(*func_args, **func_kwargs): 30 30 if variables: 31 31 sensitive_variables_wrapper.sensitive_variables = variables 32 32 else: 33 33 sensitive_variables_wrapper.sensitive_variables = '__ALL__' 34 return func(* args, **kwargs)34 return func(*func_args, **func_kwargs) 35 35 return sensitive_variables_wrapper 36 36 return decorator 37 37 -
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 7 7 import os 8 8 import sys 9 9 10 from django.conf import settings11 10 from django.core import mail 12 11 from django.core.files.uploadedfile import SimpleUploadedFile 13 12 from django.core.urlresolvers import reverse … … from django.views.debug import ExceptionReporter 19 18 20 19 from .. import BrokenException, except_args 21 20 from ..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) 23 23 24 24 25 25 @override_settings(DEBUG=True, TEMPLATE_DEBUG=True) … … class ExceptionReportTestMixin(object): 306 306 response = view(request) 307 307 self.assertEqual(len(mail.outbox), 1) 308 308 email = mail.outbox[0] 309 309 310 # 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 315 324 if check_for_POST_params: 316 325 for k, v in self.breakfast_data.items(): 317 326 # 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) 320 331 321 332 def verify_safe_email(self, view, check_for_POST_params=True): 322 333 """ … … class ExceptionReportTestMixin(object): 328 339 response = view(request) 329 340 self.assertEqual(len(mail.outbox), 1) 330 341 email = mail.outbox[0] 342 331 343 # 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 337 357 if check_for_POST_params: 338 358 for k, v in self.breakfast_data.items(): 339 359 # All POST parameters' names are shown. 340 self.assertIn(k, body )360 self.assertIn(k, body_plain) 341 361 # 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) 344 366 # 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) 347 371 348 372 def verify_paranoid_email(self, view): 349 373 """ … … class ExceptionReporterFilterTests(TestCase, ExceptionReportTestMixin): 445 469 self.verify_safe_email(sensitive_method_view, 446 470 check_for_POST_params=False) 447 471 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 448 502 449 503 class AjaxResponseExceptionReporterFilter(TestCase, ExceptionReportTestMixin): 450 504 """ -
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): 132 132 ][0] 133 133 orig_filters = admin_email_handler.filters 134 134 admin_email_handler.filters = [] 135 admin_email_handler.include_html = True 135 136 logger.error('Internal Server Error: %s', request.path, 136 137 exc_info=exc_info, 137 138 extra={ … … def paranoid_view(request): 184 185 send_log(request, exc_info) 185 186 return technical_500_response(request, *exc_info) 186 187 188 def 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') 197 def 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 204 def 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') 213 def 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 187 220 class UnsafeExceptionReporterFilter(SafeExceptionReporterFilter): 188 221 """ 189 222 Ignores all the filtering done by its parent class.