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 )