VYPR
Moderate severityNVD Advisory· Published Jun 1, 2023· Updated Jan 9, 2025

CVE-2023-29159

CVE-2023-29159

Description

Directory traversal in Starlette allows remote unauthenticated attackers to view arbitrary files in the web service.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Directory traversal in Starlette allows remote unauthenticated attackers to view arbitrary files in the web service.

Vulnerability

CVE-2023-29159 is a directory traversal vulnerability in Starlette versions 0.13.5 through 0.26.x. The issue arises from improper handling of file paths, enabling an attacker to read files outside the intended directory.

Exploitation

An unauthenticated remote attacker can exploit this by sending crafted requests that traverse directories (e.g., using ../ sequences). Successful exploitation requires a running Starlette application that serves static files or uses similar functionality.

Impact

An attacker can read arbitrary files on the server, potentially accessing sensitive data such as configuration files, source code, or credentials. The CVSS score is 3.7 (Low) due to high attack complexity and limited impact (confidentiality only).

Mitigation

The vulnerability is fixed in Starlette version 0.27.0. Users should upgrade immediately. There are no known workarounds. The issue was reported by JPCERT/CC and is tracked in the Starlette GitHub repository [1][2][3].

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
starlettePyPI
>= 0.13.5, < 0.27.00.27.0

Affected products

2

Patches

1
1797de464124

Merge pull request from GHSA-v5gw-mw7f-84px

https://github.com/encode/starletteAmin AlaeeMay 16, 2023via ghsa
2 files changed · +38 6
  • starlette/staticfiles.py+1 1 modified
    @@ -169,7 +169,7 @@ def lookup_path(
                 else:
                     full_path = os.path.realpath(joined_path)
                 directory = os.path.realpath(directory)
    -            if os.path.commonprefix([full_path, directory]) != directory:
    +            if os.path.commonpath([full_path, directory]) != directory:
                     # Don't allow misbehaving clients to break out of the static files
                     # directory.
                     continue
    
  • tests/test_staticfiles.py+37 5 modified
    @@ -1,8 +1,8 @@
     import os
    -import pathlib
     import stat
     import tempfile
     import time
    +from pathlib import Path
     
     import anyio
     import pytest
    @@ -28,13 +28,12 @@ def test_staticfiles(tmpdir, test_client_factory):
         assert response.text == "<file content>"
     
     
    -def test_staticfiles_with_pathlib(tmpdir, test_client_factory):
    -    base_dir = pathlib.Path(tmpdir)
    -    path = base_dir / "example.txt"
    +def test_staticfiles_with_pathlib(tmp_path: Path, test_client_factory):
    +    path = tmp_path / "example.txt"
         with open(path, "w") as file:
             file.write("<file content>")
     
    -    app = StaticFiles(directory=base_dir)
    +    app = StaticFiles(directory=tmp_path)
         client = test_client_factory(app)
         response = client.get("/example.txt")
         assert response.status_code == 200
    @@ -516,3 +515,36 @@ def test_staticfiles_disallows_path_traversal_with_symlinks(tmpdir):
     
         assert exc_info.value.status_code == 404
         assert exc_info.value.detail == "Not Found"
    +
    +
    +def test_staticfiles_avoids_path_traversal(tmp_path: Path):
    +    statics_path = tmp_path / "static"
    +    statics_disallow_path = tmp_path / "static_disallow"
    +
    +    statics_path.mkdir()
    +    statics_disallow_path.mkdir()
    +
    +    static_index_file = statics_path / "index.html"
    +    statics_disallow_path_index_file = statics_disallow_path / "index.html"
    +    static_file = tmp_path / "static1.txt"
    +
    +    static_index_file.write_text("<h1>Hello</h1>")
    +    statics_disallow_path_index_file.write_text("<h1>Private</h1>")
    +    static_file.write_text("Private")
    +
    +    app = StaticFiles(directory=statics_path)
    +
    +    # We can't test this with 'httpx', so we test the app directly here.
    +    path = app.get_path({"path": "/../static1.txt"})
    +    with pytest.raises(HTTPException) as exc_info:
    +        anyio.run(app.get_response, path, {"method": "GET"})
    +
    +    assert exc_info.value.status_code == 404
    +    assert exc_info.value.detail == "Not Found"
    +
    +    path = app.get_path({"path": "/../static_disallow/index.html"})
    +    with pytest.raises(HTTPException) as exc_info:
    +        anyio.run(app.get_response, path, {"method": "GET"})
    +
    +    assert exc_info.value.status_code == 404
    +    assert exc_info.value.detail == "Not Found"
    

Vulnerability mechanics

Synthesis attempt was rejected by the grounding validator. Re-run pending.

References

9

News mentions

0

No linked articles in our index yet.