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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions