Security is very important for NiceGUI and its community. π
Learn more about it below. π
The latest version of NiceGUI is supported.
You are encouraged to write tests for your application and update your NiceGUI version frequently after ensuring that your tests are passing. This way you will benefit from the latest features, bug fixes, and security fixes.
Automated scanners and AI-assisted reviews frequently flag the patterns below. They are by design or already bounded, so please trace the data flow and check them against this list before filing β it keeps the advisory queue focused on real issues. The Security Best Practices documentation is the canonical reference for the security model itself β what the framework guarantees and what the application must do β while this list only records why each pattern is repeatedly misread.
| safein the page template (nicegui/templates/index.html). Jinja auto-escaping is bypassed there intentionally; the values are escaped at the source instead (for example, element data is escaped with a table sized for theString.rawscript context it lands in). A bare| safeis not itself a finding β trace where the value comes from and how it is escaped.- Raw
innerHTMLinui.html/ui.markdown(nicegui/elements/html.js,markdown.js). The raw assignment is theelsebranch ofsanitize: the opt-insanitize=Falsepath. The default (sanitize=True) sanitizes via DOMPurify. Passing untrusted input withsanitize=Falseis the caller's documented responsibility. ui.add_head_html,ui.add_body_html,ui.run_javascript,ui.add_css. These inject raw HTML/JS/CSS by design and are documented as never-for-untrusted-input. Using them with unsanitized user data is an application bug, not a framework vulnerability.ast.literal_evalin the docs.literal_evalonly parses literals; it is the safe alternative toevalthat the documentation demonstrates.- Forging Socket.IO events with a known
client_id(nicegui/nicegui.py, theevent/javascript_response/ack/loghandlers). Theclient_idis a per-session secret capability (an unguessableuuid4), and every attack of this shape presupposes the attacker already knows it. Binding the socketsidto the client does not change this: the handshake is reached with the sameclient_id, so anyone holding it can bind their own socket. Cross-origin connections are a separate concern already addressed independently. - Guessing a
uuid4identifier (client_id, theapp.storage.userID, event-listener / Leaflet-layer / scene-object IDs). Not every ID is a secret, and the ones that are carry 122 bits of platform-CSPRNG randomness β the security documentation describes which is which. The margins do shrink as more IDs are issued, but realistic deployments stay far below either bound: at a billion IDs the collision probability is around 10^-19, and a blind guess against a million live sessions succeeds with probability around 10^-31. Assuming such an ID can simply be guessed is not a finding β a leak path, predictable generation, or a route that treats a public ID as authority is.
If instead you have found a way around one of these protections β
an escaping bypass, or a default sanitize=True path that still executes script β
that is a real issue and we would very much like to hear about it.
Please include a reproduction.
If you think you found a vulnerability, and even if you are not sure about it, please report it right away by creating a private security advisory (GitHub β Security β Report a vulnerability) so we can discuss and patch the issue in a secure workspace. If you cannot use GitHub, you can also send an email to: nicegui@zauberzeug.com.
Please try to be as explicit as possible, describing all the steps and example code to reproduce the security issue.
We (the team at Zauberzeug) will review it thoroughly and get back to you.
Please restrain from publicly discussing a potential security vulnerability. π
It's better to discuss privately and try to find a solution first, to limit the potential impact as much as possible.
Thanks for your help!
The NiceGUI community and the Zauberzeug team thank you for that. π