Skip to content

Watson search indexer can fail with SuspiciousOperationException #14649

@valentijnscholten

Description

@valentijnscholten

https://www.djangoproject.com/weblog/2025/may/07/security-releases/

CVE-2025-32873: Denial-of-service possibility in strip_tags()
django.utils.html.strip_tags() would be slow to evaluate certain inputs containing large sequences of incomplete HTML tags. This function is used to implement the striptags template filter, which was thus also vulnerable. django.utils.html.strip_tags() now raises a SuspiciousOperation exception if it encounters an unusually large number of unclosed opening tags.

uwsgi-1         | [05/Apr/2026 07:43:30] ERROR [dojo.tasks:301] Watson async index update failed for finding: 
uwsgi-1         | [05/Apr/2026 07:43:30] ERROR [django.security.SuspiciousOperation:253] 
uwsgi-1         | Traceback (most recent call last):
uwsgi-1         |   File "/usr/local/lib/python3.13/site-packages/django/core/handlers/exception.py", line 55, in inner
uwsgi-1         |     response = get_response(request)
uwsgi-1         |   File "/usr/local/lib/python3.13/site-packages/django/utils/deprecation.py", line 122, in __call__
uwsgi-1         |     response = self.process_response(request, response)
uwsgi-1         |   File "/usr/local/lib/python3.13/site-packages/watson/middleware.py", line 37, in process_response
uwsgi-1         |     self._close_search_context(request)
uwsgi-1         |     ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
uwsgi-1         |   File "/app/dojo/middleware.py", line 294, in _close_search_context
uwsgi-1         |     self._trigger_async_index_update(captured_tasks)
uwsgi-1         |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
uwsgi-1         |   File "/app/dojo/middleware.py", line 340, in _trigger_async_index_update
uwsgi-1         |     update_watson_search_index_for_model(model_name, batch)
uwsgi-1         |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^
uwsgi-1         |   File "/usr/local/lib/python3.13/site-packages/celery/local.py", line 182, in __call__
uwsgi-1         |     return self._get_current_object()(*a, **kw)
uwsgi-1         |            ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
uwsgi-1         |   File "/app/dojo/celery.py", line 102, in __call__
uwsgi-1         |     return super().__call__(*args, **kwargs)
uwsgi-1         |            ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
uwsgi-1         |   File "/app/dojo/celery.py", line 43, in __call__
uwsgi-1         |     return super().__call__(*args, **kwargs)
uwsgi-1         |            ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
uwsgi-1         |   File "/usr/local/lib/python3.13/site-packages/celery/app/task.py", line 411, in __call__
uwsgi-1         |     return self.run(*args, **kwargs)
uwsgi-1         |            ~~~~~~~~^^^^^^^^^^^^^^^^^
uwsgi-1         |   File "/app/dojo/tasks.py", line 296, in update_watson_search_index_for_model
uwsgi-1         |     context_manager.end()
uwsgi-1         |     ~~~~~~~~~~~~~~~~~~~^^
uwsgi-1         |   File "/usr/local/lib/python3.13/site-packages/watson/search.py", line 261, in end
uwsgi-1         |     list(chain.from_iterable(engine._update_obj_index_iter(obj)
uwsgi-1         |     ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
uwsgi-1         |                              for engine, obj in tasks)
uwsgi-1         |                              ^^^^^^^^^^^^^^^^^^^^^^^^^
uwsgi-1         |          )
uwsgi-1         |          ^
uwsgi-1         |   File "/usr/local/lib/python3.13/site-packages/watson/search.py", line 505, in _update_obj_index_iter
uwsgi-1         |     "content": adapter.get_content(obj),
uwsgi-1         |                ~~~~~~~~~~~~~~~~~~~^^^^^
uwsgi-1         |   File "/usr/local/lib/python3.13/site-packages/watson/search.py", line 147, in get_content
uwsgi-1         |     return self.prepare_content(" ".join(
uwsgi-1         |            ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
uwsgi-1         |         force_str(self._resolve_field(obj, field_name))
uwsgi-1         |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
uwsgi-1         |         for field_name in field_names
uwsgi-1         |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
uwsgi-1         |     ))
uwsgi-1         |     ^^
uwsgi-1         |   File "/usr/local/lib/python3.13/site-packages/watson/search.py", line 101, in prepare_content
uwsgi-1         |     content = strip_tags(content)
uwsgi-1         |   File "/usr/local/lib/python3.13/site-packages/django/utils/functional.py", line 231, in wrapper
uwsgi-1         |     return func(*args, **kwargs)
uwsgi-1         |   File "/usr/local/lib/python3.13/site-packages/django/utils/html.py", line 221, in strip_tags
uwsgi-1         |     raise SuspiciousOperation
uwsgi-1         | django.core.exceptions.SuspiciousOperation
uwsgi-1         | Traceback (most recent call last):
uwsgi-1         |   File "/usr/local/lib/python3.13/site-packages/django/http/response.py", line 340, in close
uwsgi-1         |     signals.request_finished.send(sender=self._handler_class)
uwsgi-1         |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^
uwsgi-1         |   File "/usr/local/lib/python3.13/site-packages/django/dispatch/dispatcher.py", line 189, in send
uwsgi-1         |     response = receiver(signal=self, sender=sender, **named)
uwsgi-1         |   File "/usr/local/lib/python3.13/site-packages/watson/search.py", line 293, in _request_finished_receiver
uwsgi-1         |     self.end()
uwsgi-1         |     ~~~~~~~~^^
uwsgi-1         |   File "/usr/local/lib/python3.13/site-packages/watson/search.py", line 261, in end
uwsgi-1         |     list(chain.from_iterable(engine._update_obj_index_iter(obj)
uwsgi-1         |     ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
uwsgi-1         |                              for engine, obj in tasks)
uwsgi-1         |                              ^^^^^^^^^^^^^^^^^^^^^^^^^
uwsgi-1         |          )
uwsgi-1         |          ^
uwsgi-1         |   File "/usr/local/lib/python3.13/site-packages/watson/search.py", line 505, in _update_obj_index_iter
uwsgi-1         |     "content": adapter.get_content(obj),
uwsgi-1         |                ~~~~~~~~~~~~~~~~~~~^^^^^
uwsgi-1         |   File "/usr/local/lib/python3.13/site-packages/watson/search.py", line 147, in get_content
uwsgi-1         |     return self.prepare_content(" ".join(
uwsgi-1         |            ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
uwsgi-1         |         force_str(self._resolve_field(obj, field_name))
uwsgi-1         |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
uwsgi-1         |         for field_name in field_names
uwsgi-1         |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
uwsgi-1         |     ))
uwsgi-1         |     ^^
uwsgi-1         |   File "/usr/local/lib/python3.13/site-packages/watson/search.py", line 101, in prepare_content
uwsgi-1         |     content = strip_tags(content)
uwsgi-1         |   File "/usr/local/lib/python3.13/site-packages/django/utils/functional.py", line 231, in wrapper
uwsgi-1         |     return func(*args, **kwargs)
uwsgi-1         |   File "/usr/local/lib/python3.13/site-packages/django/utils/html.py", line 221, in strip_tags
uwsgi-1         |     raise SuspiciousOperation
uwsgi-1         | django.core.exceptions.SuspiciousOperation
uwsgi-1         | [pid: 35|app: -|req: -/-] 172.19.0.1 (admin) {40 vars in 740 bytes} [Sun Apr  5 07:43:28 2026] POST /api/v2/import-scan/ => generated 50187 bytes in 2690 msecs (HTTP/1.1 400) 8 headers in 485 bytes (1 switches on core 0)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions