Feb 24, 2026·6 min read·54 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')
An authorization bypass vulnerability exists in the legacy session export API of Indico, an open-source event management system developed at CERN. Due to a missing object-level access check, authenticated users can bypass configuration-level restrictions to extract private session metadata (including session titles, descriptions, and list of conveners) from events that they are otherwise authorized to view.
An incomplete Server-Side Request Forgery (SSRF) validation check in Indico prior to version 3.3.13 allows authenticated event organizers to bypass outbound network restrictions. By utilizing backslash characters within crafted URLs, attackers can exploit a parser differential between the application's validator and the downstream HTTP client library to access internal network resources.
CVE-2026-107717 represents a critical prompt boundary bypass and chat role injection vulnerability in the Banks Python package (versions prior to 2.5.0). The library parses generated template outputs line-by-line, attempting to validate each segment as a JSON-serialized ChatMessage object without validating the source boundaries of the text. If an application integrates user input directly into a prompt template, a remote, unauthenticated attacker can supply multi-line inputs with structured JSON payloads. This input is then parsed as high-privilege system instructions or tool execution responses, completely hijacking downstream Large Language Model behavior.
Improper pathname limitation and link resolution (CWE-22 and CWE-59) in the banks library prior to version 2.5.1 allow local attackers to read or write arbitrary files via crafted symbolic links in the prompt directory registry.
Improper validation of dynamic class resolution within Hazelcast's Zero Config Compact Serialization allows unauthenticated clients to trigger reflective class instantiation. This flaw can be exploited to read arbitrary JVM heap or off-heap memory, crash cluster nodes, or achieve arbitrary code execution under specific classpath conditions. This issue is resolved in Hazelcast versions 5.4.5, 5.5.10, 5.6.1, and 5.7.0.
An authentication bypass vulnerability in NearForm's fast-jwt before version 6.3.4 allows attackers to replay expired tokens due to an error in the verifier's cache expiration logic. When caching is enabled, the cache TTL defaults to 10 minutes instead of honoring the token's exp claim if the token lacks an iat claim.