CVE-2023-29159Low· 3.7▾ SunlitStarlette has Path Traversal vulnerability in StaticFiles
▾ Sunlit zone — Low / medium · no exploitation signal
impact 20.4 · likelihood 0.4 · exploitation 0
Need a working PoC? Pro members can cast a request and our team develops one — it lands right here.
Exploit-prediction probability, daily snapshots since Jul 8.
Disclosure to exploitation, from the record and what we observed since indexing it.
Disclosed via OSV
Last analysed / modified upstream
2.0%
2.0% → 2.0%
When using StaticFiles, if there's a file or directory that starts with the same name as the StaticFiles directory, that file or directory is also exposed via StaticFiles which is a path traversal vulnerability.
The root cause of this issue is the usage of os.path.commonprefix():
https://github.com/encode/starlette/blob/4bab981d9e870f6cee1bd4cd59b87ddaf355b2dc/starlette/staticfiles.py#L172-L174
As stated in the Python documentation (https://docs.python.org/3/library/os.path.html#os.path.commonprefix) this function returns the longest prefix common to paths.
When passing a path like /static/../static1.txt, os.path.commonprefix([full_path, directory]) returns ./static which is the common part of ./static1.txt and ./static, It refers to /static/../static1.txt because it is considered in the staticfiles directory. As a result, it becomes possible to view files that should not be open to the public.
The solution is to use os.path.commonpath as the Python documentation explains that os.path.commonprefix works a character at a time, it does not treat the arguments as paths.
In order to reproduce the issue, you need to create the following structure:
├── static
│ ├── index.html
├── static_disallow
│ ├── index.html
└── static1.txt
And run the Starlette app with:
import uvicorn
from starlette.applications import Starlette
from starlette.routing import Mount
from starlette.staticfiles import StaticFiles
routes = [
Mount("/static", app=StaticFiles(directory="static", html=True), name="static"),
]
app = Starlette(routes=routes)
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
And running the commands:
curl --path-as-is 'localhost:8000/static/../static_disallow/'
curl --path-as-is 'localhost:8000/static/../static1.txt'
The static1.txt and the directory static_disallow are exposed.
Confidentiality is breached: An attacker may obtain files that should not be open to the public.
Security researcher Masashi Yamane of LAC Co., Ltd reported this vulnerability to JPCERT/CC Vulnerability Coordination Group and they contacted us to coordinate a patch for the security issue.
starlette >= 0.13.5, < 0.27.0Upgrade to a patched release:
starlette 0.27.0Connected by shared product, vendor, weakness, or advisory.
CVE-2024-47874None· 0.0Starlette Denial of service (DoS) via multipart/form-data
CVE-2023-30798High· 7.5MultipartParser denial of service with too many fields or files
CVE-2025-54121Medium· 5.3Starlette has possible denial-of-service vector when parsing large files in multipart forms
CVE-2025-62727High· 7.5Starlette vulnerable to O(n^2) DoS via Range header merging in ``starlette.responses.FileResponse``
CVE-2026-48817Medium· 5.3Starlette: Arbitrary HTTP method dispatched to `HTTPEndpoint` attributes via `getattr`
CVE-2026-48818High· 7.5Starlette: SSRF and NTLM credential theft via UNC paths in StaticFiles on Windows