NiceGUI's unvalidated chunk size parameter in media routes can cause memory exhaustion
Description
NiceGUI is a Python-based UI framework. Prior to version 3.9.0, NiceGUI's app.add_media_file() and app.add_media_files() media routes accept a user-controlled query parameter that influences how files are read during streaming. The parameter is passed to the range-response implementation without validation, allowing an attacker to bypass chunked streaming and force the server to load entire files into memory at once. With large media files and concurrent requests, this can lead to excessive memory consumption, degraded performance, or denial of service. This issue has been patched in version 3.9.0.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
niceguiPyPI | < 3.9.0 | 3.9.0 |
Affected products
1- Range: < 3.9.0
Patches
19026962b8c4fMerge commit from fork
2 files changed · +45 −4
nicegui/app/range_response.py+14 −4 modified@@ -10,9 +10,13 @@ mimetypes.init() +MIN_CHUNK_SIZE = 1024 +MAX_CHUNK_SIZE = 8192 + def get_range_response(file: Path, request: Request, chunk_size: int) -> Response: """Get a Response for the given file, supporting range-requests, E-Tag and Last-Modified.""" + chunk_size = max(MIN_CHUNK_SIZE, min(chunk_size, MAX_CHUNK_SIZE)) file_size = file.stat().st_size last_modified_time = datetime.fromtimestamp(file.stat().st_mtime, timezone.utc) start = 0 @@ -29,10 +33,16 @@ def get_range_response(file: Path, request: Request, chunk_size: int) -> Respons range_header = request.headers.get('range') media_type = mimetypes.guess_type(str(file))[0] or 'application/octet-stream' if range_header is not None: - byte1, byte2 = range_header.split('=')[1].split('-') - start = int(byte1) - if byte2: - end = int(byte2) + try: + byte1, byte2 = range_header.split('=')[1].split('-') + start = int(byte1) + if byte2: + end = int(byte2) + except (IndexError, ValueError): + return Response(status_code=416, headers={'Content-Range': f'bytes */{file_size}'}) + if start > end or start >= file_size: + return Response(status_code=416, headers={'Content-Range': f'bytes */{file_size}'}) + end = min(end, file_size - 1) status_code = 206 # Partial Content content_length = end - start + 1 headers.update({
tests/test_serving_files.py+31 −0 modified@@ -73,6 +73,37 @@ def page(): assert_video_file_streaming(url_path) +def test_invalid_range_header_returns_416(screen: Screen): + app.add_media_files('/media', Path(TEST_DIR) / 'media') + + @ui.page('/') + def page(): + ui.label('Hello, world!') + + screen.open('/') + with httpx.Client() as http_client: + for range_value in ['bytes=1000-500', 'bytes=9999-10000', 'bytes=abc-def', 'invalid']: + r = http_client.get(f'http://localhost:{Screen.PORT}/media/test.mp4', headers={'Range': range_value}) + assert r.status_code == 416, f'Expected 416 for Range: {range_value}' + + +def test_malicious_chunk_size_is_clamped(screen: Screen): + app.add_media_files('/media', Path(TEST_DIR) / 'media') + + @ui.page('/') + def page(): + ui.label('Hello, world!') + + screen.open('/') + with httpx.Client() as http_client: + for chunk_size in [-1, 0, -9999]: + r = http_client.get( + f'http://localhost:{Screen.PORT}/media/test.mp4?nicegui_chunk_size={chunk_size}', + headers={'Range': 'bytes=0-1000'}, + ) + assert r.status_code == 206 + + @pytest.mark.parametrize('url_path', ['/static', '/static/']) def test_get_from_static_files_dir(url_path: str, screen: Screen): app.add_static_files(url_path, Path(TEST_DIR).parent, max_cache_age=3456)
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
5- github.com/advisories/GHSA-w5g8-5849-vj76ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-33332ghsaADVISORY
- github.com/zauberzeug/nicegui/commit/9026962b8c4f3f225c98b2fbc35aa6b60cb3495bghsax_refsource_MISCWEB
- github.com/zauberzeug/nicegui/releases/tag/v3.9.0ghsax_refsource_MISCWEB
- github.com/zauberzeug/nicegui/security/advisories/GHSA-w5g8-5849-vj76ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.