fix: require auth for agent file downloads#3067
Conversation
There was a problem hiding this comment.
Pull request overview
This PR hardens the /v1/agent/files/download endpoint by aligning it with nearby agent/skill endpoints’ authentication dependency and restricting downloads to agent-created temp directories, with regression tests intended to prevent future path-boundary regressions.
Changes:
- Adds a user dependency (
Depends(get_user_from_headers)) to the agent file download route. - Removes
ROOT_PATHfrom the allowlist so downloads are restricted to/tmpandPILOT_PATH/tmp. - Adds tests intended to validate the auth dependency and allowlist/path-boundary behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| packages/dbgpt-app/src/dbgpt_app/openapi/api_v1/agentic_data_api.py | Adds user dependency to the route and tightens allowlist by removing ROOT_PATH. |
| packages/dbgpt-app/src/dbgpt_app/tests/test_agent_file_download_api.py | Adds regression tests for dependency presence and allowed/denied download paths. |
Comments suppressed due to low confidence (1)
packages/dbgpt-app/src/dbgpt_app/openapi/api_v1/agentic_data_api.py:3989
- The
file_pathparameter description says “Absolute path”, but the handler still accepts relative paths and resolves them againstROOT_PATH. SinceROOT_PATHhas been removed from the allowlist, relative paths will now typically resolve into a directory that is denied, which is confusing for API consumers. Either enforce absolute-only input (and drop the ROOT_PATH join) or update the description/logic so relative paths resolve into an allowed base directory.
async def download_agent_file(
file_path: str = Query(..., description="Absolute path to the file to download"),
user_token: UserRequest = Depends(get_user_from_headers),
):
"""Download a file created by agent tools (shell_interpreter, code_interpreter).
Only files under allowed directories (/tmp, PILOT_PATH/tmp/) can be downloaded.
This prevents arbitrary file access on the server.
"""
from fastapi import HTTPException
from fastapi.responses import FileResponse
from dbgpt.configs.model_config import PILOT_PATH, ROOT_PATH
# If path is not absolute, resolve relative to ROOT_PATH (sandbox working dir)
if not os.path.isabs(file_path):
file_path = os.path.join(ROOT_PATH, file_path)
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| async def test_agent_file_download_rejects_project_root_file(tmp_path, monkeypatch): | ||
| from dbgpt.configs import model_config | ||
| from dbgpt_app.openapi.api_v1 import agentic_data_api | ||
|
|
||
| project_file = tmp_path / "docker-compose.yml" | ||
| project_file.write_text("services: {}\n", encoding="utf-8") | ||
| monkeypatch.setattr(model_config, "ROOT_PATH", str(tmp_path)) | ||
| monkeypatch.setattr(model_config, "PILOT_PATH", str(tmp_path / "pilot")) | ||
|
|
||
| with pytest.raises(HTTPException) as exc_info: | ||
| await agentic_data_api.download_agent_file(str(project_file)) | ||
|
|
||
| assert exc_info.value.status_code == 403 |
| async def test_agent_file_download_allows_pilot_tmp_file(tmp_path, monkeypatch): | ||
| from dbgpt.configs import model_config | ||
| from dbgpt_app.openapi.api_v1 import agentic_data_api | ||
|
|
||
| pilot_tmp = tmp_path / "pilot" / "tmp" | ||
| pilot_tmp.mkdir(parents=True) | ||
| generated_file = pilot_tmp / "result.txt" | ||
| generated_file.write_text("ok\n", encoding="utf-8") | ||
| monkeypatch.setattr(model_config, "ROOT_PATH", str(tmp_path / "project")) | ||
| monkeypatch.setattr(model_config, "PILOT_PATH", str(tmp_path / "pilot")) | ||
|
|
||
| response = await agentic_data_api.download_agent_file(str(generated_file)) | ||
|
|
||
| assert isinstance(response, FileResponse) | ||
| assert response.path == str(generated_file) |
| @router.get("/v1/agent/files/download") | ||
| async def download_agent_file( | ||
| file_path: str = Query(..., description="Absolute path to the file to download"), | ||
| user_token: UserRequest = Depends(get_user_from_headers), | ||
| ): |
|
Thanks for your fix. A note on removing |
10b4568 to
228b143
Compare
Summary
/v1/agent/files/download/tmpandPILOT_PATH/tmpas the agent-created file directoriesFixes #3018
Tests