Skip to content

Buffered uploads preserve path segments in UploadFile.path while streamed uploads sanitize to basename #6752

Description

@harsh21234i

Description

Buffered and streamed uploads currently normalize uploaded filenames differently.

The streamed upload parser sanitizes the multipart filename to a basename:

filename = Path(filename.lstrip("/")).name

But the buffered upload path keeps the original path segments:

path=Path(file.filename.lstrip("/")) if file.filename else None

This means a filename such as ../secret.txt becomes:

- streamed upload: secret.txt
- buffered upload: ../secret.txt in UploadFile.path

UploadFile.name still returns the basename, but UploadFile.path is exposed to user handlers, so app code can observe
inconsistent behavior depending on whether the handler uses rx.upload_files or rx.upload_files_chunk.

### Reproduction

import io
from pathlib import Path

from starlette.datastructures import UploadFile as StarletteUploadFile
from reflex_components_core.core._upload import UploadFile

file = StarletteUploadFile(file=io.BytesIO(b"x"), filename="../secret.txt")

upload = UploadFile(
    file=file.file,
    path=Path(file.filename.lstrip("/")),
    size=file.size,
    headers=file.headers,
)

print(upload.path)
print(upload.name)

On Windows this prints:

..\secret.txt
secret.txt

The streamed parser path for the same filename emits:

secret.txt

### Expected behavior

Buffered uploads should apply the same filename normalization as streamed uploads, so UploadFile.path does not
preserve directory or traversal segments from the client-provided multipart filename.

### Suggested fix

Use the same basename normalization in the buffered upload path:

path=Path(Path(file.filename.lstrip("/")).name) if file.filename else None

or factor this into a shared helper so both upload modes always normalize filenames identically.

### Why this matters

This avoids inconsistent behavior between rx.upload_files and rx.upload_files_chunk, and prevents user handlers from
accidentally trusting path segments supplied by the client.


I searched existing issues with upload/path/filename traversal terms and did not find an existing report for this
specific buffered-vs-streamed inconsistency.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions