Skip to content

Commit 391e212

Browse files
fix: CVE, do not let the cdn host files above the base cache dir
This allowed arbitrary files to be read from the server, and could be used to read files from the server that are not intended to be served.
1 parent c302caf commit 391e212

File tree

2 files changed

+31
-1
lines changed

2 files changed

+31
-1
lines changed

solara/server/cdn_helper.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,14 @@ def put_in_cache(base_cache_dir: pathlib.Path, path, data: bytes):
2222

2323

2424
def get_from_cache(base_cache_dir: pathlib.Path, path):
25-
cache_path = base_cache_dir / path
25+
cache_path = (base_cache_dir / path).resolve()
26+
# make sure cache_path is a subdirectory of base_cache_dir
27+
# so we don't accidentally read files from the parent directory
28+
# which is a security risk
29+
if not str(cache_path).startswith(str(base_cache_dir.resolve())):
30+
logger.warning("Trying to read from outside of cache directory: %s", cache_path)
31+
raise PermissionError("Trying to read from outside of cache directory")
32+
2633
try:
2734
logger.info("Opening cache file: %s", cache_path)
2835
return cache_path.read_bytes()

tests/unit/cdn_helper_test.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
import os
33
from pathlib import Path
44

5+
from pytest import TempPathFactory
6+
import pytest
7+
58
from solara.server.cdn_helper import get_cdn_url, get_data, get_from_cache, put_in_cache, get_path
69

710

@@ -68,6 +71,26 @@ def test_get_data(tmp_path_factory):
6871
assert data == b"test_cached_2"
6972

7073

74+
def test_get_data_secure(tmp_path_factory: TempPathFactory):
75+
# we should never be able to get data from the parent directory
76+
77+
root_dir = tmp_path_factory.mktemp("root")
78+
base_cache_dir = root_dir / "cdn"
79+
base_cache_dir.mkdir()
80+
81+
(root_dir / "secret").write_bytes(b"not allowed")
82+
83+
project_dir = base_cache_dir / "project"
84+
project_dir.mkdir()
85+
86+
(project_dir / "file").write_bytes(b"a")
87+
88+
data = get_data(base_cache_dir, "project/file")
89+
assert data == b"a"
90+
with pytest.raises(PermissionError):
91+
get_data(base_cache_dir, "project/../../secret")
92+
93+
7194
def test_redirect(tmp_path_factory):
7295
base_cache_dir = tmp_path_factory.mktemp("cdn")
7396

0 commit comments

Comments
 (0)