From 857077d41f6aa1bb3dd770c70108af98653ddaa7 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Tue, 21 Apr 2026 13:46:31 +0000 Subject: [PATCH] Add backslash escaping to redis text search values --- .../VectorData/Redis/RedisFilterTranslator.cs | 4 +- .../RedisFilterTranslatorTests.cs | 52 +++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/dotnet/src/VectorData/Redis/RedisFilterTranslator.cs b/dotnet/src/VectorData/Redis/RedisFilterTranslator.cs index d69655d4afb2..78643ead0cc4 100644 --- a/dotnet/src/VectorData/Redis/RedisFilterTranslator.cs +++ b/dotnet/src/VectorData/Redis/RedisFilterTranslator.cs @@ -258,8 +258,8 @@ private void TranslateAny(Expression source, LambdaExpression lambda) private static string SanitizeStringConstant(string value) #if NET - => value.Replace("\"", "\\\"", StringComparison.Ordinal); + => value.Replace("\\", "\\\\", StringComparison.Ordinal).Replace("\"", "\\\"", StringComparison.Ordinal); #else - => value.Replace("\"", "\\\""); + => value.Replace("\\", "\\\\").Replace("\"", "\\\""); #endif } diff --git a/dotnet/test/VectorData/Redis.UnitTests/RedisFilterTranslatorTests.cs b/dotnet/test/VectorData/Redis.UnitTests/RedisFilterTranslatorTests.cs index 789a1c25142e..3b937aa3fe4e 100644 --- a/dotnet/test/VectorData/Redis.UnitTests/RedisFilterTranslatorTests.cs +++ b/dotnet/test/VectorData/Redis.UnitTests/RedisFilterTranslatorTests.cs @@ -108,6 +108,58 @@ public void Equal_with_double_quote_in_value() Assert.Equal("""@Name:{"foo\"bar"}""", result); } + [Fact] + public void Equal_with_backslash_in_value() + { + var result = Translate(r => r.Name == "foo\\bar"); + Assert.Equal("""@Name:{"foo\\bar"}""", result); + } + + [Fact] + public void Equal_with_single_backslash() + { + var result = Translate(r => r.Name == "\\"); + Assert.Equal("""@Name:{"\\"}""", result); + } + + [Fact] + public void Equal_with_backslash_quote_injection_attempt() + { + // Input: \" which should NOT break out of the quoted string + var result = Translate(r => r.Name == "\\\""); + Assert.Equal("""@Name:{"\\\""}""", result); + } + + [Fact] + public void Equal_with_backslash_quote_wildcard_injection() + { + // The specific attack payload: \" | * | \" + var result = Translate(r => r.Name == "\\\" | * | \\\""); + Assert.Equal("""@Name:{"\\\" | * | \\\""}""", result); + } + + [Fact] + public void Contains_with_backslash_in_value() + { + var result = Translate(r => r.Tags.Contains("foo\\bar")); + Assert.Equal("""@Tags:{"foo\\bar"}""", result); + } + + [Fact] + public void Contains_with_backslash_quote_injection_attempt() + { + var result = Translate(r => r.Tags.Contains("\\\"")); + Assert.Equal("""@Tags:{"\\\""}""", result); + } + + [Fact] + public void Any_with_backslash_in_values() + { + var values = new[] { "a\\b", "c\\d" }; + var result = Translate(r => r.Tags.Any(t => values.Contains(t))); + Assert.Equal("""@Tags:{"a\\b" | "c\\d"}""", result); + } + private static string Translate(Expression> filter) { var model = BuildModel();