Files
searxng/searx/templates/simple/result_templates/paper.html
Renaud Allard 23fb76f08f 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 <span> 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='<img src=x onerror=alert(document.cookie)>' would execute
arbitrary JavaScript in every user's browser viewing that result.
2026-03-13 13:28:31 +01:00

97 lines
2.7 KiB
HTML

{% from 'simple/macros.html' import result_header, result_sub_header, result_sub_footer, result_footer, result_link with context %}
{{ result_header(result, favicons, image_proxify) }}
<div class="attributes">
{%- if result.date_of_publication %}
<div>
<span>{{ _("Published date") }}:</span>
<span>{{ result.date_of_publication.l10n_date("long", "UI") }}</span>
</div>
{% endif -%}
{%- if result.authors %}
<div>
<span>{{ _("Author") }}:</span>
<span>{{ result.authors | join(", ") }}</span>
</div>
{% endif -%}
{%- if result.journal -%}
<div>
<span>{{- _("Journal") }}:</span>
<span>{{ result.journal -}}
{%- if result.volume -%}
&nbsp;{{- result.volume -}}
{%- if result.number -%}.{{- result.number -}}{%- endif -%}
{%- endif -%}
{%- if result.pages -%}&nbsp;{{- result.pages -}}{%- endif -%}
</span>
</div>
{%- endif %}
{%- if result.editor %}
<div>
<span>{{ _("Editor") }}:</span>
<span>{{ result.editor }}</span>
</div>
{% endif -%}
{%- if result.publisher %}
<div>
<span>{{ _("Publisher") }}:</span>
<span>{{ result.publisher }}</span>
</div>
{% endif -%}
{%- if result.type %}
<div>
<span>{{ _("Type") }}:</span>
<span>{{ result.type }}</span>
</div>
{% endif -%}
{%- if result.tags %}
<div>
<span>{{ _("Tags") }}:</span>
<span>{{ result.tags | join(", ")}}</span>
</div>
{%- endif -%}
{%- if result.doi %}
<div>
<span>{{ _("DOI") }}:</span>
<span>{{ result_link(doi_resolver + result.doi, result.doi) }}</span>
</div>
{% endif -%}
{%- if result.issn %}
<div>
<span>{{ _("ISSN") }}:</span>
<span>{{ result.issn | join(", ") }}</span>
</div>
{% endif -%}
{%- if result.isbn %}
<div class="result_isbn">
<span>{{ _("ISBN") }}:</span>
<span>{{ result.isbn | join(", ") }}</span>
</div>
{% endif -%}
{%- if result.views %}
<div>
<span>{{ _('Views') }}:</span>
<span>{{ result.views }}</span>
</div>
{% endif -%}
</div>
{%- if result.content -%}
<p class="content">{{- result.content | safe -}}</p>
{%- endif -%}
{%- if result.comments -%}
<p class="comments">{{- result.comments -}}</p>
{%- endif -%}
{%- if result.metadata %}
<div class="highlight">{{ result.metadata }}</div>
{% endif -%}
<p class="altlink">
{%- if result.pdf_url -%}{{ result_link(result.pdf_url, _('PDF')) }}{%- endif -%}
{%- if result.html_url -%}{{ result_link(result.html_url, _('HTML')) }}{%- endif -%}
{%- if result.doi %}{{ result_link('https://www.altmetric.com/details/doi/' + result.doi, 'Altmetric') }}{% endif -%}
</p>
{{- result_sub_footer(result) -}}
{{- result_footer(result) }}