GitHub Advisoryadvisory · PyPI

GHSA-v5mq-3xhg-98m9 unstructured: Stored XSS via unescaped HTML generation

MEDIUM Published 2026-10-15 Modified 2026-10-15 Official page ↗
Aliases
CVE-2026-104250
Ecosystem
PyPI
Packages
unstructured
Severity
Moderate
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
Weaknesses
CWE-116, CWE-79
Reviewed
2026-10-15

Affected packages

• PyPI unstructured: < 0.24.1 → fixed in 0.24.1

Description

Summary When `unstructured` parses an untrusted document and outputs the result as HTML, malicious markup contained in the document (e.g. ``) is left in the output HTML without escaping. When that output is displayed in a browser, the script embedded in the document executes in the viewer's browser. Details `partition_html(html_parser_version="v2")` (and the VLM partitioner) preserve the input document's element tags, attributes, and text into `metadata.text_as_html`, and `elements_to_html()` outputs that as an HTML document. The function that builds HTML on this path, `to_html()` in `unstructured/documents/ontology.py`, performs no escaping at all. As a result, event handlers such as `onerror` / `onmouseover` / `onload` originating from the input document are left in the output HTML as-is (literal `` tags are stripped by the parser, but attribute-based vectors and `javascript:` URLs are not). In addition, link URLs are not scheme-filtered, so `` is also emitted. Relevant source: `documents/ontology.py` (`to_html`, `_construct_attribute_string`, `_generate_final_html`); `partition/html/transformations.py` (`text_as_html = ontology_element.to_html()`); `partition/html/convert.py` (`elements_to_html`). PoC Environment: `downloads.unstructured.io/unstructured-io/unstructured:latest`, `unstructured` 0.23.3. ```python from unstructured.partition.html import partition_html from unstructured.partition.html.convert import elements_to_html malicious = ('' 'hello' '' 'click me' '') els = partition_html(text=malicious, html_parser_version="v2") open("xss_output.html", "w").write(elements_to_html(list(els), no_group_by_page=True)) ``` `elements_to_html()` output body — the unescaped vectors survive: ```html hello click me ``` Opening `xss_output.html` in a browser fires `alert` automatically (via ``), with no interaction. Per-vector confirmation (each payload labelled, each verified in a browser): | Vector | Trigger | Cause | |---|---|---| | `` | open file (auto) | element `text` not escaped | | `` (via `title='">'`) | open file (auto) | attribute value not escaped (`"` breakout) | | `` | click | link URL scheme not filtered | | `onmouseover` | hover | source attribute preserved as-is | Impact Stored XSS caused by improper output encoding. Anyone who renders the HTML or `text_as_html` generated from an untrusted document is affected. In an ingestion pipeline where uploaded documents are parsed and a staff member reviews their contents in a web UI, a low-privileged or external submitter can inject malicious HTML; when a privileged reviewer later opens the parsed result, `` fires in their session with no interaction, leading to cross-record information disclosure and privileged actions.

Related

Alias · CVE (1)

  • CVE-2026-104250 The unstructured library provides open-source components for ingesting and pre-processing images and text documents, such as PDFs, HTML,… vulnerability

Weakness · CWE (2)

  • CWE-116 Improper Encoding or Escaping of Output weakness
  • CWE-79 Improper Neutralization of Input During Web Page Generation weakness

References