From 79206f391912b917135037b4877da52cfbff92c8 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 3 Apr 2026 17:57:49 +0100 Subject: [PATCH] fix: harden faiss --- application/vectorstore/faiss.py | 32 +++++++++++++++++++++++++++----- tests/vectorstore/test_faiss.py | 22 ++++++++++++++++++++++ 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/application/vectorstore/faiss.py b/application/vectorstore/faiss.py index 3e86203b..5c926d4d 100644 --- a/application/vectorstore/faiss.py +++ b/application/vectorstore/faiss.py @@ -11,11 +11,33 @@ from application.storage.storage_creator import StorageCreator def get_vectorstore(path: str) -> str: - if path: - vectorstore = f"indexes/{path}" - else: - vectorstore = "indexes" - return vectorstore + """Build a safe local path for a FAISS index. + + Args: + path: Source identifier provided by the caller. + + Returns: + The validated vectorstore path rooted under ``indexes``. + + Raises: + ValueError: If ``path`` escapes the ``indexes`` directory. + """ + base_dir = "indexes" + if not path: + return base_dir + + normalized = str(path).strip() + if "\\" in normalized: + raise ValueError("Invalid source_id path") + + candidate = os.path.normpath(os.path.join(base_dir, normalized)) + base_abs = os.path.abspath(base_dir) + candidate_abs = os.path.abspath(candidate) + + if not candidate_abs.startswith(base_abs + os.sep) and candidate_abs != base_abs: + raise ValueError("Invalid source_id path") + + return candidate class FaissStore(BaseVectorStore): diff --git a/tests/vectorstore/test_faiss.py b/tests/vectorstore/test_faiss.py index c9fd40e3..4529f856 100644 --- a/tests/vectorstore/test_faiss.py +++ b/tests/vectorstore/test_faiss.py @@ -363,6 +363,28 @@ class TestGetVectorstore: assert get_vectorstore("user/source123") == "indexes/user/source123" + @pytest.mark.parametrize( + "malicious_path", + [ + "../outside", + "../../etc/passwd", + "nested/../../../outside", + "/tmp/evil", + "..\\outside", + "valid/../../escape", + ], + ) + def test_rejects_path_traversal(self, malicious_path): + from application.vectorstore.faiss import get_vectorstore + + with pytest.raises(ValueError, match="Invalid source_id path"): + get_vectorstore(malicious_path) + + def test_allows_mongodb_style_ids(self): + from application.vectorstore.faiss import get_vectorstore + + assert get_vectorstore("65e8f6a8a7a96b1bdad4154f") == "indexes/65e8f6a8a7a96b1bdad4154f" + @pytest.mark.unit class TestFaissStoreAddChunk: