
Advisory: CVE-2026-38361 multiple DoS vulnerabilities (CWE-400/CWE-670) in dash-uploader (Python/PyPI)
Multiple unauthenticated Denial of Service (DoS) issues in fohrloop/dash-uploader (Python, PyPI), including (but not limited to) Out-of-Memory (OOM) process crash, file truncation to zero bytes, permanent disk exhaustion, and complete bypass of the documented max_file_size limit. Additional resource-abuse paths exist through the same unsanitized parameter set.
The repository was archived on 2025-07-19 with no active maintainer. Every published version (0.1.0 through 0.7.0a2) is affected and will remain so. The package still pulls roughly 28,000 monthly downloads.
Anyone running dash-uploader in production must apply a mitigation themselves. The recommended fix is to migrate to Plotly Dash's built-in dcc.Upload component. See Mitigation for full options.
The dash-uploader HTTP handler accepts unauthenticated POST requests with attacker-controlled parameters that flow into memory allocation, file operations, and directory creation with no bounds checks, no rate limits, and no cleanup mechanism. Four independent issues in the same code path:
Verified on a 7.7 GB system: 5 concurrent POST requests with resumableTotalChunks=30000000 triggered the Linux OOM killer within 2 seconds. Kernel log confirms:
Out of memory: Killed process 24203 (python3) total-vm:8302276kB, anon-rss:7068012kB
Each request allocates approximately 2.9 GB via a list comprehension over range(1, resumableTotalChunks + 1). The server process is terminated and the application becomes completely unavailable until manual restart.
A file containing 42 bytes of data was reduced to 0 bytes via a single POST request with resumableTotalChunks=0. The root cause is Python's all() returning True for empty iterables, which tricks the upload handler into treating zero chunks as a completed upload. The existing file is deleted via os.unlink() and replaced with an empty file.
10 orphaned temp directories with chunk files were created and persisted indefinitely on disk. A codebase-wide search across all source files for cleanup, ttl, expire, garbage, purge, cron, schedule, and periodic returned zero results. The only cleanup call (shutil.rmtree) executes exclusively on completed uploads. There is no mechanism to reclaim disk space from incomplete sessions.
max_file_size bypass (verified)The server accepted a 5 MB chunk for a file claiming resumableTotalSize=999999999999 (~999 GB) with HTTP 200. The max_file_size parameter is passed only to the React JavaScript component. The server never checks file size, chunk size, Content-Length, or Flask MAX_CONTENT_LENGTH. A developer setting max_file_size=10 has no server-side protection.
# dash_uploader/httprequesthandler.py
def _post(self):
resumableTotalChunks = request.form.get("resumableTotalChunks", type=int) # attacker-controlled, no bounds
...
chunk_paths = [
os.path.join(temp_dir, get_chunk_name(resumableFilename, x))
for x in range(1, resumableTotalChunks + 1) # unbounded; e.g. 30M -> ~2.9 GB -> OOM
]
upload_complete = all([os.path.exists(p) for p in chunk_paths]) # all([]) is True -> truncation when chunks=0
if upload_complete:
target_file_name = os.path.join(temp_root, resumableFilename)
if os.path.exists(target_file_name):
os.unlink(target_file_name) # existing file deleted
with open(target_file_name, "ab") as target_file:
for p in chunk_paths: # empty list -> empty file written
...
Same code path produces both the OOM (large resumableTotalChunks) and the file-truncation primitive (resumableTotalChunks=0).
An attacker sends unauthenticated POST requests to the /API/resumable endpoint.
resumableTotalChunks=30000000 allocate ~2.9 GB each, triggering the OOM killer.resumableTotalChunks=0; Python all([])=True tricks the server into overwriting the target file with empty content.No authentication or privileges required.
resumableTotalChunks)os.makedirs() with unsanitized resumableIdentifierall() returning True for empty iterables when resumableTotalChunks=0max_file_size is enforced only in client-side JavaScript while the server-side handler performs zero size validation and never sets Flask MAX_CONTENT_LENGTHdash_uploader/httprequesthandler.py (BaseHttpRequestHandler._post method)dash_uploader/upload.py (Upload function, max_file_size parameter)dash_uploader/configure_upload.py (missing MAX_CONTENT_LENGTH)Options for currently-deployed users, in order of preference:
dcc.Upload, the official upload component shipped with Plotly Dash. It has no chunk-count parameter, no on-disk temporary state, and respects Flask MAX_CONTENT_LENGTH. None of the four issues here apply. Best suited to small and medium files. For very large uploads, see item 2.MAX_CONTENT_LENGTH), bounds on any client-supplied chunk count, and an allowlist for accepted filenames.MAX_CONTENT_LENGTH at the application level (the library does not), and reject inputs at the application or reverse-proxy layer where any of the following are true:
resumableTotalChunks <= 0resumableTotalChunks exceeds a reasonable bound (e.g., 10,000)resumableTotalSize exceeds the developer-configured max_file_size0.6.1 (stable line). Pre-releases extend to 0.7.0a2.dash. Optional dependency: pyyaml. License: MIT.Muhammad Fitri Bin Mohd Sultan
| CVE ID | CVE-2026-38361 (NVD) |
| Vulnerability | Uncontrolled Resource Consumption (CWE-400), Always-Incorrect Control Flow Implementation (CWE-670) |
| CVSS 3.1 | 7.5 / High (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H) |
| Product | dash-uploader |
| Affected versions | 0.1.0 through 0.7.0a2 (all 18 releases) |
| Fixed version | none (project archived 2025-07-19) |
| Attack vector | Remote, unauthenticated |
| Discoverer | Muhammad Fitri Bin Mohd Sultan |
| Assigned by | MITRE, 2026-05-07 |
| Related | CVE-2026-38360 (path traversal in same library) |
| Date | Event |
|---|
| 2026-03-19 | Vulnerabilities discovered during security research on a production deployment. |
| 2026-03-22 | CVE request submitted to MITRE. |
| 2026-05-07 | CVE-2026-38361 assigned by MITRE. |
| 2026-05-07 | Public advisory published. |
| 2026-05-09 | CVE record published on the MITRE CVE database and the NVD. |