VYPR
Moderate severityNVD Advisory· Published Dec 8, 2025· Updated Dec 9, 2025

NiceGUI Reflected XSS in ui.add_css, ui.add_scss, and ui.add_sass via Style Injection

CVE-2025-66469

Description

NiceGUI is a Python-based UI framework. Versions 3.3.1 and below are vulnerable to Reflected XSS through its ui.add_css, ui.add_scss, and ui.add_sass functions. The functions lack proper sanitization or encoding for the JavaScript context they generate. An attacker can break out of the intended <style> or <script> tags by injecting closing tags (e.g., </style> or </script>), allowing for the execution of arbitrary JavaScript. This issue is fixed in version 3.4.0.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
niceguiPyPI
< 3.4.03.4.0

Affected products

1

Patches

1
a8fd25b7d5e2

Merge commit from fork

https://github.com/zauberzeug/niceguiEvan ChanDec 8, 2025via ghsa
3 files changed · +30 4
  • nicegui/functions/style.py+4 4 modified
    @@ -17,7 +17,8 @@ def add_css(content: Union[str, Path], *, shared: bool = False) -> None:
         """
         if helpers.is_file(content):
             content = Path(content).read_text(encoding='utf-8')
    -    add_head_html(f'<style>{content}</style>', shared=shared)
    +    safe_content = json.dumps(content).replace('<', r'\u003c')
    +    add_head_html(f'<script>addStyle({safe_content});</script>', shared=shared)
     
     
     def add_scss(content: Union[str, Path], *, indented: bool = False, shared: bool = False) -> None:  # DEPRECATED
    @@ -35,12 +36,11 @@ def add_scss(content: Union[str, Path], *, indented: bool = False, shared: bool
         """
         content = Path(content).read_text(encoding='utf-8') if helpers.is_file(content) else str(content).strip()
         syntax = 'indented' if indented else 'scss'
    +    safe_content = json.dumps(content).replace('<', r'\u003c')
         add_head_html(f'''
             <script type="module">
                 import * as sass from "sass";
    -            const style = document.createElement("style");
    -            style.textContent = sass.compileString({json.dumps(content)}, {{syntax: "{syntax}"}}).css;
    -            document.head.appendChild(style);
    +            addStyle(sass.compileString({safe_content}, {{syntax: "{syntax}"}}).css);
             </script>
         ''', shared=shared)
     
    
  • nicegui/templates/index.html+3 0 modified
    @@ -34,6 +34,9 @@
         <script type="importmap">
           {"imports": {{ imports | safe }}}
         </script>
    +    <script>
    +      addStyle = (c) => document.head.append(Object.assign(document.createElement("style"), { textContent: c }));
    +    </script>
         {{ head_html | safe }}
         <!-- prevent Prettier from removing this line -->
         {% for url in js_imports_urls %}
    
  • tests/test_add_html.py+23 0 modified
    @@ -63,3 +63,26 @@ def page():
     
         screen.open('/')
         assert screen.find('This is purple on yellow with SASS.').value_of_css_property('color') == 'rgba(128, 0, 128, 1)'
    +
    +
    +def test_add_css_with_script(screen: Screen):
    +    @ui.page('/')
    +    def page():
    +        ui.add_css("</style></script><script>document.write('XSS');</script>")
    +        ui.label('Hello, World!')
    +
    +    screen.open('/')
    +    screen.should_contain('Hello, World!')
    +    screen.should_not_contain('XSS')
    +
    +
    +def test_add_scss_with_script(screen: Screen):
    +    @ui.page('/')
    +    def page():
    +        ui.add_scss("</style></script><script>document.write('XSS');</script>")
    +        ui.label('Hello, World!')
    +
    +    screen.allowed_js_errors.append('static/sass.dart.js')
    +    screen.open('/')
    +    screen.should_contain('Hello, World!')
    +    screen.should_not_contain('XSS')
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.