Skip to content

Commit 1797de4

Browse files
authored
Merge pull request from GHSA-v5gw-mw7f-84px
1 parent 24c1fac commit 1797de4

File tree

2 files changed

+38
-6
lines changed

2 files changed

+38
-6
lines changed

starlette/staticfiles.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ def lookup_path(
169169
else:
170170
full_path = os.path.realpath(joined_path)
171171
directory = os.path.realpath(directory)
172-
if os.path.commonprefix([full_path, directory]) != directory:
172+
if os.path.commonpath([full_path, directory]) != directory:
173173
# Don't allow misbehaving clients to break out of the static files
174174
# directory.
175175
continue

tests/test_staticfiles.py

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import os
2-
import pathlib
32
import stat
43
import tempfile
54
import time
5+
from pathlib import Path
66

77
import anyio
88
import pytest
@@ -28,13 +28,12 @@ def test_staticfiles(tmpdir, test_client_factory):
2828
assert response.text == "<file content>"
2929

3030

31-
def test_staticfiles_with_pathlib(tmpdir, test_client_factory):
32-
base_dir = pathlib.Path(tmpdir)
33-
path = base_dir / "example.txt"
31+
def test_staticfiles_with_pathlib(tmp_path: Path, test_client_factory):
32+
path = tmp_path / "example.txt"
3433
with open(path, "w") as file:
3534
file.write("<file content>")
3635

37-
app = StaticFiles(directory=base_dir)
36+
app = StaticFiles(directory=tmp_path)
3837
client = test_client_factory(app)
3938
response = client.get("/example.txt")
4039
assert response.status_code == 200
@@ -516,3 +515,36 @@ def test_staticfiles_disallows_path_traversal_with_symlinks(tmpdir):
516515

517516
assert exc_info.value.status_code == 404
518517
assert exc_info.value.detail == "Not Found"
518+
519+
520+
def test_staticfiles_avoids_path_traversal(tmp_path: Path):
521+
statics_path = tmp_path / "static"
522+
statics_disallow_path = tmp_path / "static_disallow"
523+
524+
statics_path.mkdir()
525+
statics_disallow_path.mkdir()
526+
527+
static_index_file = statics_path / "index.html"
528+
statics_disallow_path_index_file = statics_disallow_path / "index.html"
529+
static_file = tmp_path / "static1.txt"
530+
531+
static_index_file.write_text("<h1>Hello</h1>")
532+
statics_disallow_path_index_file.write_text("<h1>Private</h1>")
533+
static_file.write_text("Private")
534+
535+
app = StaticFiles(directory=statics_path)
536+
537+
# We can't test this with 'httpx', so we test the app directly here.
538+
path = app.get_path({"path": "/../static1.txt"})
539+
with pytest.raises(HTTPException) as exc_info:
540+
anyio.run(app.get_response, path, {"method": "GET"})
541+
542+
assert exc_info.value.status_code == 404
543+
assert exc_info.value.detail == "Not Found"
544+
545+
path = app.get_path({"path": "/../static_disallow/index.html"})
546+
with pytest.raises(HTTPException) as exc_info:
547+
anyio.run(app.get_response, path, {"method": "GET"})
548+
549+
assert exc_info.value.status_code == 404
550+
assert exc_info.value.detail == "Not Found"

0 commit comments

Comments
 (0)