oracle-haystack 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {oracle_haystack-0.2.0 → oracle_haystack-0.3.0}/CHANGELOG.md +7 -0
- {oracle_haystack-0.2.0 → oracle_haystack-0.3.0}/PKG-INFO +1 -1
- {oracle_haystack-0.2.0 → oracle_haystack-0.3.0}/src/haystack_integrations/document_stores/oracle/document_store.py +1 -8
- {oracle_haystack-0.2.0 → oracle_haystack-0.3.0}/src/haystack_integrations/document_stores/oracle/filters.py +11 -8
- {oracle_haystack-0.2.0 → oracle_haystack-0.3.0}/tests/test_filter_translator.py +24 -0
- {oracle_haystack-0.2.0 → oracle_haystack-0.3.0}/.gitignore +0 -0
- {oracle_haystack-0.2.0 → oracle_haystack-0.3.0}/README.md +0 -0
- {oracle_haystack-0.2.0 → oracle_haystack-0.3.0}/docker-compose.yml +0 -0
- {oracle_haystack-0.2.0 → oracle_haystack-0.3.0}/init/01_vector_memory.sql +0 -0
- {oracle_haystack-0.2.0 → oracle_haystack-0.3.0}/pydoc/config_docusaurus.yml +0 -0
- {oracle_haystack-0.2.0 → oracle_haystack-0.3.0}/pyproject.toml +0 -0
- {oracle_haystack-0.2.0 → oracle_haystack-0.3.0}/src/haystack_integrations/components/retrievers/oracle/__init__.py +0 -0
- {oracle_haystack-0.2.0 → oracle_haystack-0.3.0}/src/haystack_integrations/components/retrievers/oracle/embedding_retriever.py +0 -0
- {oracle_haystack-0.2.0 → oracle_haystack-0.3.0}/src/haystack_integrations/components/retrievers/oracle/keyword_retriever.py +0 -0
- {oracle_haystack-0.2.0/src/haystack_integrations/components/retrievers/oracle → oracle_haystack-0.3.0/src/haystack_integrations/components/retrievers}/py.typed +0 -0
- {oracle_haystack-0.2.0 → oracle_haystack-0.3.0}/src/haystack_integrations/document_stores/oracle/__about__.py +0 -0
- {oracle_haystack-0.2.0 → oracle_haystack-0.3.0}/src/haystack_integrations/document_stores/oracle/__init__.py +0 -0
- {oracle_haystack-0.2.0/src/haystack_integrations/document_stores/oracle → oracle_haystack-0.3.0/src/haystack_integrations/document_stores}/py.typed +0 -0
- {oracle_haystack-0.2.0 → oracle_haystack-0.3.0}/tests/__init__.py +0 -0
- {oracle_haystack-0.2.0 → oracle_haystack-0.3.0}/tests/conftest.py +0 -0
- {oracle_haystack-0.2.0 → oracle_haystack-0.3.0}/tests/test_document_store.py +0 -0
- {oracle_haystack-0.2.0 → oracle_haystack-0.3.0}/tests/test_embedding_retriever.py +0 -0
- {oracle_haystack-0.2.0 → oracle_haystack-0.3.0}/tests/test_keyword_retriever.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: oracle-haystack
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Oracle AI Vector Search DocumentStore integration for Haystack
|
|
5
5
|
Project-URL: Source Code, https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/oracle
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/deepset-ai/haystack-core-integrations/issues
|
|
@@ -18,21 +18,14 @@ from haystack.document_stores.errors import DocumentStoreError, DuplicateDocumen
|
|
|
18
18
|
from haystack.document_stores.types import DuplicatePolicy
|
|
19
19
|
from haystack.utils import Secret, deserialize_secrets_inplace
|
|
20
20
|
|
|
21
|
-
from .filters import FilterTranslator
|
|
21
|
+
from .filters import FilterTranslator, _validate_field_path
|
|
22
22
|
|
|
23
23
|
logger = logging.getLogger(__name__)
|
|
24
24
|
|
|
25
25
|
_SAFE_TABLE_NAME = re.compile(r"^[A-Za-z_][A-Za-z0-9_$#]{0,127}$")
|
|
26
|
-
_SAFE_FIELD_PATH = re.compile(r"^[A-Za-z0-9_.]+$")
|
|
27
26
|
MAX_INDEX_NAME_LEN = 128
|
|
28
27
|
|
|
29
28
|
|
|
30
|
-
def _validate_field_path(field_path: str) -> None:
|
|
31
|
-
if not _SAFE_FIELD_PATH.match(field_path):
|
|
32
|
-
msg = f"Invalid metadata field name: {field_path!r}"
|
|
33
|
-
raise ValueError(msg)
|
|
34
|
-
|
|
35
|
-
|
|
36
29
|
def _try_parse_number(value: Any) -> Any:
|
|
37
30
|
"""
|
|
38
31
|
Attempt to parse a string as a number.
|
|
@@ -2,12 +2,20 @@
|
|
|
2
2
|
#
|
|
3
3
|
# SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
|
|
5
|
+
import re
|
|
5
6
|
from datetime import datetime
|
|
6
7
|
from typing import Any, ClassVar
|
|
7
8
|
|
|
8
9
|
from haystack.errors import FilterError
|
|
9
10
|
|
|
10
11
|
_RANGE_OPS = {">", ">=", "<", "<="}
|
|
12
|
+
_SAFE_FIELD_PATH = re.compile(r"^[A-Za-z0-9_.]+$")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _validate_field_path(field_path: str) -> None:
|
|
16
|
+
if not _SAFE_FIELD_PATH.match(field_path):
|
|
17
|
+
msg = f"Invalid metadata field name: {field_path!r}"
|
|
18
|
+
raise FilterError(msg)
|
|
11
19
|
|
|
12
20
|
|
|
13
21
|
class FilterTranslator:
|
|
@@ -123,14 +131,9 @@ class FilterTranslator:
|
|
|
123
131
|
return "id"
|
|
124
132
|
if field == "content":
|
|
125
133
|
return "text"
|
|
126
|
-
if field.startswith("meta.")
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if isinstance(value, (int, float)) and not isinstance(value, bool):
|
|
130
|
-
return f"TO_NUMBER({json_path})"
|
|
131
|
-
return json_path
|
|
132
|
-
# Fallback: treat as top-level JSON key
|
|
133
|
-
json_path = f"JSON_VALUE(metadata, '$.{field}')"
|
|
134
|
+
key = field[len("meta.") :] if field.startswith("meta.") else field
|
|
135
|
+
_validate_field_path(key)
|
|
136
|
+
json_path = f"JSON_VALUE(metadata, '$.{key}')"
|
|
134
137
|
if isinstance(value, (int, float)) and not isinstance(value, bool):
|
|
135
138
|
return f"TO_NUMBER({json_path})"
|
|
136
139
|
return json_path
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
#
|
|
3
3
|
# SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
|
|
5
|
+
import pytest
|
|
6
|
+
from haystack.errors import FilterError
|
|
7
|
+
|
|
5
8
|
from haystack_integrations.document_stores.oracle.filters import FilterTranslator
|
|
6
9
|
|
|
7
10
|
|
|
@@ -134,3 +137,24 @@ def test_param_counter_increments_correctly():
|
|
|
134
137
|
}
|
|
135
138
|
)
|
|
136
139
|
assert set(params.keys()) == {"p0", "p1", "p2"}
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def test_sql_injection_via_field_name_raises():
|
|
143
|
+
with pytest.raises(FilterError, match="Invalid metadata field name"):
|
|
144
|
+
_translate({"field": "meta.x') = 'x' OR 1=1 OR ('a", "operator": "==", "value": "y"})
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def test_sql_injection_via_fallback_field_raises():
|
|
148
|
+
# Injection via the non-meta fallback path
|
|
149
|
+
with pytest.raises(FilterError, match="Invalid metadata field name"):
|
|
150
|
+
_translate({"field": "x') OR 1=1--", "operator": "==", "value": "y"})
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def test_sql_injection_in_not_in_raises():
|
|
154
|
+
with pytest.raises(FilterError, match="Invalid metadata field name"):
|
|
155
|
+
_translate({"field": "meta.x') OR 1=1--", "operator": "in", "value": ["a", "b"]})
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def test_valid_nested_field_path_accepted():
|
|
159
|
+
sql, _ = _translate({"field": "meta.author.city", "operator": "==", "value": "Berlin"})
|
|
160
|
+
assert "'$.author.city'" in sql
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|