1
0
mirror of https://github.com/django/django.git synced 2024-12-22 17:16:24 +00:00

Documented risk of XSS vulnerability when using Postgres headlines.

Because the default start and stop parameters are <b> and </b>
respectively, it is tempting to pass the headline value to the `safe`
template filter, to render the highlighted section of the headline in
bold. This is dangerous.

Also, tested the sanitation behavior of Postgres. If the undocumented
behavior of Postgres changes in this regard, we want to ensure that
Django's code and documentation is updated appropriately.
This commit is contained in:
David D Lowe 2024-08-27 14:22:50 +01:00
parent cdcd604ef8
commit bfc83d8ff9
2 changed files with 50 additions and 0 deletions

View File

@ -218,6 +218,19 @@ Usage example:
See :ref:`postgresql-fts-search-configuration` for an explanation of the
``config`` parameter.
.. warning::
In order to prevent XSS attacks, the string value of this field is not
marked as safe. Although the ``start_sel`` and ``stop_sel`` parameters
default to ``<b>`` and ``</b>``, it is dangerous to mark the headline as
safe or to pass it to the :tfilter:`safe` template filter, especially if
the value is from an untrusted source.
Tested versions of PostgreSQL will not remove or escape HTML tags from the
headline if ``highlight_all`` is ``True``. If ``highlight_all`` is
``False``, then HTML tags will be removed from the headline. This behavior
is undocumented, and must not be relied upon.
.. _highlighting search results: https://www.postgresql.org/docs/current/textsearch-controls.html#TEXTSEARCH-HEADLINE
.. _postgresql-fts-search-configuration:

View File

@ -7,6 +7,7 @@ transcript.
"""
from django.db.models import F, Value
from django.utils.safestring import SafeString
from . import PostgreSQLSimpleTestCase, PostgreSQLTestCase
from .models import Character, Line, LineSavedSearch, Scene
@ -732,6 +733,42 @@ class SearchHeadlineTests(GrailTestData, PostgreSQLTestCase):
searched.headline,
)
def test_headline_sanitize_html(self):
dangerous_line = Line.objects.create(
scene=self.robin,
character=self.minstrel,
dialogue='Foobar <script>console.log("danger");</script>',
)
dangerous_line.full_clean()
searched = Line.objects.annotate(
headline=SearchHeadline(
"dialogue",
SearchQuery("Foobar", config="english"),
highlight_all=False,
),
).get(pk=dangerous_line.pk)
self.assertNotIsInstance(searched.headline, SafeString)
self.assertEqual(
searched.headline,
'<b>Foobar</b> console.log("danger"); ',
"When hightlight_all is False, PostgreSQL removes existing HTML tags",
)
searched = Line.objects.annotate(
headline=SearchHeadline(
"dialogue",
SearchQuery("Foobar", config="english"),
highlight_all=True,
),
).get(pk=dangerous_line.pk)
self.assertNotIsInstance(searched.headline, SafeString)
self.assertEqual(
searched.headline,
'<b>Foobar</b> <script>console.log("danger");</script>',
"When highlight_all is True, PostgreSQL riskily keeps existing HTML tags",
)
def test_headline_short_word_option(self):
self.check_default_text_search_config()
searched = Line.objects.annotate(