Every CVE Search/ Get API key

CVE-2026-104250

unstructured: Stored XSS via unescaped HTML generation

6.1 MediumFix available

The unstructured library provides open-source components for ingesting and pre-processing images and text documents, such as PDFs, HTML, Word docs, and many more. Prior to 0.24.1, partition_html(html_parser_version="v2") and the VLM partitioner can preserve untrusted document tags, attributes, text, and link targets in metadata.text_as_html, while elements_to_html() emits the result as an HTML document. The to_html() path in unstructured/documents/ontology.py does not escape output or reject unsafe URL schemes, allowing event-handler attributes, attribute breakout markup, and javascript: links to survive parsing. When the generated HTML or text_as_html is rendered in a browser, attacker-controlled script can execute in the viewer's origin, including in document-ingestion workflows where a privileged reviewer opens submitted content. This issue is fixed in version 0.24.1.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
None
User interaction
Required

What an attacker can affect

Confidentiality
Low
Integrity
Low
Availability
None

Exploit likelihood

0.23%Unchanged since Oct 14, 2026
90%50%10%1%

What each source reports

CVE List Score used 6.1

unstructured: Stored XSS via unescaped HTML generation

The unstructured library provides open-source components for ingesting and pre-processing images and text documents, such as PDFs, HTML, Word docs, and many more. Prior to 0.24.1, partition_html(html_parser_version="v2") and the VLM partitioner can preserve untrusted document tags, attributes, text, and link targets in metadata.text_as_html, while elements_to_html() emits the result as an HTML document. The to_html() path in unstructured/documents/ontology.py does not escape output or reject unsafe URL schemes, allowing event-handler attributes, attribute breakout markup, and javascript: links to survive parsing. When the generated HTML or text_as_html is rendered in a browser, attacker-controlled script can execute in the viewer's origin, including in document-ingestion workflows where a privileged reviewer opens submitted content. This issue is fixed in version 0.24.1.

Record
CVE-2026-104250
CVSS 3.1
6.1 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
Weakness
CWE-79, CWE-116
Updated
2026-11-02
EUVDEUVD-2026-98918 6.1

The unstructured library provides open-source components for ingesting and pre-processing images and text documents, such as PDFs, HTML, Word docs, and many more. Prior to 0.24.1, partition_html(html_parser_version="v2") and the VLM partitioner can preserve untrusted document tags, attributes, text, and link targets in metadata.text_as_html, while elements_to_html() emits the result as an HTML document. The to_html() path in unstructured/documents/ontology.py does not escape output or reject unsafe URL schemes, allowing event-handler attributes, attribute breakout markup, and javascript: links to survive parsing. When the generated HTML or text_as_html is rendered in a browser, attacker-controlled script can execute in the viewer's origin, including in document-ingestion workflows where a privileged reviewer opens submitted content. This issue is fixed in version 0.24.1.

Record
EUVD-2026-98918
CVSS 3.1
6.1 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-v5mq-3xhg-98m9 6.1

unstructured: Stored XSS via unescaped HTML generation

Summary

When unstructured parses an untrusted document and outputs the result as HTML, malicious markup contained in the document (e.g. <img src=x onerror=...>) 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 <script> tags are stripped by the parser, but attribute-based vectors and javascript: URLs are not). In addition, link URLs are not scheme-filtered, so <a href="javascript:..."> 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.

from unstructured.partition.html import partition_html
from unstructured.partition.html.convert import elements_to_html

malicious = ('<body class="Document">'
             '<p class="Paragraph" onmouseover="alert(1)">hello'
             '<img src=x onerror="alert(document.domain)"></p>'
             '<a class="Hyperlink" href="javascript:alert(document.cookie)">click me</a>'
             '</body>')
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:

<p class="NarrativeText" id="..." onmouseover="alert(1)">
  <p class="Paragraph">hello</p>
  <img class="Image" onerror="alert(document.domain)" src="x"/>
</p>
<a class="UncategorizedText" href="javascript:alert(document.cookie)" id="...">click me</a>

Opening xss_output.html in a browser fires alert automatically (via <img onerror>), with no interaction. Per-vector confirmation (each payload labelled, each verified in a browser):

| Vector | Trigger | Cause | |---|---|---| | <img onerror> | open file (auto) | element text not escaped | | <svg onload> (via title='"><svg ...>') | open file (auto) | attribute value not escaped (" breakout) | | <a href=javascript:> | 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, <img onerror> fires in their session with no interaction, leading to cross-record information disclosure and privileged actions.

Record
GHSA-v5mq-3xhg-98m9
CVSS 3.1
6.1 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
Weakness
CWE-79, CWE-116
Updated
2026-11-02
NVD 6.1

The unstructured library provides open-source components for ingesting and pre-processing images and text documents, such as PDFs, HTML, Word docs, and many more. Prior to 0.24.1, partition_html(html_parser_version="v2") and the VLM partitioner can preserve untrusted document tags, attributes, text, and link targets in metadata.text_as_html, while elements_to_html() emits the result as an HTML document. The to_html() path in unstructured/documents/ontology.py does not escape output or reject unsafe URL schemes, allowing event-handler attributes, attribute breakout markup, and javascript: links to survive parsing. When the generated HTML or text_as_html is rendered in a browser, attacker-controlled script can execute in the viewer's origin, including in document-ingestion workflows where a privileged reviewer opens submitted content. This issue is fixed in version 0.24.1.

Record
CVE-2026-104250
CVSS 3.1
6.1 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
Weakness
CWE-79, CWE-116
Updated
2026-11-02
OSV 6.1

unstructured: Stored XSS via unescaped HTML generation

The unstructured library provides open-source components for ingesting and pre-processing images and text documents, such as PDFs, HTML, Word docs, and many more. Prior to 0.24.1, partition_html(html_parser_version="v2") and the VLM partitioner can preserve untrusted document tags, attributes, text, and link targets in metadata.text_as_html, while elements_to_html() emits the result as an HTML document. The to_html() path in unstructured/documents/ontology.py does not escape output or reject unsafe URL schemes, allowing event-handler attributes, attribute breakout markup, and javascript: links to survive parsing. When the generated HTML or text_as_html is rendered in a browser, attacker-controlled script can execute in the viewer's origin, including in document-ingestion workflows where a privileged reviewer opens submitted content. This issue is fixed in version 0.24.1.

Record
CVE-2026-104250
CVSS 3.1
6.1 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
Weakness
CWE-116, CWE-79
Updated
2026-11-02
OSVGHSA-v5mq-3xhg-98m9 6.1

unstructured: Stored XSS via unescaped HTML generation

Summary

When unstructured parses an untrusted document and outputs the result as HTML, malicious markup contained in the document (e.g. <img src=x onerror=...>) 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 <script> tags are stripped by the parser, but attribute-based vectors and javascript: URLs are not). In addition, link URLs are not scheme-filtered, so <a href="javascript:..."> 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.

from unstructured.partition.html import partition_html
from unstructured.partition.html.convert import elements_to_html

malicious = ('<body class="Document">'
             '<p class="Paragraph" onmouseover="alert(1)">hello'
             '<img src=x onerror="alert(document.domain)"></p>'
             '<a class="Hyperlink" href="javascript:alert(document.cookie)">click me</a>'
             '</body>')
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:

<p class="NarrativeText" id="..." onmouseover="alert(1)">
  <p class="Paragraph">hello</p>
  <img class="Image" onerror="alert(document.domain)" src="x"/>
</p>
<a class="UncategorizedText" href="javascript:alert(document.cookie)" id="...">click me</a>

Opening xss_output.html in a browser fires alert automatically (via <img onerror>), with no interaction. Per-vector confirmation (each payload labelled, each verified in a browser):

| Vector | Trigger | Cause | |---|---|---| | <img onerror> | open file (auto) | element text not escaped | | <svg onload> (via title='"><svg ...>') | open file (auto) | attribute value not escaped (" breakout) | | <a href=javascript:> | 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, <img onerror> fires in their session with no interaction, leading to cross-record information disclosure and privileged actions.

Record
GHSA-v5mq-3xhg-98m9
CVSS 3.1
6.1 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
Weakness
CWE-116, CWE-79
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
unstructured
PyPI
through 0.24.0
0.24.1
unstructured
PyPI
before 0.24.1, plus listed versions
0.0.1.dev00.10.00.10.10.10.100.10.11
Show 45 moreShow less
0.10.120.10.130.10.140.10.150.10.160.10.180.10.190.10.19.dev180.10.20.10.200.10.210.10.220.10.230.10.240.10.250.10.260.10.270.10.280.10.290.10.300.10.40.10.50.10.60.10.70.10.80.10.90.11.00.11.10.11.20.11.40.11.50.11.60.11.70.11.80.12.00.12.20.12.30.12.40.12.50.12.60.13.00.13.10.13.20.13.30.13.4
0.24.1

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-104250 is a medium vulnerability (CVSS 6.1) in Unstructured-io Unstructured, published October 13, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.23%, the 14th percentile. Fixed in unstructured 0.24.1.

References

This page doesn't list everything

Left out here: 177 versions. The JSON record has them.

MoveEnter Open