diff --git a/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md index 08012d0593..ce5810d7ca 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)) + * Sanitize the object name for SQL query text using the LOGIN or USER keywords and remove from query summaries. ([#3663](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/3663)) diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md index 068333d93d..b1652d5ee5 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)) + * Sanitize the object name for SQL query text using the LOGIN or USER keywords and remove from query summaries. ([#3663](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/3663)) diff --git a/src/Shared/SqlProcessor.cs b/src/Shared/SqlProcessor.cs index 0f8e668c1f..4f19ebdd91 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(); @@ -672,6 +673,10 @@ private static bool SanitizeStringLiteral(ReadOnlySpan sql, Span buf return true; } + // 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; while (searchPos < sql.Length) @@ -686,6 +691,12 @@ private static bool SanitizeStringLiteral(ReadOnlySpan sql, Span buf } // Found terminating quote + if (isUnicode) + { + // Skip the Unicode prefix by overwriting the previous position instead + 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 b227615849..6edba788d8 100644 --- a/test/OpenTelemetry.Contrib.Shared.Tests/SqlProcessorAdditionalTestCases.json +++ b/test/OpenTelemetry.Contrib.Shared.Tests/SqlProcessorAdditionalTestCases.json @@ -905,7 +905,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]" } @@ -1052,5 +1052,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" + } } ]