fix(api): block path traversal in download_work_dir_file (CVE-2026-4307)
Reject download requests whose resolved path escapes the runtime base directory before file metadata lookup or streaming. This keeps valid in-base absolute paths working in both Docker and development setups while preventing arbitrary file reads via /download_work_dir_file (CVE-2026-4307). Reported by Edward-x (@YLChen-007). Thanks again. Refs: - https://nvd.nist.gov/vuln/detail/CVE-2026-4307 - https://gist.github.com/YLChen-007/1819c843ad26aaaaecdc768a789df022 - https://vuldb.com/vuln/351337/cti
Alessandro committed
Apr 12, 2026 at 02:31 UTC
0e3e8a159abd97dc0da3b50215465aaaa546e6b5
1 file changed
+26
api/download_work_dir_file.py
+26
@@ -2,6 +2,7 @@ import base64
2
from io import BytesIO
3
import mimetypes
4
import os
5
+from pathlib import Path
6
7
from flask import Response
8
from helpers.api import ApiHandler, Input, Output, Request
@@ -85,6 +86,24 @@ def make_disposition(download_name: str) -> str:
86
return f'attachment; filename="{ascii_fallback}"; filename*=UTF-8\'\'{utf8_name}'
87
88
89
+def resolve_download_path(path: str) -> str:
90
+ """Resolve a requested download path and keep it within the runtime base dir."""
91
+ base_dir = Path(files.get_base_dir()).resolve()
92
+ candidate = Path(path)
93
+
94
+ if candidate.is_absolute():
95
+ resolved = candidate.resolve()
96
+ else:
97
+ resolved = (base_dir / candidate).resolve()
98
+
99
+ try:
100
+ resolved.relative_to(base_dir)
101
+ except ValueError as exc:
102
+ raise ValueError("Invalid file path") from exc
103
+
104
+ return str(resolved)
105
+
106
+
107
class DownloadFile(ApiHandler):
108
109
@classmethod
@@ -98,6 +117,13 @@ class DownloadFile(ApiHandler):
117
if not file_path.startswith("/"):
118
file_path = f"/{file_path}"
119
120
+ try:
121
+ file_path = await runtime.call_development_function(
122
+ resolve_download_path, file_path
123
+ )
124
+ except ValueError as exc:
125
+ return Response(str(exc), status=400)
126
+
127
file = await runtime.call_development_function(
128
file_info.get_file_info, file_path
129
)