Skip to content

Commit ceb8394

Browse files
Merge branch 'main' into feat/supabase-groonga
2 parents 5c8d270 + 4141ac2 commit ceb8394

3 files changed

Lines changed: 52 additions & 1 deletion

File tree

integrations/arcadedb/CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# Changelog
22

3+
## [integrations/arcadedb-v1.3.0] - 2026-05-19
4+
5+
### 🐛 Bug Fixes
6+
7+
- Arcade db sql injection (#3329)
8+
9+
### 🧪 Testing
10+
11+
- Track test coverage for all integrations (#3065)
12+
- Arcadedb - add unit tests (#3192)
13+
14+
315
## [integrations/arcadedb-v1.2.0] - 2026-03-24
416

517
### 🚀 Features

integrations/arcadedb/src/haystack_integrations/document_stores/arcadedb/filters.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
"""Convert Haystack filter dictionaries to ArcadeDB SQL WHERE clauses."""
66

7+
import re
78
from typing import Any
89

910

@@ -64,6 +65,14 @@ def _parse_condition(condition: dict[str, Any]) -> str:
6465

6566

6667
def _comparison_to_sql(field: str, operator: str, value: Any) -> str:
68+
69+
if not re.match(r'^[a-zA-Z_][a-zA-Z0-9_.\[\]"]*$', field):
70+
msg = (
71+
f"Invalid field name: {field}. Field names must start with a letter or underscore and can contain "
72+
f"letters, digits, underscores, dots, brackets, and quotes."
73+
)
74+
raise ValueError(msg)
75+
6776
if operator == "==":
6877
if value is None:
6978
return f"{field} IS NULL"
@@ -106,7 +115,7 @@ def _comparison_to_sql(field: str, operator: str, value: Any) -> str:
106115
def _sql_value(value: Any) -> str:
107116
"""Format a Python value as an ArcadeDB SQL literal."""
108117
if isinstance(value, str):
109-
escaped = value.replace("'", "\\'")
118+
escaped = value.replace("\\", "\\\\").replace("'", "\\'")
110119
return f"'{escaped}'"
111120
if isinstance(value, bool):
112121
return "true" if value else "false"

integrations/arcadedb/tests/test_filters.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,33 @@ def test_conversion_edge_cases(self, filter_dict, expected):
149149
def test_invalid_filter_raises(self, filter_dict):
150150
with pytest.raises(ValueError):
151151
_convert_filters(filter_dict)
152+
153+
@pytest.mark.parametrize(
154+
"field",
155+
[
156+
"x; DROP TABLE Documents",
157+
"x OR 1=1",
158+
"x--",
159+
"x; SELECT *",
160+
"'injected'",
161+
"1field",
162+
"field name",
163+
],
164+
)
165+
def test_sql_injection_field_names_raise(self, field):
166+
with pytest.raises(ValueError, match="Invalid field name"):
167+
_convert_filters({"field": field, "operator": "==", "value": "v"})
168+
169+
def test_value_with_backslash(self):
170+
# A single backslash must be doubled: \ → \\
171+
result = _convert_filters({"field": "meta.x", "operator": "==", "value": "\\"})
172+
assert result == "meta.x = '\\\\'"
173+
174+
def test_value_with_backslash_then_quote(self):
175+
# \' in value → \\ (escaped backslash) + \' (escaped quote) in SQL
176+
result = _convert_filters({"field": "meta.x", "operator": "==", "value": "a\\'b"})
177+
assert result == "meta.x = 'a\\\\\\'b'"
178+
179+
def test_value_with_single_quote(self):
180+
result = _convert_filters({"field": "meta.x", "operator": "==", "value": "it's"})
181+
assert result == "meta.x = 'it\\'s'"

0 commit comments

Comments
 (0)