High severity7.5NVD Advisory· Published Mar 27, 2026· Updated Apr 16, 2026
CVE-2026-22744
CVE-2026-22744
Description
In RedisFilterExpressionConverter of spring-ai-redis-store, when a user-controlled string is passed as a filter value for a TAG field, stringValue() inserts the value directly into the @field:{VALUE} RediSearch TAG block without escaping characters.This issue affects Spring AI: from 1.0.0 before 1.0.5, from 1.1.0 before 1.1.4.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.springframework.ai:spring-ai-redis-storeMaven | >= 1.0.0-M5, < 1.0.5 | 1.0.5 |
org.springframework.ai:spring-ai-redis-storeMaven | >= 1.1.0-M1, < 1.1.4 | 1.1.4 |
Affected products
1Patches
1707e990c9152fix: RedisFilterExpressionConverter handling string values for TAG/TEXT filter values
2 files changed · +79 −8
vector-stores/spring-ai-redis-store/src/main/java/org/springframework/ai/vectorstore/redis/RedisFilterExpressionConverter.java+36 −5 modified@@ -22,6 +22,8 @@ import java.util.function.Function; import java.util.stream.Collectors; +import redis.clients.jedis.search.RediSearchUtil; + import org.springframework.ai.vectorstore.filter.Filter.Expression; import org.springframework.ai.vectorstore.filter.Filter.ExpressionType; import org.springframework.ai.vectorstore.filter.Filter.Group; @@ -117,12 +119,12 @@ private void doField(Expression expression, StringBuilder context) { break; case TAG: context.append("{"); - context.append(stringValue(expression, value)); + context.append(tagStringValue(expression, value)); context.append("}"); break; case TEXT: context.append("("); - context.append(stringValue(expression, value)); + context.append(textStringValue(expression, value)); context.append(")"); break; default: @@ -131,12 +133,41 @@ private void doField(Expression expression, StringBuilder context) { } } - private Object stringValue(Expression expression, Value value) { + private String tagStringValue(Expression expression, Value value) { + String delimiter = tagValueDelimiter(expression); + if (value.value() instanceof List<?> list) { + return list.stream().map(String::valueOf).map(this::escapeTagValue).collect(Collectors.joining(delimiter)); + } + return escapeTagValue(String.valueOf(value.value())); + } + + private String textStringValue(Expression expression, Value value) { String delimiter = tagValueDelimiter(expression); if (value.value() instanceof List<?> list) { - return String.join(delimiter, list.stream().map(String::valueOf).toList()); + return list.stream() + .map(String::valueOf) + .map(RediSearchUtil::escapeQuery) + .collect(Collectors.joining(delimiter)); + } + return RediSearchUtil.escapeQuery(String.valueOf(value.value())); + } + + /** + * Escapes characters that have special meaning inside a RediSearch TAG query clause + * ({@code @field:\{value\}}). The following characters are escaped with a backslash: + * {@code $}, {@code \}, {@code |}, {@code {}, {@code }}, {@code (}, {@code )}, + * {@code [}, {@code ]}, {@code -}, and {@code '}. + */ + private String escapeTagValue(String value) { + StringBuilder sb = new StringBuilder(value.length()); + for (int i = 0; i < value.length(); i++) { + char c = value.charAt(i); + switch (c) { + case '\\', '$', '|', '{', '}', '(', ')', '[', ']', '-', '\'' -> sb.append('\\').append(c); + default -> sb.append(c); + } } - return value.value(); + return sb.toString(); } private String tagValueDelimiter(Expression expression) {
vector-stores/spring-ai-redis-store/src/test/java/org/springframework/ai/vectorstore/redis/RedisFilterExpressionConverterTests.java+43 −3 modified@@ -131,12 +131,52 @@ void testComplexIdentifiers() { @Test void testSpecialCharactersInValues() { - // Test values with Redis special characters that need escaping String vectorExpr = converter(RedisVectorStore.MetadataField.tag("description")) .convertExpression(new Expression(EQ, new Key("description"), new Value("test@value{with}special|chars"))); - // Should properly escape special Redis characters - assertThat(vectorExpr).isEqualTo("@description:{test@value{with}special|chars}"); + assertThat(vectorExpr).isEqualTo("@description:{test@value\\{with\\}special\\|chars}"); + } + + @Test + void testTagValueWithInjectionPayload() { + String vectorExpr = converter(RedisVectorStore.MetadataField.tag("category")).convertExpression( + new Expression(EQ, new Key("category"), new Value("science} | @access_level:{restricted"))); + + assertThat(vectorExpr).isEqualTo("@category:{science\\} \\| @access_level:\\{restricted}"); + assertThat(vectorExpr).doesNotContain("} | @"); + } + + @Test + void testTagValueInListWithSpecialChars() { + String vectorExpr = converter(RedisVectorStore.MetadataField.tag("category")).convertExpression(new Expression( + IN, new Key("category"), new Value(List.of("science} | @access_level:{restricted", "normal")))); + + assertThat(vectorExpr).isEqualTo("@category:{science\\} \\| @access_level:\\{restricted | normal}"); + assertThat(vectorExpr).doesNotContain("} | @"); + } + + @Test + void testTagValueWithPipe() { + String vectorExpr = converter(RedisVectorStore.MetadataField.tag("status")) + .convertExpression(new Expression(EQ, new Key("status"), new Value("active|inactive"))); + + assertThat(vectorExpr).isEqualTo("@status:{active\\|inactive}"); + } + + @Test + void testTagValueWithHyphen() { + String vectorExpr = converter(RedisVectorStore.MetadataField.tag("type")) + .convertExpression(new Expression(EQ, new Key("type"), new Value("non-fiction"))); + + assertThat(vectorExpr).isEqualTo("@type:{non\\-fiction}"); + } + + @Test + void testTextValueWithSpecialChars() { + String vectorExpr = converter(RedisVectorStore.MetadataField.text("description")) + .convertExpression(new Expression(EQ, new Key("description"), new Value("hello@world.com"))); + + assertThat(vectorExpr).isEqualTo("@description:(hello\\@world\\.com)"); } @Test
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-44f4-gvwj-6qg3ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-22744ghsaADVISORY
- spring.io/security/cve-2026-22744nvdVendor AdvisoryWEB
- github.com/spring-projects/spring-ai/commit/707e990c9152aabb9c9226053725efa2ada72223ghsaWEB
- github.com/spring-projects/spring-ai/releases/tag/v1.0.5ghsaWEB
- github.com/spring-projects/spring-ai/releases/tag/v1.1.4ghsaWEB
News mentions
0No linked articles in our index yet.