Feb 24, 2026·6 min read·47 visits
NiceGUI versions prior to 3.8.0 used Python f-strings to construct JavaScript commands and included an `eval()` fallback in the client-side code. This allows attackers to break out of string quotas or trigger arbitrary code execution via the `run_method` API.
A critical Cross-Site Scripting (XSS) vulnerability in NiceGUI allows attackers to execute arbitrary JavaScript by injecting malicious payloads into method names. The flaw stems from unsafe string interpolation in the Python backend and a dangerous `eval()` fallback in the JavaScript frontend.
There is an old saying in security: the most dangerous place in any system is the boundary between two different languages. Whether it's SQL injection (Application vs. Database) or command injection (Application vs. Shell), the translation layer is where the bodies are buried. NiceGUI, a popular Python-based UI framework, attempts to bridge the gap between Python logic and the browser's JavaScript engine. It allows developers to control web elements purely from Python code.
It sounds magical. You write ui.button('Click me').on('click', handler), and the browser obeys. But under the hood, this magic relies on the server sending instructions to the client. Specifically, NiceGUI exposes APIs like run_method() that tell the frontend to execute specific JavaScript methods on DOM elements.
In CVE-2026-27156, we find out what happens when that communication channel is built with the digital equivalent of duct tape and optimism. It turns out that constructing executable code using string concatenation is—surprise, surprise—a terrible idea. This vulnerability transforms a feature meant for UI interactivity into a direct pipe for Cross-Site Scripting (XSS), allowing attackers to hijack the user's browser session with humiliating ease.
The vulnerability is a beautiful, catastrophic duet between the backend and the frontend. It requires two distinct failures working in harmony to create the exploit condition.
Sin #1: The Python Interpolation
On the server side (Python), NiceGUI needs to tell the browser: "Hey, run the focus method on element #42." Prior to version 3.8.0, the code constructed this instruction using a Python f-string. It looked something like this:
f'return runMethod({self.id}, "{name}", ...)'
See the problem? The name variable is wrapped in double quotes. If an attacker can control name, they don't just provide a method name; they provide a string that creates code. By injecting a double quote ("), they can close the string context and start writing their own JavaScript. It is the classic "Bobby Tables" scenario, but for the browser.
Sin #2: The JavaScript Fallback
But wait, it gets worse. Even if you didn't break the string syntax, the frontend had a trapdoor waiting for you. The runMethod function in nicegui.js had a "helpful" fallback mechanism. If the method you requested didn't exist on the target element, the code decided to just eval() it.
Yes, you read that right. eval(method_name)(target, ...args). This meant that if you passed a global function name (like alert or a malicious payload defined elsewhere) instead of a valid element method, NiceGUI would helpfully execute it for you. It's like a bank vault that opens if you just ask it nicely in a different language.
Let's look at the fix, because it perfectly illustrates the difference between "hacking it together" and "engineering." The developers at Zauberzeug (the maintainers) released a patch in version 3.8.0 that addresses both sides of the coin.
The Python Fix
In nicegui/element.py, the unsafe f-string was replaced with json.dumps(). This is the gold standard for passing data to JavaScript. json.dumps() ensures that quotes, backslashes, and control characters are properly escaped.
# The Vulnerable Way (Before)
return self.client.run_javascript(
f'return runMethod({self.id}, "{name}", {json.dumps(args)})',
timeout=timeout
)
# The Secure Way (After)
return self.client.run_javascript(
f'return runMethod({self.id}, {json.dumps(name)}, {json.dumps(args)})',
timeout=timeout,
)The JavaScript Fix
In nicegui/static/nicegui.js, the patch is basically a deletion. They removed the eval() branch entirely. If the method doesn't exist on the element or its Quasar reference, the code now simply does nothing—which is exactly what it should do.
// nicegui.js diff
function runMethod(target, method_name, args) {
// ... checks if method exists ...
- } else {
- return eval(method_name)(target, ...args); // Goodbye, old friend.
}
// ...This creates a "defense in depth" posture. Even if the Python side somehow failed to escape the string (unlikely with json.dumps), the JS side no longer has the eval sink to abuse.
So, how do we weaponize this? We need a scenario where a user-controlled input feeds into one of the affected APIs: run_method, run_grid_method, run_chart_method, or get_computed_prop.
Imagine a NiceGUI application that dynamically allows users to query properties of UI elements via a URL parameter, perhaps for a debugging dashboard or a dynamic report generator.
Attack Vector 1: Quote Breakout
If the application takes a query param ?action=... and passes it to run_method(action), we can send this payload:
"); alert(document.domain); //
The Python backend interpolates this into:
return runMethod(123, ""); alert(document.domain); //", ...)
The browser sees:
runMethod with empty string.alert(document.domain).//).Attack Vector 2: The Eval Fallback
If the quote injection is sanitized but the input still reaches the runMethod function, we can abuse the eval fallback (in versions < 3.8.0). If we control the method name, we can pass alert.
Even though alert isn't a method of the HTML Element, the else { return eval(method_name)(...) } block catches it. The browser resolves eval("alert") to the window's alert function and executes it. This is particularly dangerous because it bypasses checks that might only look for special characters like quotes or semicolons.
NiceGUI is often used for internal dashboards, IoT control panels, and data science visualizations. These are environments that often sit behind a VPN but lack rigorous internal application security controls.
An XSS vulnerability here isn't just about popping an alert box. It allows an attacker to:
The CVSS score is 6.1 (Medium) mostly because it requires User Interaction (Reflected XSS), but in the context of trusted internal tools, the impact is often critical.
The remediation is straightforward: Upgrade to NiceGUI 3.8.0 or later immediately.
pip install --upgrade nicegui
If you cannot upgrade for some reason (maybe you enjoy living on the edge?), you must audit every single call to run_method and its siblings. Ensure that the name argument is never, ever derived from user input. Hardcode your method names.
For developers using the ui.run_javascript() escape hatch: The vulnerability in the core library serves as a warning. If you are manually constructing JavaScript strings using f-strings, stop it. Use json.dumps() for any variable you are injecting into a JS context. Do not trust your own ability to sanitize input; you will miss edge cases. Let the JSON library handle the serialization.
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
nicegui zauberzeug | < 3.8.0 | 3.8.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-79 |
| Attack Vector | Network (Reflected) |
| CVSS v3.1 | 6.1 (Medium) |
| Impact | Cross-Site Scripting (XSS) |
| Exploit Status | PoC Available |
| Fix Version | 3.8.0 |
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
netfoil, an allowlist-based DNS proxy, failed to sanitize ALPN fields parsed from untrusted DNS-over-HTTPS (DoH) HTTPS Resource Records. This allowed attackers to inject ANSI escape sequences into log files or trigger Denial of Service (DoS) via uncontrolled memory allocations.
An issue was discovered in the tokio-postgres library for Rust prior to version 0.7.18. A trust assumption mismatch between the PostgreSQL protocol messages sent by a server and how they are parsed and indexed by the client-side library allows a rogue or compromised database server to trigger a Denial of Service (DoS) crash via an unhandled out-of-bounds slice indexing panic.
CVE-2026-14669 is a critical heap-based buffer overflow vulnerability in PostgreSQL's date/time formatting function to_char(timestamptz). The flaw arises from unsafe copying of user-controlled timezone abbreviations into a fixed-size internal buffer. An authenticated database user can trigger this issue by setting a long POSIX timezone abbreviation containing custom formatting, allowing them to overwrite adjacent heap structures and hijack execution control to achieve remote code execution (RCE) with the privileges of the 'postgres' operating system user.
An unauthenticated remote denial of service vulnerability exists in the Unleash feature management platform. By submitting a crafted JSON payload containing deeply nested structures to an OpenAPI-validated endpoint, an attacker can trigger uncontrolled recursion within the error formatting module. This leads to a call-stack exhaustion (RangeError: Maximum call stack size exceeded) inside the Node.js runtime, causing the service to crash immediately without recovery.
CVE-2026-63004 is a server-side request forgery (SSRF) vulnerability in the Unleash feature management platform. Authenticated administrators with CREATE_ADDON or UPDATE_ADDON privileges can exploit this vulnerability to initiate requests to loopback addresses, private networks, and cloud metadata endpoints, potentially leading to information disclosure and credential extraction.
Prior to version 8.0.3, Unleash's Markdown event formatter directly mutated the global template-escaping function of the shared mustache Node.js module, resulting in a process-wide security degradation where HTML/Markdown escaping was permanently disabled for the application lifetime.