Summary
The DataFile case-run routes allowed path traversal payloads to escape the selected case/run directory while still remaining inside Config.DATA_STORAGE.
Examples:
POST /deleteCaseRun accepted a caserunname like ../../victim_case.
GET /downloadFile accepted a file value like ../../../victim_case/genData.json.
Because the previous guard validated against the broad DATA_STORAGE root, these payloads could still resolve inside storage but outside the intended selected case/run folder. Expected behavior is that traversal outside the selected case/run directory is rejected with 400 Invalid path. and no sibling case data is deleted or downloaded.
How can we reproduce it?
- Create two cases in data storage, for example
source_case and victim_case.
- For delete behavior, send:
POST /deleteCaseRun
{
"casename": "source_case",
"caserunname": "../../victim_case",
"resultsOnly": false
}
- For download behavior, set the active session case to source_case and request:
GET /downloadFile?file=../../../victim_case/genData.json
- Before the fix, the path could resolve to the sibling victim_case under DATA_STORAGE.
- After the fix, the route returns:
{
"message": "Invalid path.",
"status_code": "error"
}
Logs or screenshots (optional)
Validation command:
.\venv\Scripts\python.exe -m pytest tests\test_datafile_guards.py -q
Output:
platform win32 -- Python 3.11.9, pytest-9.0.2, pluggy-1.6.0
collected 3 items
tests\test_datafile_guards.py ... [100%]
3 passed, 1 warning in 0.63s
the warning was only a local pytest cache permission warning for .pytest_cache; the tests passed.
## Overlap assessment
- Classification: Partial thematic overlap, not duplicate.
- Overlapping items: Path traversal/path sanitization concerns.
- Why this is not duplicate/superseded: This PR fixes a different route and attack surface: `deleteCaseRun`, `downloadDataFile`, `downloadFile`, `downloadCSVFile`, and `downloadResultsFile` in `DataFileRoute.py`. The related PRs focus on ZIP extraction or backup archive generation in `UploadRoute.py`, so they do not cover sibling case traversal through case-run/download parameters.
Validate untrusted path segments relative to the narrow intended parent directory instead of only validating against `Config.DATA_STORAGE`.
The proposed patch:
- Adds `_safe_child_path(...)` for nested path validation.
- Validates case-run names relative to `<DATA_STORAGE>/<case>/res`.
- Validates CSV/download filenames relative to the specific `csv` or case-run directory.
- Returns `400 Invalid path.` on `PermissionError`.
- Adds regression tests proving valid delete still works and traversal attempts are blocked.
Summary
The DataFile case-run routes allowed path traversal payloads to escape the selected case/run directory while still remaining inside
Config.DATA_STORAGE.Examples:
POST /deleteCaseRunaccepted acaserunnamelike../../victim_case.GET /downloadFileaccepted afilevalue like../../../victim_case/genData.json.Because the previous guard validated against the broad
DATA_STORAGEroot, these payloads could still resolve inside storage but outside the intended selected case/run folder. Expected behavior is that traversal outside the selected case/run directory is rejected with400 Invalid path.and no sibling case data is deleted or downloaded.How can we reproduce it?
source_caseandvictim_case.GET /downloadFile?file=../../../victim_case/genData.json
{
"message": "Invalid path.",
"status_code": "error"
}
UploadRoute.py.backupCase, but they focus on archive entry layout/relative paths.deleteCaseRunor DataFile download traversal throughcaserunname/fileinAPI/Routes/DataFile/DataFileRoute.py.Logs or screenshots (optional)
Validation command: