From d9884efaeea8322706e24c560d2c17e5bf3fab5f Mon Sep 17 00:00:00 2001 From: FFXN <31929997+FFXN@users.noreply.github.com> Date: Thu, 2 Jul 2026 14:22:39 +0800 Subject: [PATCH] fix: sql injection (#38295) Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../src/dify_vdb_myscale/myscale_vector.py | 13 ++++++++--- .../tests/unit_tests/test_myscale_vector.py | 22 +++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/api/providers/vdb/vdb-myscale/src/dify_vdb_myscale/myscale_vector.py b/api/providers/vdb/vdb-myscale/src/dify_vdb_myscale/myscale_vector.py index dadd1bb77a8..941d14693a0 100644 --- a/api/providers/vdb/vdb-myscale/src/dify_vdb_myscale/myscale_vector.py +++ b/api/providers/vdb/vdb-myscale/src/dify_vdb_myscale/myscale_vector.py @@ -132,9 +132,16 @@ class MyScaleVector(BaseVector): @override def search_by_full_text(self, query: str, **kwargs: Any) -> list[Document]: - return self._search(f"TextSearch('enable_nlq=false')(text, '{query}')", SortOrder.DESC, **kwargs) + return self._search( + "TextSearch('enable_nlq=false')(text, {query:String})", + SortOrder.DESC, + parameters={"query": query}, + **kwargs, + ) - def _search(self, dist: str, order: SortOrder, **kwargs: Any) -> list[Document]: + def _search( + self, dist: str, order: SortOrder, parameters: dict[str, Any] | None = None, **kwargs: Any + ) -> list[Document]: top_k = kwargs.get("top_k", 4) if not isinstance(top_k, int) or top_k <= 0: raise ValueError("top_k must be a positive integer") @@ -159,7 +166,7 @@ class MyScaleVector(BaseVector): vector=r["vector"], metadata=r["metadata"], ) - for r in self._client.query(sql).named_results() + for r in self._client.query(sql, parameters=parameters).named_results() ] except Exception: logger.exception("Vector search operation failed") diff --git a/api/providers/vdb/vdb-myscale/tests/unit_tests/test_myscale_vector.py b/api/providers/vdb/vdb-myscale/tests/unit_tests/test_myscale_vector.py index 900c75fdabf..b1ae1f84a31 100644 --- a/api/providers/vdb/vdb-myscale/tests/unit_tests/test_myscale_vector.py +++ b/api/providers/vdb/vdb-myscale/tests/unit_tests/test_myscale_vector.py @@ -199,6 +199,28 @@ def test_search_delegation_methods(myscale_module): assert result_vector == ["result"] assert result_text == ["result"] assert vector._search.call_count == 2 + vector._search.assert_any_call( + "TextSearch('enable_nlq=false')(text, {query:String})", + myscale_module.SortOrder.DESC, + parameters={"query": "hello"}, + top_k=2, + ) + + +def test_search_by_full_text_uses_query_parameters(myscale_module): + vector = myscale_module.MyScaleVector("collection_1", _config(myscale_module)) + vector._client.query.return_value = SimpleNamespace( + named_results=lambda: [{"text": "doc", "vector": [0.1], "metadata": {"doc_id": "1"}}] + ) + payload = "x') AS dist FROM dify.collection_1 UNION ALL SELECT secret FROM users --" + + docs = vector.search_by_full_text(payload, top_k=2) + + assert len(docs) == 1 + sql = vector._client.query.call_args.args[0] + assert payload not in sql + assert "TextSearch('enable_nlq=false')(text, {query:String})" in sql + assert vector._client.query.call_args.kwargs["parameters"] == {"query": payload} def test_search_with_document_filter_and_exception(myscale_module):