From 6ec79402cc21e9d3e1fed539f5483204028ae39d Mon Sep 17 00:00:00 2001 From: bctiemann Date: Fri, 10 Jul 2026 11:26:40 -0400 Subject: [PATCH] Closes #22657: escape exception message in render_widget before mark_safe (#22658) --- netbox/extras/templatetags/dashboard.py | 3 ++- netbox/extras/tests/test_dashboard.py | 32 ++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) 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('