From 2ef0cf4a4eea57d66689a76326208237cc10bd56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E8=88=AA=E5=AE=87?= <3364451258@qq.com> Date: Sun, 5 Jul 2026 01:32:04 +0800 Subject: [PATCH] fix: add path traversal protection to ingest endpoint --- src/server/app.py | 12 ++++++++++++ tests/test_api.py | 20 ++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/server/app.py b/src/server/app.py index 8fc075b..f77b78a 100644 --- a/src/server/app.py +++ b/src/server/app.py @@ -12,6 +12,16 @@ from src.core.ingest import DocumentIngestor from src.core.search import Searcher +def _is_safe_path(path_str: str) -> bool: + """检查路径是否安全: 仅允许相对路径且不含 .. 穿越.""" + normalized = os.path.normpath(path_str) + if os.path.isabs(normalized): + return False + if ".." in normalized.split(os.sep): + return False + return True + + # -- 请求模型 -- class IngestRequest(BaseModel): file_path: str | None = None @@ -109,6 +119,8 @@ def ingest_document(req: IngestRequest): ingestor = _get_ingestor() try: if req.file_path: + if not _is_safe_path(req.file_path): + raise HTTPException(status_code=400, detail="不允许的路径") path = Path(req.file_path) if not path.exists(): raise HTTPException(status_code=404, detail=f"文件不存在: {req.file_path}") diff --git a/tests/test_api.py b/tests/test_api.py index 824a601..023c92a 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -90,3 +90,23 @@ class TestIngestEndpoint: assert response.status_code == 200 data = response.json() assert len(data["results"]) > 0 + + +class TestSecurity: + """安全测试.""" + + def test_ingest_rejects_path_traversal(self, client): + """拒绝路径遍历攻击.""" + response = client.post( + "/api/v1/ingest", + json={"file_path": "../../../etc/passwd"}, + ) + assert response.status_code in (400, 403) + + def test_ingest_rejects_absolute_path(self, client): + """拒绝绝对路径.""" + response = client.post( + "/api/v1/ingest", + json={"file_path": "C:\\Windows\\System32\\config\\SAM"}, + ) + assert response.status_code in (400, 403)