From 23fb76f08f9e1e78247601d5d86b6b79aece1e63 Mon Sep 17 00:00:00 2001 From: Renaud Allard Date: Fri, 13 Mar 2026 13:28:31 +0100 Subject: [PATCH] Fix XSS via unsafe rendering of untrusted external data in templates (#5826) Remove |safe filter from 6 template locations where data from external search engine APIs was rendered as raw HTML without sanitization. Jinja2 autoescape now properly escapes these fields. The |safe filter was originally added in commit 213041adc (March 2021) by copying the pattern from result.title|safe and result.content|safe. However, title and content are pre-escaped via escape() in webapp.py lines 704-706 before highlight_content() adds trusted tags for search term highlighting. The metadata, info.value, link.url_label, repository, and filename fields never go through any escaping and flow directly from external API responses to the template. Affected templates and their untrusted data sources: - macros.html: result.metadata from DuckDuckGo, Reuters, Presearch, Podcast Index, Fyyd, bpb, moviepilot, mediawiki, and others - paper.html: result.metadata from academic search engines - map.html: info.value and link.url_label from OpenStreetMap user-contributed extratags - code.html: result.repository and result.filename from GitHub API Example exploit: a search engine API returning metadata='' would execute arbitrary JavaScript in every user's browser viewing that result. --- searx/templates/simple/macros.html | 2 +- searx/templates/simple/result_templates/code.html | 4 ++-- searx/templates/simple/result_templates/map.html | 4 ++-- searx/templates/simple/result_templates/paper.html | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/searx/templates/simple/macros.html b/searx/templates/simple/macros.html index 43be87615..c3e3a9b84 100644 --- a/searx/templates/simple/macros.html +++ b/searx/templates/simple/macros.html @@ -41,7 +41,7 @@ {%- if result.length and not result.thumbnail %}
{{ _('Length') }}: {{ result.length }}
{% endif -%} {%- if result.views %}
{{ _('Views') }}: {{ result.views }}
{% endif -%} {%- if result.author %}
{{ _('Author') }}: {{ result.author }}
{% endif -%} - {%- if result.metadata %}
{{ result.metadata|safe }}
{% endif -%} + {%- if result.metadata %}
{{ result.metadata }}
{% endif -%} {%- endmacro -%} diff --git a/searx/templates/simple/result_templates/code.html b/searx/templates/simple/result_templates/code.html index 6fba99a3e..29605151d 100644 --- a/searx/templates/simple/result_templates/code.html +++ b/searx/templates/simple/result_templates/code.html @@ -11,7 +11,7 @@ {%- if result.repository -%}

{{- '' -}} {{ _('Repository') }}: {{- ' ' -}} - - {{ _('Filename') }}: {{ result.filename|safe }} + {{ _('Filename') }}: {{ result.filename }}

{% endif -%} diff --git a/searx/templates/simple/result_templates/map.html b/searx/templates/simple/result_templates/map.html index 73a2701da..fb4e8293f 100644 --- a/searx/templates/simple/result_templates/map.html +++ b/searx/templates/simple/result_templates/map.html @@ -32,10 +32,10 @@ {%- endif %} {%- for info in result.data -%} - {{ info.label }}{{ info.value|safe }} + {{ info.label }}{{ info.value }} {%- endfor -%} {%- for link in result.links -%} - {{ link.label }}{{ link.url_label|safe }} + {{ link.label }}{{ link.url_label }} {%- endfor -%} diff --git a/searx/templates/simple/result_templates/paper.html b/searx/templates/simple/result_templates/paper.html index 074ad9081..de37bd287 100644 --- a/searx/templates/simple/result_templates/paper.html +++ b/searx/templates/simple/result_templates/paper.html @@ -84,7 +84,7 @@ {%- endif -%} {%- if result.metadata %} -
{{ result.metadata|safe }}
+
{{ result.metadata }}
{% endif -%}