From ad803297dacfd9ea650877b4bffb436c105d54ab Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 7 Jan 2026 16:13:44 +0000 Subject: [PATCH 1/3] [SqlClient] Skip N from unicode strings Skip leading `N` in values of the form `N'foo'`. Resolves #3659. --- src/Shared/SqlProcessor.cs | 10 ++++++++++ .../SqlProcessorAdditionalTestCases.json | 15 ++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/Shared/SqlProcessor.cs b/src/Shared/SqlProcessor.cs index 869e39925a..e62239dd12 100644 --- a/src/Shared/SqlProcessor.cs +++ b/src/Shared/SqlProcessor.cs @@ -662,6 +662,10 @@ private static bool SanitizeStringLiteral(ReadOnlySpan sql, Span buf return true; } + // Is the string literal of the form N'foo'? + // If so, we want to skip the leading N when sanitizing. + bool isUnicode = state.ParsePosition >= 1 && sql[state.ParsePosition - 1] is 'N'; + // Use index arithmetic instead of slicing var searchPos = state.ParsePosition + 1; while (searchPos < sql.Length) @@ -676,6 +680,12 @@ private static bool SanitizeStringLiteral(ReadOnlySpan sql, Span buf } // Found terminating quote + if (isUnicode) + { + // Skip the leading N + state.SanitizedPosition--; + } + state.ParsePosition = searchPos + 1; buffer[state.SanitizedPosition++] = SanitizationPlaceholder; return true; diff --git a/test/OpenTelemetry.Contrib.Shared.Tests/SqlProcessorAdditionalTestCases.json b/test/OpenTelemetry.Contrib.Shared.Tests/SqlProcessorAdditionalTestCases.json index 5e97615f8c..1d69126fe5 100644 --- a/test/OpenTelemetry.Contrib.Shared.Tests/SqlProcessorAdditionalTestCases.json +++ b/test/OpenTelemetry.Contrib.Shared.Tests/SqlProcessorAdditionalTestCases.json @@ -866,7 +866,7 @@ }, "expected": { "db.query.text": [ - "DECLARE @__geräteIds_0 nvarchar(?) = N?;\n\nSELECT [t].[Id]\nFROM [Tests] AS [t]\nWHERE [t].[Id] IN (\n\tSELECT [g].[value]\n\tFROM OPENJSON(@__geräteIds_0) WITH ([value] bigint ?) AS [g]\n)" + "DECLARE @__geräteIds_0 nvarchar(?) = ?;\n\nSELECT [t].[Id]\nFROM [Tests] AS [t]\nWHERE [t].[Id] IN (\n\tSELECT [g].[value]\n\tFROM OPENJSON(@__geräteIds_0) WITH ([value] bigint ?) AS [g]\n)" ], "db.query.summary": "SELECT [Tests]" } @@ -1013,5 +1013,18 @@ ], "db.query.summary": "SELECT [Order-Details] [Customer.Info]" } + }, + { + "name": "unicode_string", + "input": { + "db.system.name": "other_sql", + "query": "SELECT * FROM USERS WHERE username = N'john.doe'" + }, + "expected": { + "db.query.text": [ + "SELECT * FROM USERS WHERE username = ?" + ], + "db.query.summary": "SELECT USERS" + } } ] From 69f70c6b10ff56aa93a19fc55c9fd90708aa1c3a Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 7 Jan 2026 16:20:29 +0000 Subject: [PATCH 2/3] [SqlClient] Update CHANGELOG Add changes to EFCore and SqlClient CHANGELOGs. --- .../CHANGELOG.md | 3 +++ src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md index f360358a9e..bfecb3cf0a 100644 --- a/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md @@ -6,6 +6,9 @@ cases for escaped identifiers. Optimize performance of parsing logic. ([#3627](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/3627)) +* Improve SQL parsing for sanitization for Unicode string literals. + ([#3662](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/3662)) + ## 1.14.0-beta.2 Released 2025-Nov-14 diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md index 83073163e5..40ffd0ad6e 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md @@ -11,6 +11,9 @@ cases for escaped identifiers. Optimize performance of parsing logic. ([#3627](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/3627)) +* Improve SQL parsing for sanitization for Unicode string literals. + ([#3662](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/3662)) + ## 1.14.0-beta.1 Released 2025-Nov-13 From 6ceb6967c73416fd1971fff268b9b990e6cd436b Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 7 Jan 2026 16:21:55 +0000 Subject: [PATCH 3/3] [SqlClient] Use constant for unicode prefix Use a constant for the prefix and expand on the comment. --- src/Shared/SqlProcessor.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Shared/SqlProcessor.cs b/src/Shared/SqlProcessor.cs index e62239dd12..b148f92b42 100644 --- a/src/Shared/SqlProcessor.cs +++ b/src/Shared/SqlProcessor.cs @@ -28,6 +28,7 @@ internal static class SqlProcessor private const char NewLineChar = '\n'; private const char CarriageReturnChar = '\r'; private const char TabChar = '\t'; + private const char UnicodePrefixChar = 'N'; private static readonly ConcurrentDictionary Cache = new(); @@ -662,9 +663,9 @@ private static bool SanitizeStringLiteral(ReadOnlySpan sql, Span buf return true; } - // Is the string literal of the form N'foo'? - // If so, we want to skip the leading N when sanitizing. - bool isUnicode = state.ParsePosition >= 1 && sql[state.ParsePosition - 1] is 'N'; + // Is the string literal of the form `N'foo'` (i.e. a Unicode literal)? + // If so, we want to skip the Unicode prefix when sanitizing. + bool isUnicode = state.ParsePosition >= 1 && sql[state.ParsePosition - 1] is UnicodePrefixChar; // Use index arithmetic instead of slicing var searchPos = state.ParsePosition + 1; @@ -682,7 +683,7 @@ private static bool SanitizeStringLiteral(ReadOnlySpan sql, Span buf // Found terminating quote if (isUnicode) { - // Skip the leading N + // Skip the Unicode prefix by overwriting the previous position instead state.SanitizedPosition--; }