Commit 8f8c54f7 authored by Vlastimil Zíma's avatar Vlastimil Zíma Committed by Tim Graham
Browse files

Fixed #25099 -- Cleaned up HttpRequest representations in error reporting.

parent 6bdd3840
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
from django.http.cookie import SimpleCookie, parse_cookie
from django.http.request import (
    HttpRequest, QueryDict, RawPostDataException, UnreadablePostError,
    build_request_repr,
)
from django.http.response import (
    BadHeaderError, FileResponse, Http404, HttpResponse,
@@ -14,7 +13,7 @@ from django.http.utils import conditional_content_removal

__all__ = [
    'SimpleCookie', 'parse_cookie', 'HttpRequest', 'QueryDict',
    'RawPostDataException', 'UnreadablePostError', 'build_request_repr',
    'RawPostDataException', 'UnreadablePostError',
    'HttpResponse', 'StreamingHttpResponse', 'HttpResponseRedirect',
    'HttpResponsePermanentRedirect', 'HttpResponseNotModified',
    'HttpResponseBadRequest', 'HttpResponseForbidden', 'HttpResponseNotFound',
+0 −47
Original line number Diff line number Diff line
@@ -5,7 +5,6 @@ import re
import sys
from io import BytesIO
from itertools import chain
from pprint import pformat

from django.conf import settings
from django.core import signing
@@ -465,52 +464,6 @@ class QueryDict(MultiValueDict):
        return '&'.join(output)


def build_request_repr(request, path_override=None, GET_override=None,
                       POST_override=None, COOKIES_override=None,
                       META_override=None):
    """
    Builds and returns the request's representation string. The request's
    attributes may be overridden by pre-processed values.
    """
    # Since this is called as part of error handling, we need to be very
    # robust against potentially malformed input.
    try:
        get = (pformat(GET_override)
               if GET_override is not None
               else pformat(request.GET))
    except Exception:
        get = '<could not parse>'
    if request._post_parse_error:
        post = '<could not parse>'
    else:
        try:
            post = (pformat(POST_override)
                    if POST_override is not None
                    else pformat(request.POST))
        except Exception:
            post = '<could not parse>'
    try:
        cookies = (pformat(COOKIES_override)
                   if COOKIES_override is not None
                   else pformat(request.COOKIES))
    except Exception:
        cookies = '<could not parse>'
    try:
        meta = (pformat(META_override)
                if META_override is not None
                else pformat(request.META))
    except Exception:
        meta = '<could not parse>'
    path = path_override if path_override is not None else request.path
    return force_str('<%s\npath:%s,\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' %
                     (request.__class__.__name__,
                      path,
                      six.text_type(get),
                      six.text_type(post),
                      six.text_type(cookies),
                      six.text_type(meta)))


# It's neither necessary nor appropriate to use
# django.utils.encoding.smart_text for parsing URLs and form inputs. Thus,
# this slightly more restricted function, used by QueryDict.
+9 −6
Original line number Diff line number Diff line
@@ -4,14 +4,14 @@ import logging
import logging.config  # needed when logging_config doesn't start with logging.config
import sys
import warnings
from copy import copy

from django.conf import settings
from django.core import mail
from django.core.mail import get_connection
from django.utils.deprecation import RemovedInNextVersionWarning
from django.utils.encoding import force_text
from django.utils.module_loading import import_string
from django.views.debug import ExceptionReporter, get_exception_reporter_filter
from django.views.debug import ExceptionReporter

# Default logging for Django. This sends an email to the site admins on every
# HTTP 500 error. Depending on DEBUG, all other log records are either sent to
@@ -90,24 +90,27 @@ class AdminEmailHandler(logging.Handler):
                 else 'EXTERNAL'),
                record.getMessage()
            )
            filter = get_exception_reporter_filter(request)
            request_repr = '\n{}'.format(force_text(filter.get_request_repr(request)))
        except Exception:
            subject = '%s: %s' % (
                record.levelname,
                record.getMessage()
            )
            request = None
            request_repr = "unavailable"
        subject = self.format_subject(subject)

        # Since we add a nicely formatted traceback on our own, create a copy
        # of the log record without the exception data.
        no_exc_record = copy(record)
        no_exc_record.exc_info = None
        no_exc_record.exc_text = None

        if record.exc_info:
            exc_info = record.exc_info
        else:
            exc_info = (None, record.getMessage(), None)

        message = "%s\n\nRequest repr(): %s" % (self.format(record), request_repr)
        reporter = ExceptionReporter(request, is_email=True, *exc_info)
        message = "%s\n\n%s" % (self.format(no_exc_record), reporter.get_traceback_text())
        html_message = reporter.get_traceback_html() if self.include_html else None
        self.send_mail(subject, message, fail_silently=True, html_message=html_message)

+7 −16
Original line number Diff line number Diff line
@@ -6,9 +6,7 @@ import types

from django.conf import settings
from django.core.urlresolvers import Resolver404, resolve
from django.http import (
    HttpRequest, HttpResponse, HttpResponseNotFound, build_request_repr,
)
from django.http import HttpResponse, HttpResponseNotFound
from django.template import Context, Engine, TemplateDoesNotExist
from django.template.defaultfilters import force_escape, pprint
from django.utils import lru_cache, six, timezone
@@ -113,12 +111,6 @@ class ExceptionReporterFilter(object):
    contain lenient default behaviors.
    """

    def get_request_repr(self, request):
        if request is None:
            return repr(None)
        else:
            return build_request_repr(request, POST_override=self.get_post_parameters(request))

    def get_post_parameters(self, request):
        if request is None:
            return {}
@@ -186,16 +178,13 @@ class SafeExceptionReporterFilter(ExceptionReporterFilter):
    def cleanse_special_types(self, request, value):
        try:
            # If value is lazy or a complex object of another kind, this check
            # might raise an exception. isinstance checks that lazy HttpRequests
            # or MultiValueDicts will have a return value.
            is_request = isinstance(value, HttpRequest)
            # might raise an exception. isinstance checks that lazy
            # MultiValueDicts will have a return value.
            is_multivalue_dict = isinstance(value, MultiValueDict)
        except Exception as e:
            return '{!r} while evaluating {!r}'.format(e, value)

        if is_request:
            # Cleanse the request's POST parameters.
            value = self.get_request_repr(value)
        elif isinstance(value, MultiValueDict):
        if is_multivalue_dict:
            # Cleanse MultiValueDicts (request.POST is the one we usually care about)
            value = self.get_cleansed_multivaluedict(request, value)
        return value
@@ -1126,9 +1115,11 @@ Settings:
Using settings module {{ settings.SETTINGS_MODULE }}{% for k, v in settings.items|dictsort:"0" %}
{{ k }} = {{ v|stringformat:"r" }}{% endfor %}

{% if not is_email %}
You're seeing this error because you have DEBUG = True in your
Django settings file. Change that to False, and Django will
display a standard page generated by the handler for this status code.
{% endif %}
""")

TECHNICAL_404_TEMPLATE = """
+0 −7
Original line number Diff line number Diff line
@@ -255,13 +255,6 @@ following methods:
    Returns ``True`` to activate the filtering operated in the other methods.
    By default the filter is active if :setting:`DEBUG` is ``False``.

.. method:: SafeExceptionReporterFilter.get_request_repr(request)

    Returns the representation string of the request object, that is, the
    value that would be returned by ``repr(request)``, except it uses the
    filtered dictionary of POST parameters as determined by
    :meth:`SafeExceptionReporterFilter.get_post_parameters`.

.. method:: SafeExceptionReporterFilter.get_post_parameters(request)

    Returns the filtered dictionary of POST parameters. By default it replaces
Loading