Fixed #30521 -- Fixed invalid HTML in default error pages.

This commit is contained in:
Alexandre Varas 2019-06-05 20:12:21 +02:00 committed by Mariusz Felisiak
parent 3fb0a1a67f
commit c498f088c5
2 changed files with 44 additions and 7 deletions

View File

@ -11,6 +11,17 @@ ERROR_404_TEMPLATE_NAME = '404.html'
ERROR_403_TEMPLATE_NAME = '403.html' ERROR_403_TEMPLATE_NAME = '403.html'
ERROR_400_TEMPLATE_NAME = '400.html' ERROR_400_TEMPLATE_NAME = '400.html'
ERROR_500_TEMPLATE_NAME = '500.html' ERROR_500_TEMPLATE_NAME = '500.html'
ERROR_PAGE_TEMPLATE = """
<!doctype html>
<html lang="en">
<head>
<title>%(title)s</title>
</head>
<body>
<h1>%(title)s</h1><p>%(details)s</p>
</body>
</html>
"""
# This can be called when CsrfViewMiddleware.process_view has not run, # This can be called when CsrfViewMiddleware.process_view has not run,
@ -55,8 +66,11 @@ def page_not_found(request, exception, template_name=ERROR_404_TEMPLATE_NAME):
# Render template (even though there are no substitutions) to allow # Render template (even though there are no substitutions) to allow
# inspecting the context in tests. # inspecting the context in tests.
template = Engine().from_string( template = Engine().from_string(
'<h1>Not Found</h1>' ERROR_PAGE_TEMPLATE % {
'<p>The requested resource was not found on this server.</p>') 'title': 'Not Found',
'details': 'The requested resource was not found on this server.',
},
)
body = template.render(Context(context)) body = template.render(Context(context))
content_type = 'text/html' content_type = 'text/html'
return HttpResponseNotFound(body, content_type=content_type) return HttpResponseNotFound(body, content_type=content_type)
@ -76,7 +90,10 @@ def server_error(request, template_name=ERROR_500_TEMPLATE_NAME):
if template_name != ERROR_500_TEMPLATE_NAME: if template_name != ERROR_500_TEMPLATE_NAME:
# Reraise if it's a missing custom template. # Reraise if it's a missing custom template.
raise raise
return HttpResponseServerError('<h1>Server Error (500)</h1>', content_type='text/html') return HttpResponseServerError(
ERROR_PAGE_TEMPLATE % {'title': 'Server Error (500)', 'details': ''},
content_type='text/html',
)
return HttpResponseServerError(template.render()) return HttpResponseServerError(template.render())
@ -94,7 +111,10 @@ def bad_request(request, exception, template_name=ERROR_400_TEMPLATE_NAME):
if template_name != ERROR_400_TEMPLATE_NAME: if template_name != ERROR_400_TEMPLATE_NAME:
# Reraise if it's a missing custom template. # Reraise if it's a missing custom template.
raise raise
return HttpResponseBadRequest('<h1>Bad Request (400)</h1>', content_type='text/html') return HttpResponseBadRequest(
ERROR_PAGE_TEMPLATE % {'title': 'Bad Request (400)', 'details': ''},
content_type='text/html',
)
# No exception content is passed to the template, to not disclose any sensitive information. # No exception content is passed to the template, to not disclose any sensitive information.
return HttpResponseBadRequest(template.render()) return HttpResponseBadRequest(template.render())
@ -119,7 +139,10 @@ def permission_denied(request, exception, template_name=ERROR_403_TEMPLATE_NAME)
if template_name != ERROR_403_TEMPLATE_NAME: if template_name != ERROR_403_TEMPLATE_NAME:
# Reraise if it's a missing custom template. # Reraise if it's a missing custom template.
raise raise
return HttpResponseForbidden('<h1>403 Forbidden</h1>', content_type='text/html') return HttpResponseForbidden(
ERROR_PAGE_TEMPLATE % {'title': '403 Forbidden', 'details': ''},
content_type='text/html',
)
return HttpResponseForbidden( return HttpResponseForbidden(
template.render(request=request, context={'exception': str(exception)}) template.render(request=request, context={'exception': str(exception)})
) )

View File

@ -81,8 +81,7 @@ class DefaultsTests(TestCase):
def test_bad_request(self): def test_bad_request(self):
request = self.request_factory.get('/') request = self.request_factory.get('/')
response = bad_request(request, Exception()) response = bad_request(request, Exception())
self.assertEqual(response.status_code, 400) self.assertContains(response, b'<h1>Bad Request (400)</h1>', status_code=400)
self.assertEqual(response.content, b'<h1>Bad Request (400)</h1>')
@override_settings(TEMPLATES=[{ @override_settings(TEMPLATES=[{
'BACKEND': 'django.template.backends.django.DjangoTemplates', 'BACKEND': 'django.template.backends.django.DjangoTemplates',
@ -134,3 +133,18 @@ class DefaultsTests(TestCase):
with self.assertRaises(TemplateDoesNotExist): with self.assertRaises(TemplateDoesNotExist):
server_error(request, template_name='nonexistent') server_error(request, template_name='nonexistent')
def test_error_pages(self):
request = self.request_factory.get('/')
for response, title in (
(bad_request(request, Exception()), b'Bad Request (400)'),
(permission_denied(request, Exception()), b'403 Forbidden'),
(page_not_found(request, Http404()), b'Not Found'),
(server_error(request), b'Server Error (500)'),
):
with self.subTest(title=title):
self.assertIn(b'<!doctype html>', response.content)
self.assertIn(b'<html lang="en">', response.content)
self.assertIn(b'<head>', response.content)
self.assertIn(b'<title>%s</title>' % title, response.content)
self.assertIn(b'<body>', response.content)