GitHub Advisoryadvisory · PyPI
GHSA-v5mq-3xhg-98m9 unstructured: Stored XSS via unescaped HTML generation
MEDIUM
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
References
- https://github.com/Unstructured-IO/unstructured/security/advisories/GHSA-v5mq-3xhg-98m9 web
- https://nvd.nist.gov/vuln/detail/CVE-2026-104250 advisory
- https://github.com/Unstructured-IO/unstructured/pull/4394 web
- https://github.com/Unstructured-IO/unstructured/commit/cda16b3f2170019be9c6ea5635946fb3eaa2d66f web
- https://github.com/Unstructured-IO/unstructured package
- https://github.com/Unstructured-IO/unstructured/releases/tag/0.24.1 web