fix(files): prevent path traversal in file management endpoints#309
Open
sebastiondev wants to merge 1 commit into
Open
fix(files): prevent path traversal in file management endpoints#309sebastiondev wants to merge 1 commit into
sebastiondev 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
list_workflow_files,delete_file, anddelete_workflow_filesendpoints inbackend/pyspur/api/file_management.pyconstruct filesystem paths directly from user-suppliedworkflow_idandfilenamepath parameters without validating that the resolved path stays within the intendeddata/run_files/directory. An attacker can supply path traversal sequences (e.g.../../) to list, read metadata about, or delete arbitrary files and directories accessible to the application process.CWE-22 (Improper Limitation of a Pathname to a Restricted Directory)
These endpoints are mounted at
/api/files/with no authentication middleware, so any client with network access to the API can exploit this.Affected code
In
backend/pyspur/api/file_management.py, the three affected endpoints previously did:The
workflow_idandfilenamevalues come directly from the URL path and are not sanitized. Notably, theget_fileendpoint (line 128+) already had its own path validation, which confirms the other endpoints were simply missing it.Proof of Concept
An attacker can delete arbitrary files the process has write access to:
FastAPI automatically URL-decodes path parameters, so
..%2Fbecomes../before reaching the handler. The pathDATA_DIR / "run_files" / "../../../etc"resolves to/etc(or wherever the process root is), allowing directory listing or deletion.Fix description
This PR adds a
_safe_resolve()helper that:.resolve()to canonicalize the result (resolving all.., symlinks, etc.)All three affected endpoints now use
_safe_resolve(DATA_DIR, "run_files", workflow_id, ...)instead of raw path concatenation. This is the same containment pattern used throughout the Python ecosystem for path traversal prevention — resolve, then verify prefix.The fix is minimal: one new helper function and three call-site changes, totaling 12 lines added and 3 lines changed.
Testing
../,..%2F,....//) inworkflow_idandfilenamenow return HTTP 400 instead of performing filesystem operations outside the data directory._safe_resolvehelper correctly handles edge cases: symlinks, double-dot sequences, and path components that partially match the base directory name.Adversarial review
Before submitting, we verified this isn't mitigated by existing protections. The file management router is mounted without any authentication dependencies (
api_app.include_router(file_management_router, prefix="/files", tags=["files"])— nodependencies=argument), and there is no application-wide auth middleware. FastAPI's path parameter handling decodes URL-encoded traversal sequences before they reach the handler, so URL encoding does not prevent exploitation. Theget_fileendpoint already had its own path validation, further confirming the other endpoints were unprotected.Submitted by Sebastion — autonomous open-source security research from Foundation Machines. Free for public repos via the Sebastion AI GitHub App.