minimal django templates engine to render the error templates regardless of the project s templates setting templates are read directly from the filesystem so that the error handler works even if the template loader is broken return a path to a builtin template avoid calling this function at the module level or in a classdefinition because file may not exist e g in frozen environments object to wrap callable appearing in settings not to call in the debug page 21345 not to break the debug page if the callable forbidding to set attributes 23070 create a technical server error response the last three arguments are the values returned from sys excinfo and friends instantiate the default filter for the first time and cache it use annotations made by the sensitivepostparameters and sensitivevariables decorators to filter out sensitive information cleanse an individual setting keyvalue of sensitive content if the value is a dictionary recursively cleanse the keys in that dictionary return a dictionary of the settings module with values of sensitive settings replaced with stars return a dictionary of request meta with sensitive values redacted return a dictionary of request cookies with sensitive values redacted this filter is to add safety in production environments i e debug is false if debug is true then your site is not safe anyway this hook is provided as a convenience to easily activate or deactivate the filter on a per request basis replace the keys in a multivaluedict marked as sensitive with stars this mitigates leaking sensitive post parameters if something like request post nonexistentkey throws an exception 21098 replace the values of post parameters marked as sensitive with stars cleanse all parameters cleanse only the specified parameters if value is lazy or a complex object of another kind this check might raise an exception isinstance checks that lazy multivaluedicts will have a return value cleanse multivaluedicts request post is the one we usually care about replace the values of variables marked as sensitive with stars coroutines don t have a proper fback so they need to be inspected separately handle this by stashing the registered sensitive variables in a global dict indexed by hashfilepath linenumber loop through the frame s callers to see if the sensitivevariables decorator was used the sensitivevariables decorator was used so take note of the sensitive variables names cleanse all variables cleanse specified variables potentially cleanse the request and any multivaluedicts if they are one of the frame variables for good measure obfuscate the decorated function s arguments in the sensitivevariables decorator s frame in case the variables associated with those arguments were meant to be obfuscated from the decorated function s frame organize and coordinate reporting on exceptions property def htmltemplatepathself return builtintemplatepathtechnical500 html property def texttemplatepathself return builtintemplatepathtechnical500 txt def initself request exctype excvalue tb isemailfalse self request request self filter getexceptionreporterfilterself request self exctype exctype self excvalue excvalue self tb tb self isemail isemail self templateinfo getattrself excvalue templatedebug none self templatedoesnotexist false self postmortem none def getrawinsecureuriself return scheme hostpath format schemeself request scheme hostself request getrawhost pathself request getfullpath def gettracebackdataself trim large blobs of data request user may raise operationalerror if the database is unavailable for example check whether exception info is available return html version of debug 500 http error page with self htmltemplatepath openencodingutf8 as fh t debugengine fromstringfh read c contextself gettracebackdata usel10nfalse return t renderc def gettracebacktextself return contextlines before and after lineno from file return precontextlineno precontext contextline postcontext if we just read the source from a file or if the loader did not apply tokenize detectencoding to decode the source into a string then we should do that ourselves file coding may be specified match pattern from pep263 https www python orgdevpepspep0263 get the exception and all its causes avoid infinite loop if there s a cyclic reference 29393 no exceptions were supplied to exceptionreporter in case there s just one exception take the traceback from self tb support for tracebackhide which is used by a few libraries to hide internal frames create a technical 404 error response exception is the http404 try errorurl exception args0path except indexerror typeerror keyerror errorurl request pathinfo1 trim leading slash try tried exception args0tried except indexerror typeerror keyerror resolved true tried request resolvermatch tried if request resolvermatch else none else resolved false if not tried or empty urlconf request path and lentried 1 and lentried0 1 default urlconf and getattrtried00 appname getattrtried00 namespace admin return defaulturlconfrequest urlconf getattrrequest urlconf settings rooturlconf if isinstanceurlconf types moduletype urlconf urlconf name with builtintemplatepathtechnical404 html openencodingutf8 as fh t debugengine fromstringfh read reporterfilter getdefaultexceptionreporterfilter c context urlconf urlconf rooturlconf settings rooturlconf requestpath errorurl urlpatterns tried resolved resolved reason strexception request request settings reporterfilter getsafesettings raisingviewname getcallerrequest return httpresponsenotfoundt renderc def defaulturlconfrequest minimal django templates engine to render the error templates regardless of the project s templates setting templates are read directly from the filesystem so that the error handler works even if the template loader is broken return a path to a builtin template avoid calling this function at the module level or in a class definition because __file__ may not exist e g in frozen environments object to wrap callable appearing in settings not to call in the debug page 21345 not to break the debug page if the callable forbidding to set attributes 23070 create a technical server error response the last three arguments are the values returned from sys exc_info and friends instantiate the default filter for the first time and cache it use annotations made by the sensitive_post_parameters and sensitive_variables decorators to filter out sensitive information cleanse an individual setting key value of sensitive content if the value is a dictionary recursively cleanse the keys in that dictionary return a dictionary of the settings module with values of sensitive settings replaced with stars return a dictionary of request meta with sensitive values redacted return a dictionary of request cookies with sensitive values redacted this filter is to add safety in production environments i e debug is false if debug is true then your site is not safe anyway this hook is provided as a convenience to easily activate or deactivate the filter on a per request basis replace the keys in a multivaluedict marked as sensitive with stars this mitigates leaking sensitive post parameters if something like request post nonexistent_key throws an exception 21098 replace the values of post parameters marked as sensitive with stars cleanse all parameters cleanse only the specified parameters if value is lazy or a complex object of another kind this check might raise an exception isinstance checks that lazy multivaluedicts will have a return value cleanse multivaluedicts request post is the one we usually care about replace the values of variables marked as sensitive with stars coroutines don t have a proper f_back so they need to be inspected separately handle this by stashing the registered sensitive variables in a global dict indexed by hash file_path line_number loop through the frame s callers to see if the sensitive_variables decorator was used the sensitive_variables decorator was used so take note of the sensitive variables names cleanse all variables cleanse specified variables potentially cleanse the request and any multivaluedicts if they are one of the frame variables for good measure obfuscate the decorated function s arguments in the sensitive_variables decorator s frame in case the variables associated with those arguments were meant to be obfuscated from the decorated function s frame organize and coordinate reporting on exceptions return an absolute uri from variables available in this request skip allowed hosts protection so may return insecure uri return a dictionary containing traceback information trim large blobs of data request user may raise operationalerror if the database is unavailable for example check whether exception info is available return html version of debug 500 http error page return plain text version of debug 500 http error page return context_lines before and after lineno from file return pre_context_lineno pre_context context_line post_context if we just read the source from a file or if the loader did not apply tokenize detect_encoding to decode the source into a string then we should do that ourselves file coding may be specified match pattern from pep 263 https www python org dev peps pep 0263 get the exception and all its causes avoid infinite loop if there s a cyclic reference 29393 no exceptions were supplied to exceptionreporter in case there s just one exception take the traceback from self tb support for __traceback_hide__ which is used by a few libraries to hide internal frames create a technical 404 error response exception is the http404 trim leading slash empty urlconf default urlconf create an empty urlconf 404 error response
import functools
import inspect
import itertools
import re
import sys
import types
import warnings
from pathlib import Path

from django.conf import settings
from django.http import Http404, HttpResponse, HttpResponseNotFound
from django.template import Context, Engine, TemplateDoesNotExist
from django.template.defaultfilters import pprint
from django.urls import resolve
from django.utils import timezone
from django.utils.datastructures import MultiValueDict
from django.utils.encoding import force_str
from django.utils.module_loading import import_string
from django.utils.regex_helper import _lazy_re_compile
from django.utils.version import PY311, get_docs_version
from django.views.decorators.debug import coroutine_functions_to_sensitive_variables





DEBUG_ENGINE = Engine(
    debug=True,
    libraries={"i18n": "django.templatetags.i18n"},
)


def builtin_template_path(name):
    
    return Path(__file__).parent / "templates" / name


class ExceptionCycleWarning(UserWarning):
    pass


class CallableSettingWrapper:
    

    def __init__(self, callable_setting):
        self._wrapped = callable_setting

    def __repr__(self):
        return repr(self._wrapped)


def technical_500_response(request, exc_type, exc_value, tb, status_code=500):
    
    reporter = get_exception_reporter_class(request)(request, exc_type, exc_value, tb)
    if request.accepts("text/html"):
        html = reporter.get_traceback_html()
        return HttpResponse(html, status=status_code)
    else:
        text = reporter.get_traceback_text()
        return HttpResponse(
            text, status=status_code, content_type="text/plain; charset=utf-8"
        )


@functools.lru_cache
def get_default_exception_reporter_filter():
    
    return import_string(settings.DEFAULT_EXCEPTION_REPORTER_FILTER)()


def get_exception_reporter_filter(request):
    default_filter = get_default_exception_reporter_filter()
    return getattr(request, "exception_reporter_filter", default_filter)


def get_exception_reporter_class(request):
    default_exception_reporter_class = import_string(
        settings.DEFAULT_EXCEPTION_REPORTER
    )
    return getattr(
        request, "exception_reporter_class", default_exception_reporter_class
    )


def get_caller(request):
    resolver_match = request.resolver_match
    if resolver_match is None:
        try:
            resolver_match = resolve(request.path)
        except Http404:
            pass
    return "" if resolver_match is None else resolver_match._func_path


class SafeExceptionReporterFilter:
    

    cleansed_substitute = "********************"
    hidden_settings = _lazy_re_compile(
        "API|TOKEN|KEY|SECRET|PASS|SIGNATURE|HTTP_COOKIE", flags=re.I
    )

    def cleanse_setting(self, key, value):
        
        if key == settings.SESSION_COOKIE_NAME:
            is_sensitive = True
        else:
            try:
                is_sensitive = self.hidden_settings.search(key)
            except TypeError:
                is_sensitive = False

        if is_sensitive:
            cleansed = self.cleansed_substitute
        elif isinstance(value, dict):
            cleansed = {k: self.cleanse_setting(k, v) for k, v in value.items()}
        elif isinstance(value, list):
            cleansed = [self.cleanse_setting("", v) for v in value]
        elif isinstance(value, tuple):
            cleansed = tuple([self.cleanse_setting("", v) for v in value])
        else:
            cleansed = value

        if callable(cleansed):
            cleansed = CallableSettingWrapper(cleansed)

        return cleansed

    def get_safe_settings(self):
        
        settings_dict = {}
        for k in dir(settings):
            if k.isupper():
                settings_dict[k] = self.cleanse_setting(k, getattr(settings, k))
        return settings_dict

    def get_safe_request_meta(self, request):
        
        if not hasattr(request, "META"):
            return {}
        return {k: self.cleanse_setting(k, v) for k, v in request.META.items()}

    def get_safe_cookies(self, request):
        
        if not hasattr(request, "COOKIES"):
            return {}
        return {k: self.cleanse_setting(k, v) for k, v in request.COOKIES.items()}

    def is_active(self, request):
        
        return settings.DEBUG is False

    def get_cleansed_multivaluedict(self, request, multivaluedict):
        
        sensitive_post_parameters = getattr(request, "sensitive_post_parameters", [])
        if self.is_active(request) and sensitive_post_parameters:
            multivaluedict = multivaluedict.copy()
            for param in sensitive_post_parameters:
                if param in multivaluedict:
                    multivaluedict[param] = self.cleansed_substitute
        return multivaluedict

    def get_post_parameters(self, request):
        
        if request is None:
            return {}
        else:
            sensitive_post_parameters = getattr(
                request, "sensitive_post_parameters", []
            )
            if self.is_active(request) and sensitive_post_parameters:
                cleansed = request.POST.copy()
                if sensitive_post_parameters == "__ALL__":
                    
                    for k in cleansed:
                        cleansed[k] = self.cleansed_substitute
                    return cleansed
                else:
                    
                    for param in sensitive_post_parameters:
                        if param in cleansed:
                            cleansed[param] = self.cleansed_substitute
                    return cleansed
            else:
                return request.POST

    def cleanse_special_types(self, request, value):
        try:
            
            
            
            is_multivalue_dict = isinstance(value, MultiValueDict)
        except Exception as e:
            return "{!r} while evaluating {!r}".format(e, value)

        if is_multivalue_dict:
            
            value = self.get_cleansed_multivaluedict(request, value)
        return value

    def get_traceback_frame_variables(self, request, tb_frame):
        
        sensitive_variables = None

        
        
        
        if (
            tb_frame.f_code.co_flags & inspect.CO_COROUTINE != 0
            and tb_frame.f_code.co_name != "sensitive_variables_wrapper"
        ):
            key = hash(
                f"{tb_frame.f_code.co_filename}:{tb_frame.f_code.co_firstlineno}"
            )
            sensitive_variables = coroutine_functions_to_sensitive_variables.get(
                key, None
            )

        if sensitive_variables is None:
            
            
            current_frame = tb_frame
            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
                ):
                    
                    
                    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__":
                
                for name in tb_frame.f_locals:
                    cleansed[name] = self.cleansed_substitute
            else:
                
                for name, value in tb_frame.f_locals.items():
                    if name in sensitive_variables:
                        value = self.cleansed_substitute
                    else:
                        value = self.cleanse_special_types(request, value)
                    cleansed[name] = value
        else:
            
            
            for name, value in tb_frame.f_locals.items():
                cleansed[name] = self.cleanse_special_types(request, value)

        if (
            tb_frame.f_code.co_name == "sensitive_variables_wrapper"
            and "sensitive_variables_wrapper" in tb_frame.f_locals
        ):
            
            
            
            
            cleansed["func_args"] = self.cleansed_substitute
            cleansed["func_kwargs"] = self.cleansed_substitute

        return cleansed.items()


class ExceptionReporter:
    

    @property
    def html_template_path(self):
        return builtin_template_path("technical_500.html")

    @property
    def text_template_path(self):
        return builtin_template_path("technical_500.txt")

    def __init__(self, request, exc_type, exc_value, tb, is_email=False):
        self.request = request
        self.filter = get_exception_reporter_filter(self.request)
        self.exc_type = exc_type
        self.exc_value = exc_value
        self.tb = tb
        self.is_email = is_email

        self.template_info = getattr(self.exc_value, "template_debug", None)
        self.template_does_not_exist = False
        self.postmortem = None

    def _get_raw_insecure_uri(self):
        
        return "{scheme}://{host}{path}".format(
            scheme=self.request.scheme,
            host=self.request._get_raw_host(),
            path=self.request.get_full_path(),
        )

    def get_traceback_data(self):
        
        if self.exc_type and issubclass(self.exc_type, TemplateDoesNotExist):
            self.template_does_not_exist = True
            self.postmortem = self.exc_value.chain or [self.exc_value]

        frames = self.get_traceback_frames()
        for i, frame in enumerate(frames):
            if "vars" in frame:
                frame_vars = []
                for k, v in frame["vars"]:
                    v = pprint(v)
                    
                    if len(v) > 4096:
                        v = "%s… <trimmed %d bytes string>" % (v[0:4096], len(v))
                    frame_vars.append((k, v))
                frame["vars"] = frame_vars
            frames[i] = frame

        unicode_hint = ""
        if self.exc_type and issubclass(self.exc_type, UnicodeError):
            start = getattr(self.exc_value, "start", None)
            end = getattr(self.exc_value, "end", None)
            if start is not None and end is not None:
                unicode_str = self.exc_value.args[1]
                unicode_hint = force_str(
                    unicode_str[max(start - 5, 0) : min(end + 5, len(unicode_str))],
                    "ascii",
                    errors="replace",
                )
        from django import get_version

        if self.request is None:
            user_str = None
        else:
            try:
                user_str = str(self.request.user)
            except Exception:
                
                
                user_str = "[unable to retrieve the current user]"

        c = {
            "is_email": self.is_email,
            "unicode_hint": unicode_hint,
            "frames": frames,
            "request": self.request,
            "request_meta": self.filter.get_safe_request_meta(self.request),
            "request_COOKIES_items": self.filter.get_safe_cookies(self.request).items(),
            "user_str": user_str,
            "filtered_POST_items": list(
                self.filter.get_post_parameters(self.request).items()
            ),
            "settings": self.filter.get_safe_settings(),
            "sys_executable": sys.executable,
            "sys_version_info": "%d.%d.%d" % sys.version_info[0:3],
            "server_time": timezone.now(),
            "django_version_info": get_version(),
            "sys_path": sys.path,
            "template_info": self.template_info,
            "template_does_not_exist": self.template_does_not_exist,
            "postmortem": self.postmortem,
        }
        if self.request is not None:
            c["request_GET_items"] = self.request.GET.items()
            c["request_FILES_items"] = self.request.FILES.items()
            c["request_insecure_uri"] = self._get_raw_insecure_uri()
            c["raising_view_name"] = get_caller(self.request)

        
        if self.exc_type:
            c["exception_type"] = self.exc_type.__name__
        if self.exc_value:
            c["exception_value"] = str(self.exc_value)
            if exc_notes := getattr(self.exc_value, "__notes__", None):
                c["exception_notes"] = "\n" + "\n".join(exc_notes)
        if frames:
            c["lastframe"] = frames[-1]
        return c

    def get_traceback_html(self):
        
        with self.html_template_path.open(encoding="utf-8") as fh:
            t = DEBUG_ENGINE.from_string(fh.read())
        c = Context(self.get_traceback_data(), use_l10n=False)
        return t.render(c)

    def get_traceback_text(self):
        
        with self.text_template_path.open(encoding="utf-8") as fh:
            t = DEBUG_ENGINE.from_string(fh.read())
        c = Context(self.get_traceback_data(), autoescape=False, use_l10n=False)
        return t.render(c)

    def _get_source(self, filename, loader, module_name):
        source = None
        if hasattr(loader, "get_source"):
            try:
                source = loader.get_source(module_name)
            except ImportError:
                pass
            if source is not None:
                source = source.splitlines()
        if source is None:
            try:
                with open(filename, "rb") as fp:
                    source = fp.read().splitlines()
            except OSError:
                pass
        return source

    def _get_lines_from_file(
        self, filename, lineno, context_lines, loader=None, module_name=None
    ):
        
        source = self._get_source(filename, loader, module_name)
        if source is None:
            return None, [], None, []

        
        
        
        if isinstance(source[0], bytes):
            encoding = "ascii"
            for line in source[:2]:
                
                
                match = re.search(rb"coding[:=]\s*([-\w.]+)", line)
                if match:
                    encoding = match[1].decode("ascii")
                    break
            source = [str(sline, encoding, "replace") for sline in source]

        lower_bound = max(0, lineno - context_lines)
        upper_bound = lineno + context_lines

        try:
            pre_context = source[lower_bound:lineno]
            context_line = source[lineno]
            post_context = source[lineno + 1 : upper_bound]
        except IndexError:
            return None, [], None, []
        return lower_bound, pre_context, context_line, post_context

    def _get_explicit_or_implicit_cause(self, exc_value):
        explicit = getattr(exc_value, "__cause__", None)
        suppress_context = getattr(exc_value, "__suppress_context__", None)
        implicit = getattr(exc_value, "__context__", None)
        return explicit or (None if suppress_context else implicit)

    def get_traceback_frames(self):
        
        exceptions = []
        exc_value = self.exc_value
        while exc_value:
            exceptions.append(exc_value)
            exc_value = self._get_explicit_or_implicit_cause(exc_value)
            if exc_value in exceptions:
                warnings.warn(
                    "Cycle in the exception chain detected: exception '%s' "
                    "encountered again." % exc_value,
                    ExceptionCycleWarning,
                )
                
                break

        frames = []
        
        if not exceptions:
            return frames

        
        exc_value = exceptions.pop()
        tb = self.tb if not exceptions else exc_value.__traceback__
        while True:
            frames.extend(self.get_exception_traceback_frames(exc_value, tb))
            try:
                exc_value = exceptions.pop()
            except IndexError:
                break
            tb = exc_value.__traceback__
        return frames

    def get_exception_traceback_frames(self, exc_value, tb):
        exc_cause = self._get_explicit_or_implicit_cause(exc_value)
        exc_cause_explicit = getattr(exc_value, "__cause__", True)
        if tb is None:
            yield {
                "exc_cause": exc_cause,
                "exc_cause_explicit": exc_cause_explicit,
                "tb": None,
                "type": "user",
            }
        while tb is not None:
            
            
            if tb.tb_frame.f_locals.get("__traceback_hide__"):
                tb = tb.tb_next
                continue
            filename = tb.tb_frame.f_code.co_filename
            function = tb.tb_frame.f_code.co_name
            lineno = tb.tb_lineno - 1
            loader = tb.tb_frame.f_globals.get("__loader__")
            module_name = tb.tb_frame.f_globals.get("__name__") or ""
            (
                pre_context_lineno,
                pre_context,
                context_line,
                post_context,
            ) = self._get_lines_from_file(
                filename,
                lineno,
                7,
                loader,
                module_name,
            )
            if pre_context_lineno is None:
                pre_context_lineno = lineno
                pre_context = []
                context_line = "<source code not available>"
                post_context = []

            colno = tb_area_colno = ""
            if PY311:
                _, _, start_column, end_column = next(
                    itertools.islice(
                        tb.tb_frame.f_code.co_positions(), tb.tb_lasti // 2, None
                    )
                )
                if start_column and end_column:
                    underline = "^" * (end_column - start_column)
                    spaces = " " * (start_column + len(str(lineno + 1)) + 2)
                    colno = f"\n{spaces}{underline}"
                    tb_area_spaces = " " * (
                        4
                        + start_column
                        - (len(context_line) - len(context_line.lstrip()))
                    )
                    tb_area_colno = f"\n{tb_area_spaces}{underline}"
            yield {
                "exc_cause": exc_cause,
                "exc_cause_explicit": exc_cause_explicit,
                "tb": tb,
                "type": "django" if module_name.startswith("django.") else "user",
                "filename": filename,
                "function": function,
                "lineno": lineno + 1,
                "vars": self.filter.get_traceback_frame_variables(
                    self.request, tb.tb_frame
                ),
                "id": id(tb),
                "pre_context": pre_context,
                "context_line": context_line,
                "post_context": post_context,
                "pre_context_lineno": pre_context_lineno + 1,
                "colno": colno,
                "tb_area_colno": tb_area_colno,
            }
            tb = tb.tb_next


def technical_404_response(request, exception):
    
    try:
        error_url = exception.args[0]["path"]
    except (IndexError, TypeError, KeyError):
        error_url = request.path_info[1:]  

    try:
        tried = exception.args[0]["tried"]
    except (IndexError, TypeError, KeyError):
        resolved = True
        tried = request.resolver_match.tried if request.resolver_match else None
    else:
        resolved = False
        if not tried or (  
            request.path == "/"
            and len(tried) == 1
            and len(tried[0]) == 1  
            and getattr(tried[0][0], "app_name", "")
            == getattr(tried[0][0], "namespace", "")
            == "admin"
        ):
            return default_urlconf(request)

    urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF)
    if isinstance(urlconf, types.ModuleType):
        urlconf = urlconf.__name__

    with builtin_template_path("technical_404.html").open(encoding="utf-8") as fh:
        t = DEBUG_ENGINE.from_string(fh.read())
    reporter_filter = get_default_exception_reporter_filter()
    c = Context(
        {
            "urlconf": urlconf,
            "root_urlconf": settings.ROOT_URLCONF,
            "request_path": error_url,
            "urlpatterns": tried,
            "resolved": resolved,
            "reason": str(exception),
            "request": request,
            "settings": reporter_filter.get_safe_settings(),
            "raising_view_name": get_caller(request),
        }
    )
    return HttpResponseNotFound(t.render(c))


def default_urlconf(request):
    
    with builtin_template_path("default_urlconf.html").open(encoding="utf-8") as fh:
        t = DEBUG_ENGINE.from_string(fh.read())
    c = Context(
        {
            "version": get_docs_version(),
        }
    )

    return HttpResponse(t.render(c))