fix(uploads): prevent path traversal in reference audio upload endpoints - #37
Open
sebastionoss wants to merge 1 commit into
Open
fix(uploads): prevent path traversal in reference audio upload endpoints#37sebastionoss wants to merge 1 commit into
sebastionoss wants to merge 1 commit into
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The
/api/upload-referenceand/api/upload-and-trim-referenceendpoints inmain.pywrite uploaded files to disk using the client-suppliedfile.filenamevalue without stripping path components. A crafted filename such as../../../../tmp/pwned.wavcauses the server to write outsideUPLOADS_DIR, which is a path traversal vulnerability (CWE-22).Both endpoints do prefix the filename with a UUID (
f"{file_id}_{file.filename}"), but../sequences insidefile.filenameare still honoured byPath/open(), so the UUID prefix does not neutralize the traversal — the resulting path just becomesUPLOADS_DIR/<uuid>_../../../tmp/pwned.wav, which resolves outside the uploads directory.The file-extension check (
endswith(allowed_ext)) only restricts the suffix, so an attacker can simply append.wav(e.g.../../../../home/user/.ssh/authorized_keys.wav— or more realistically, overwrite arbitrary.wav/config files under the app user's write scope).Affected code
main.pylines ~416–470, functionsupload_referenceandupload_and_trim_reference.Fix
Sanitize the client-supplied filename with
Path(file.filename).name, which returns only the final path component and discards any directory traversal segments. The sanitized value is then used for both the on-disk path and the JSON response. This is a minimal, targeted change — 11 additions / 4 deletions — and leaves all other upload/trim logic intact.Reproduction
With the server running (default
uvicorn main:app, listening on 127.0.0.1:8000 unless--hostis overridden):I verified locally that
Path("../../../../tmp/pwned.wav").name == "pwned.wav", and that the post-fix code path produces only basenames underUPLOADS_DIR.Threat model / preconditions
--hostis user-configurable and it is common for users to run with--host 0.0.0.0for LAN access (e.g. accessing the UI from another machine on the same network).dependencies=[Depends(...)]gate and no app-level auth middleware inmain.py.Impact is therefore bounded on a strict localhost deployment but becomes a real arbitrary-write primitive (limited to the app user's permissions, with any extension the attacker chooses via suffix) whenever the server is exposed on a LAN or reachable through a reverse proxy.
Adversarial review
Before submitting I tried to disprove this: I checked whether the UUID prefix or the extension whitelist neutralizes the traversal, and neither does — the UUID becomes part of a filename fragment that
Pathhappily walks out of with../, and the extension check operates on the untrusted string so appending.wavbypasses it. I also verified there is no framework-level auth gate on the affected router that would prevent an unauthenticated attacker from reaching the endpoints.Diff stat
Discovered by the Sebastion AI GitHub App.