diff --git a/netbox/extras/templatetags/dashboard.py b/netbox/extras/templatetags/dashboard.py index 84c98a7ef..56ae80818 100644 --- a/netbox/extras/templatetags/dashboard.py +++ b/netbox/extras/templatetags/dashboard.py @@ -1,4 +1,5 @@ from django import template +from django.utils.html import escape from django.utils.safestring import mark_safe from django.utils.translation import gettext as _ @@ -19,6 +20,6 @@ def render_widget(context, widget): {message1}

-

{e}

+

{escape(e)}

{message2}

""") diff --git a/netbox/extras/tests/test_dashboard.py b/netbox/extras/tests/test_dashboard.py index 866b46477..529ab5f9e 100644 --- a/netbox/extras/tests/test_dashboard.py +++ b/netbox/extras/tests/test_dashboard.py @@ -1,6 +1,7 @@ -from django.test import TestCase, tag +from django.test import RequestFactory, TestCase, tag from extras.dashboard.widgets import ObjectListWidget +from extras.templatetags.dashboard import render_widget class ObjectListWidgetTestCase(TestCase): @@ -46,3 +47,32 @@ class ObjectListWidgetTestCase(TestCase): widget = ObjectListWidget(id='2829fd9b-5dee-4c9a-81f2-5bd84c350a27', **config) rendered = widget.render(mock_request) self.assertTrue('Unable to load content. Could not resolve list URL for:' in rendered) + + +class RenderWidgetTemplateTagTestCase(TestCase): + + def _make_context(self): + request = RequestFactory().get('/') + return {'request': request} + + def test_render_widget_escapes_exception_html(self): + """Exception text with HTML special chars must be escaped, not rendered as markup.""" + + class BrokenWidget: + def render(self, request): + raise Exception('') + + output = render_widget(self._make_context(), BrokenWidget()) + self.assertIn('<script>', output) + self.assertNotIn('