From ba37e8e49b0f945c7cedd7733e040287046ec802 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Tue, 21 Apr 2026 15:17:31 +0100 Subject: [PATCH] .Net: Add backslash escaping to redis text search values (#13902) ### Motivation and Context ### Description - Add backslash escaping to redis text search values ### Contribution Checklist - [ ] The code builds clean without any errors or warnings - [ ] The PR follows the [SK Contribution Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md) and the [pre-submission formatting script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts) raises no violations - [ ] All unit tests pass, and I have added new tests where possible - [ ] I didn't break anyone :smile: --- .../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 d69655d4af..78643ead0c 100644 --- a/dotnet/src/VectorData/Redis/RedisFilterTranslator.cs +++ b/dotnet/src/VectorData/Redis/RedisFilterTranslator.cs @@ -258,8 +258,8 @@ internal class RedisFilterTranslator : FilterTranslatorBase 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 789a1c2514..3b937aa3fe 100644 --- a/dotnet/test/VectorData/Redis.UnitTests/RedisFilterTranslatorTests.cs +++ b/dotnet/test/VectorData/Redis.UnitTests/RedisFilterTranslatorTests.cs @@ -108,6 +108,58 @@ public sealed class RedisFilterTranslatorTests 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();