From e222ecb5942d4ce1cadfd4306c39e3f4933a5c42 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Fri, 12 Apr 2024 11:47:42 -0700 Subject: [PATCH] [Instrumentation.Http][Instrumentation.AspNetCore] Fix `url.full` and `url.query` attribute values (#5532) --- OpenTelemetry.sln | 1 + .../AspNetCoreTraceInstrumentationOptions.cs | 18 +++++ .../CHANGELOG.md | 12 ++- .../Implementation/HttpInListener.cs | 10 ++- ...elemetry.Instrumentation.AspNetCore.csproj | 1 + .../README.md | 6 +- .../CHANGELOG.md | 12 ++- ...entationTracerProviderBuilderExtensions.cs | 2 + .../HttpClientTraceInstrumentationOptions.cs | 34 ++++++++- .../HttpHandlerDiagnosticListener.cs | 2 +- .../HttpInstrumentationEventSource.cs | 14 +++- .../Implementation/HttpTagHelper.cs | 11 ++- .../HttpWebRequestActivitySource.netfx.cs | 2 +- .../OpenTelemetry.Instrumentation.Http.csproj | 5 ++ .../README.md | 6 +- src/Shared/RedactionHelper.cs | 59 +++++++++++++++ .../BasicTests.cs | 73 +++++++++++++++++++ ...stsCollectionsIsAccordingToTheSpecTests.cs | 7 +- .../HttpClientTests.Basic.cs | 64 ++++++++++++++++ ...HttpWebRequestActivitySourceTests.netfx.cs | 2 + .../Internal/RedactionHelperTest.cs | 32 ++++++++ .../OpenTelemetry.Tests.csproj | 1 + 22 files changed, 359 insertions(+), 15 deletions(-) create mode 100644 src/Shared/RedactionHelper.cs create mode 100644 test/OpenTelemetry.Tests/Internal/RedactionHelperTest.cs diff --git a/OpenTelemetry.sln b/OpenTelemetry.sln index 7a98293e6db..e9839f4190d 100644 --- a/OpenTelemetry.sln +++ b/OpenTelemetry.sln @@ -270,6 +270,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A49299 src\Shared\PeerServiceResolver.cs = src\Shared\PeerServiceResolver.cs src\Shared\PeriodicExportingMetricReaderHelper.cs = src\Shared\PeriodicExportingMetricReaderHelper.cs src\Shared\PooledList.cs = src\Shared\PooledList.cs + src\Shared\RedactionHelper.cs = src\Shared\RedactionHelper.cs src\Shared\RequestMethodHelper.cs = src\Shared\RequestMethodHelper.cs src\Shared\ResourceSemanticConventions.cs = src\Shared\ResourceSemanticConventions.cs src\Shared\SemanticConventions.cs = src\Shared\SemanticConventions.cs diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreTraceInstrumentationOptions.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreTraceInstrumentationOptions.cs index 38791ee2eba..f5ffb7962f8 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreTraceInstrumentationOptions.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreTraceInstrumentationOptions.cs @@ -32,6 +32,14 @@ internal AspNetCoreTraceInstrumentationOptions(IConfiguration configuration) { this.EnableGrpcAspNetCoreSupport = enableGrpcInstrumentation; } + + if (configuration.TryGetBoolValue( + AspNetCoreInstrumentationEventSource.Log, + "OTEL_DOTNET_EXPERIMENTAL_ASPNETCORE_DISABLE_URL_QUERY_REDACTION", + out var disableUrlQueryRedaction)) + { + this.DisableUrlQueryRedaction = disableUrlQueryRedaction; + } } /// @@ -94,4 +102,14 @@ internal AspNetCoreTraceInstrumentationOptions(IConfiguration configuration) /// https://github.com/open-telemetry/semantic-conventions/blob/main/docs/rpc/rpc-spans.md. /// internal bool EnableGrpcAspNetCoreSupport { get; set; } + + /// + /// Gets or sets a value indicating whether the url query value should be redacted or not. + /// + /// + /// The query parameter values are redacted with value set as Redacted. + /// e.g. `?key1=value1` is set as `?key1=Redacted`. + /// The redaction can be disabled by setting this property to . + /// + internal bool DisableUrlQueryRedaction { get; set; } } diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md index baf8bd6a324..d8f74ef6cc5 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md @@ -1,6 +1,16 @@ # Changelog -## Unreleased +## 1.8.1 + +Released 2024-Apr-12 + +* **Breaking Change**: Fixed tracing instrumentation so that by default any + values detected in the query string component of requests are replaced with + the text `Redacted` when building the `url.query` tag. For example, + `?key1=value1&key2=value2` becomes `?key1=Redacted&key2=Redacted`. You can + disable this redaction by setting the environment variable + `OTEL_DOTNET_EXPERIMENTAL_ASPNETCORE_DISABLE_URL_QUERY_REDACTION` to `true`. + ([#5532](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5532)) ## 1.8.0 diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs index 7d8652637fb..7b5942a1084 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs @@ -193,8 +193,14 @@ public void OnStartActivity(Activity activity, object payload) if (request.QueryString.HasValue) { - // QueryString should be sanitized. see: https://github.com/open-telemetry/opentelemetry-dotnet/issues/4571 - activity.SetTag(SemanticConventions.AttributeUrlQuery, request.QueryString.Value); + if (this.options.DisableUrlQueryRedaction) + { + activity.SetTag(SemanticConventions.AttributeUrlQuery, request.QueryString.Value); + } + else + { + activity.SetTag(SemanticConventions.AttributeUrlQuery, RedactionHelper.GetRedactedQueryString(request.QueryString.Value)); + } } RequestMethodHelper.SetHttpMethodTag(activity, request.Method); diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/OpenTelemetry.Instrumentation.AspNetCore.csproj b/src/OpenTelemetry.Instrumentation.AspNetCore/OpenTelemetry.Instrumentation.AspNetCore.csproj index a95dd1c1a14..e60747b9501 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/OpenTelemetry.Instrumentation.AspNetCore.csproj +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/OpenTelemetry.Instrumentation.AspNetCore.csproj @@ -17,6 +17,7 @@ + diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/README.md b/src/OpenTelemetry.Instrumentation.AspNetCore/README.md index e51a41be4a8..f8ef36ef2d8 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/README.md +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/README.md @@ -75,7 +75,11 @@ for more details about each individual attribute: * `server.address` * `server.port` * `url.path` -* `url.query` +* `url.query` - By default, the values in the query component are replaced with + the text `Redacted`. For example, `?key1=value1&key2=value2` becomes + `?key1=Redacted&key2=Redacted`. You can disable this redaction by setting the + environment variable + `OTEL_DOTNET_EXPERIMENTAL_ASPNETCORE_DISABLE_URL_QUERY_REDACTION` to `true`. * `url.scheme` [Enrich Api](#enrich) can be used if any additional attributes are diff --git a/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md index c3f7330208d..829e5952d79 100644 --- a/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md @@ -1,6 +1,16 @@ # Changelog -## Unreleased +## 1.8.1 + +Released 2024-Apr-12 + +* **Breaking Change**: Fixed tracing instrumentation so that by default any + values detected in the query string component of requests are replaced with + the text `Redacted` when building the `url.full` tag. For example, + `?key1=value1&key2=value2` becomes `?key1=Redacted&key2=Redacted`. You can + disable this redaction by setting the environment variable + `OTEL_DOTNET_EXPERIMENTAL_HTTPCLIENT_DISABLE_URL_QUERY_REDACTION` to `true`. + ([#5532](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5532)) ## 1.8.0 diff --git a/src/OpenTelemetry.Instrumentation.Http/HttpClientInstrumentationTracerProviderBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.Http/HttpClientInstrumentationTracerProviderBuilderExtensions.cs index 6015c183f2c..5f97d0342fc 100644 --- a/src/OpenTelemetry.Instrumentation.Http/HttpClientInstrumentationTracerProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Instrumentation.Http/HttpClientInstrumentationTracerProviderBuilderExtensions.cs @@ -59,6 +59,8 @@ public static TracerProviderBuilder AddHttpClientInstrumentation( { services.Configure(name, configureHttpClientTraceInstrumentationOptions); } + + services.RegisterOptionsFactory(configuration => new HttpClientTraceInstrumentationOptions(configuration)); }); #if NETFRAMEWORK diff --git a/src/OpenTelemetry.Instrumentation.Http/HttpClientTraceInstrumentationOptions.cs b/src/OpenTelemetry.Instrumentation.Http/HttpClientTraceInstrumentationOptions.cs index 95bb6dab7b0..a5fbec23cdc 100644 --- a/src/OpenTelemetry.Instrumentation.Http/HttpClientTraceInstrumentationOptions.cs +++ b/src/OpenTelemetry.Instrumentation.Http/HttpClientTraceInstrumentationOptions.cs @@ -3,10 +3,11 @@ using System.Diagnostics; using System.Net; +using System.Runtime.CompilerServices; #if NETFRAMEWORK using System.Net.Http; #endif -using System.Runtime.CompilerServices; +using Microsoft.Extensions.Configuration; using OpenTelemetry.Instrumentation.Http.Implementation; namespace OpenTelemetry.Instrumentation.Http; @@ -16,6 +17,27 @@ namespace OpenTelemetry.Instrumentation.Http; /// public class HttpClientTraceInstrumentationOptions { + /// + /// Initializes a new instance of the class. + /// + public HttpClientTraceInstrumentationOptions() + : this(new ConfigurationBuilder().AddEnvironmentVariables().Build()) + { + } + + internal HttpClientTraceInstrumentationOptions(IConfiguration configuration) + { + Debug.Assert(configuration != null, "configuration was null"); + + if (configuration.TryGetBoolValue( + HttpInstrumentationEventSource.Log, + "OTEL_DOTNET_EXPERIMENTAL_HTTPCLIENT_DISABLE_URL_QUERY_REDACTION", + out var disableUrlQueryRedaction)) + { + this.DisableUrlQueryRedaction = disableUrlQueryRedaction; + } + } + /// /// Gets or sets a filter function that determines whether or not to /// collect telemetry on a per request basis. @@ -125,6 +147,16 @@ public class HttpClientTraceInstrumentationOptions /// public bool RecordException { get; set; } + /// + /// Gets or sets a value indicating whether the url query value should be redacted or not. + /// + /// + /// The query parameter values are redacted with value set as Redacted. + /// e.g. `?key1=value1` is set as `?key1=Redacted`. + /// The redaction can be disabled by setting this property to . + /// + internal bool DisableUrlQueryRedaction { get; set; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal bool EventFilterHttpRequestMessage(string activityName, object arg1) { diff --git a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerDiagnosticListener.cs b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerDiagnosticListener.cs index 6b3bea0db4a..8dda9051812 100644 --- a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerDiagnosticListener.cs +++ b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerDiagnosticListener.cs @@ -149,7 +149,7 @@ public void OnStartActivity(Activity activity, object payload) activity.SetTag(SemanticConventions.AttributeServerAddress, request.RequestUri.Host); activity.SetTag(SemanticConventions.AttributeServerPort, request.RequestUri.Port); - activity.SetTag(SemanticConventions.AttributeUrlFull, HttpTagHelper.GetUriTagValueFromRequestUri(request.RequestUri)); + activity.SetTag(SemanticConventions.AttributeUrlFull, HttpTagHelper.GetUriTagValueFromRequestUri(request.RequestUri, this.options.DisableUrlQueryRedaction)); try { diff --git a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpInstrumentationEventSource.cs b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpInstrumentationEventSource.cs index 360fa496701..4f04f9b6671 100644 --- a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpInstrumentationEventSource.cs +++ b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpInstrumentationEventSource.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 using System.Diagnostics.Tracing; +using Microsoft.Extensions.Configuration; using OpenTelemetry.Internal; namespace OpenTelemetry.Instrumentation.Http.Implementation; @@ -10,7 +11,7 @@ namespace OpenTelemetry.Instrumentation.Http.Implementation; /// EventSource events emitted from the project. /// [EventSource(Name = "OpenTelemetry-Instrumentation-Http")] -internal sealed class HttpInstrumentationEventSource : EventSource +internal sealed class HttpInstrumentationEventSource : EventSource, IConfigurationExtensionsLogger { public static HttpInstrumentationEventSource Log = new(); @@ -100,4 +101,15 @@ public void UnknownErrorProcessingEvent(string handlerName, string eventName, st { this.WriteEvent(7, handlerName, eventName, ex); } + + [Event(8, Message = "Configuration key '{0}' has an invalid value: '{1}'", Level = EventLevel.Warning)] + public void InvalidConfigurationValue(string key, string value) + { + this.WriteEvent(8, key, value); + } + + void IConfigurationExtensionsLogger.LogInvalidConfigurationValue(string key, string value) + { + this.InvalidConfigurationValue(key, value); + } } diff --git a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpTagHelper.cs b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpTagHelper.cs index 82d7fd11bf2..0b5d7184fd0 100644 --- a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpTagHelper.cs +++ b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpTagHelper.cs @@ -1,6 +1,8 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +using OpenTelemetry.Internal; + namespace OpenTelemetry.Instrumentation.Http.Implementation; /// @@ -12,15 +14,18 @@ internal static class HttpTagHelper /// Gets the OpenTelemetry standard uri tag value for a span based on its request . /// /// . + /// Indicates whether query parameter should be redacted or not. /// Span uri value. - public static string GetUriTagValueFromRequestUri(Uri uri) + public static string GetUriTagValueFromRequestUri(Uri uri, bool disableQueryRedaction) { - if (string.IsNullOrEmpty(uri.UserInfo)) + if (string.IsNullOrEmpty(uri.UserInfo) && disableQueryRedaction) { return uri.OriginalString; } - return string.Concat(uri.Scheme, Uri.SchemeDelimiter, uri.Authority, uri.PathAndQuery, uri.Fragment); + var query = disableQueryRedaction ? uri.Query : RedactionHelper.GetRedactedQueryString(uri.Query); + + return string.Concat(uri.Scheme, Uri.SchemeDelimiter, uri.Authority, uri.AbsolutePath, query, uri.Fragment); } public static string GetProtocolVersionString(Version httpVersion) => (httpVersion.Major, httpVersion.Minor) switch diff --git a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs index 1595a0a71f4..1418bc0f5ae 100644 --- a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs +++ b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs @@ -105,7 +105,7 @@ private static void AddRequestTagsAndInstrumentRequest(HttpWebRequest request, A activity.SetTag(SemanticConventions.AttributeServerAddress, request.RequestUri.Host); activity.SetTag(SemanticConventions.AttributeServerPort, request.RequestUri.Port); - activity.SetTag(SemanticConventions.AttributeUrlFull, HttpTagHelper.GetUriTagValueFromRequestUri(request.RequestUri)); + activity.SetTag(SemanticConventions.AttributeUrlFull, HttpTagHelper.GetUriTagValueFromRequestUri(request.RequestUri, tracingOptions.DisableUrlQueryRedaction)); try { diff --git a/src/OpenTelemetry.Instrumentation.Http/OpenTelemetry.Instrumentation.Http.csproj b/src/OpenTelemetry.Instrumentation.Http/OpenTelemetry.Instrumentation.Http.csproj index 55158028cde..35e43a09e68 100644 --- a/src/OpenTelemetry.Instrumentation.Http/OpenTelemetry.Instrumentation.Http.csproj +++ b/src/OpenTelemetry.Instrumentation.Http/OpenTelemetry.Instrumentation.Http.csproj @@ -12,8 +12,12 @@ + + + + @@ -29,6 +33,7 @@ + diff --git a/src/OpenTelemetry.Instrumentation.Http/README.md b/src/OpenTelemetry.Instrumentation.Http/README.md index e9177766ec4..b3fec4ebe45 100644 --- a/src/OpenTelemetry.Instrumentation.Http/README.md +++ b/src/OpenTelemetry.Instrumentation.Http/README.md @@ -68,7 +68,11 @@ for more details about each individual attribute: * `network.protocol.version` * `server.address` * `server.port` -* `url.full` +* `url.full` - By default, the values in the query component of the url are + replaced with the text `Redacted`. For example, `?key1=value1&key2=value2` + becomes `?key1=Redacted&key2=Redacted`. You can disable this redaction by + setting the environment variable + `OTEL_DOTNET_EXPERIMENTAL_HTTPCLIENT_DISABLE_URL_QUERY_REDACTION` to `true`. [Enrich Api](#enrich-httpclient-api) can be used if any additional attributes are required on activity. diff --git a/src/Shared/RedactionHelper.cs b/src/Shared/RedactionHelper.cs new file mode 100644 index 00000000000..05944efada5 --- /dev/null +++ b/src/Shared/RedactionHelper.cs @@ -0,0 +1,59 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#nullable enable + +using System.Text; + +namespace OpenTelemetry.Internal; + +internal class RedactionHelper +{ + private static readonly string RedactedText = "Redacted"; + + public static string? GetRedactedQueryString(string query) + { + int length = query.Length; + int index = 0; + + // Preallocate some size to avoid re-sizing multiple times. + // Since the size will increase, allocating twice as much. + // TODO: Check to see if we can borrow from https://github.com/dotnet/runtime/blob/main/src/libraries/Common/src/System/Text/ValueStringBuilder.cs + // to improve perf. + StringBuilder queryBuilder = new(2 * length); + while (index < query.Length) + { + // Check if the character is = for redacting value. + if (query[index] == '=') + { + // Append = + queryBuilder.Append('='); + index++; + + // Append redactedText in place of original value. + queryBuilder.Append(RedactedText); + + // Move until end of this key/value pair. + while (index < length && query[index] != '&') + { + index++; + } + + // End of key/value. + if (index < length && query[index] == '&') + { + queryBuilder.Append(query[index]); + } + } + else + { + // Keep adding to the result + queryBuilder.Append(query[index]); + } + + index++; + } + + return queryBuilder.ToString(); + } +} diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs index 360f9b5427b..03f6f4c162e 100644 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs +++ b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs @@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -1052,6 +1053,78 @@ public async Task NoSiblingActivityCreatedWhenTraceFlagsNone() } #endif + [Theory] + [InlineData("?a", "?a", false)] + [InlineData("?a=bdjdjh", "?a=Redacted", false)] + [InlineData("?a=b&", "?a=Redacted&", false)] + [InlineData("?c=b&", "?c=Redacted&", false)] + [InlineData("?c=a", "?c=Redacted", false)] + [InlineData("?a=b&c", "?a=Redacted&c", false)] + [InlineData("?a=b&c=1123456&", "?a=Redacted&c=Redacted&", false)] + [InlineData("?a=b&c=1&a1", "?a=Redacted&c=Redacted&a1", false)] + [InlineData("?a=ghgjgj&c=1deedd&a1=", "?a=Redacted&c=Redacted&a1=Redacted", false)] + [InlineData("?a=b&c=11&a1=&", "?a=Redacted&c=Redacted&a1=Redacted&", false)] + [InlineData("?c&c&c&", "?c&c&c&", false)] + [InlineData("?a&a&a&a", "?a&a&a&a", false)] + [InlineData("?&&&&&&&", "?&&&&&&&", false)] + [InlineData("?c", "?c", false)] + [InlineData("?a", "?a", true)] + [InlineData("?a=bdfdfdf", "?a=bdfdfdf", true)] + [InlineData("?a=b&", "?a=b&", true)] + [InlineData("?c=b&", "?c=b&", true)] + [InlineData("?c=a", "?c=a", true)] + [InlineData("?a=b&c", "?a=b&c", true)] + [InlineData("?a=b&c=111111&", "?a=b&c=111111&", true)] + [InlineData("?a=b&c=1&a1", "?a=b&c=1&a1", true)] + [InlineData("?a=b&c=1&a1=", "?a=b&c=1&a1=", true)] + [InlineData("?a=b123&c=11&a1=&", "?a=b123&c=11&a1=&", true)] + [InlineData("?c&c&c&", "?c&c&c&", true)] + [InlineData("?a&a&a&a", "?a&a&a&a", true)] + [InlineData("?&&&&&&&", "?&&&&&&&", true)] + [InlineData("?c", "?c", true)] + [InlineData("?c=%26&", "?c=Redacted&", false)] + public async Task ValidateUrlQueryRedaction(string urlQuery, string expectedUrlQuery, bool disableQueryRedaction) + { + var exportedItems = new List(); + + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary { ["OTEL_DOTNET_EXPERIMENTAL_ASPNETCORE_DISABLE_URL_QUERY_REDACTION"] = disableQueryRedaction.ToString() }) + .Build(); + + var path = "/api/values" + urlQuery; + + // Arrange + using var traceprovider = Sdk.CreateTracerProviderBuilder() + .ConfigureServices(services => services.AddSingleton(configuration)) + .AddAspNetCoreInstrumentation() + .AddInMemoryExporter(exportedItems) + .Build(); + + using (var client = this.factory + .WithWebHostBuilder(builder => + { + builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); + }) + .CreateClient()) + { + try + { + using var response = await client.GetAsync(path); + } + catch (Exception) + { + // ignore errors + } + + WaitForActivityExport(exportedItems, 1); + } + + Assert.Single(exportedItems); + var activity = exportedItems[0]; + + Assert.Equal(expectedUrlQuery, activity.GetTagValue(SemanticConventions.AttributeUrlQuery)); + } + public void Dispose() { this.tracerProvider?.Dispose(); diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/IncomingRequestsCollectionsIsAccordingToTheSpecTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/IncomingRequestsCollectionsIsAccordingToTheSpecTests.cs index ee2195c6a4c..d5777dbc6f3 100644 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/IncomingRequestsCollectionsIsAccordingToTheSpecTests.cs +++ b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/IncomingRequestsCollectionsIsAccordingToTheSpecTests.cs @@ -27,7 +27,7 @@ public IncomingRequestsCollectionsIsAccordingToTheSpecTests(WebApplicationFactor [Theory] [InlineData("/api/values", null, "user-agent", 200, null)] - [InlineData("/api/values", "?query=1", null, 200, null)] + [InlineData("/api/values", null, null, 200, null)] [InlineData("/api/exception", null, null, 503, null)] [InlineData("/api/exception", null, null, 503, null, true)] public async Task SuccessfulTemplateControllerCallGeneratesASpan_New( @@ -49,7 +49,10 @@ public async Task SuccessfulTemplateControllerCallGeneratesASpan_New( services.AddSingleton(new TestCallbackMiddlewareImpl(statusCode, reasonPhrase)); services.AddOpenTelemetry() .WithTracing(builder => builder - .AddAspNetCoreInstrumentation(options => options.RecordException = recordException) + .AddAspNetCoreInstrumentation(options => + { + options.RecordException = recordException; + }) .AddInMemoryExporter(exportedItems)); }); builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); diff --git a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.Basic.cs b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.Basic.cs index e6c0191b435..d9d58399951 100644 --- a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.Basic.cs +++ b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.Basic.cs @@ -2,8 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 using System.Diagnostics; +using Microsoft.Extensions.Configuration; + #if NETFRAMEWORK using System.Net.Http; + #endif using Microsoft.Extensions.DependencyInjection; using OpenTelemetry.Context.Propagation; @@ -666,6 +669,67 @@ public async Task DoesNotReportExceptionEventOnErrorResponseWithGetStringAsync() Assert.Empty(exportedItems[0].Events); } + [Theory] + [InlineData("?a", "?a", false)] + [InlineData("?a=bdjdjh", "?a=Redacted", false)] + [InlineData("?a=b&", "?a=Redacted&", false)] + [InlineData("?c=b&", "?c=Redacted&", false)] + [InlineData("?c=a", "?c=Redacted", false)] + [InlineData("?a=b&c", "?a=Redacted&c", false)] + [InlineData("?a=b&c=1123456&", "?a=Redacted&c=Redacted&", false)] + [InlineData("?a=b&c=1&a1", "?a=Redacted&c=Redacted&a1", false)] + [InlineData("?a=ghgjgj&c=1deedd&a1=", "?a=Redacted&c=Redacted&a1=Redacted", false)] + [InlineData("?a=b&c=11&a1=&", "?a=Redacted&c=Redacted&a1=Redacted&", false)] + [InlineData("?c&c&c&", "?c&c&c&", false)] + [InlineData("?a&a&a&a", "?a&a&a&a", false)] + [InlineData("?&&&&&&&", "?&&&&&&&", false)] + [InlineData("?c", "?c", false)] + [InlineData("?a", "?a", true)] + [InlineData("?a=bdfdfdf", "?a=bdfdfdf", true)] + [InlineData("?a=b&", "?a=b&", true)] + [InlineData("?c=b&", "?c=b&", true)] + [InlineData("?c=a", "?c=a", true)] + [InlineData("?a=b&c", "?a=b&c", true)] + [InlineData("?a=b&c=111111&", "?a=b&c=111111&", true)] + [InlineData("?a=b&c=1&a1", "?a=b&c=1&a1", true)] + [InlineData("?a=b&c=1&a1=", "?a=b&c=1&a1=", true)] + [InlineData("?a=b123&c=11&a1=&", "?a=b123&c=11&a1=&", true)] + [InlineData("?c&c&c&", "?c&c&c&", true)] + [InlineData("?a&a&a&a", "?a&a&a&a", true)] + [InlineData("?&&&&&&&", "?&&&&&&&", true)] + [InlineData("?c", "?c", true)] + [InlineData("?c=%26&", "?c=Redacted&", false)] + public async Task ValidateUrlQueryRedaction(string urlQuery, string expectedUrlQuery, bool disableQueryRedaction) + { + var exportedItems = new List(); + + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary { ["OTEL_DOTNET_EXPERIMENTAL_HTTPCLIENT_DISABLE_URL_QUERY_REDACTION"] = disableQueryRedaction.ToString() }) + .Build(); + + // Arrange + using var traceprovider = Sdk.CreateTracerProviderBuilder() + .ConfigureServices(services => services.AddSingleton(configuration)) + .AddHttpClientInstrumentation() + .AddInMemoryExporter(exportedItems) + .Build(); + + using var c = new HttpClient(); + try + { + await c.GetStringAsync($"{this.url}path{urlQuery}"); + } + catch + { + } + + Assert.Single(exportedItems); + var activity = exportedItems[0]; + + var expectedUrl = $"{this.url}path{expectedUrlQuery}"; + Assert.Equal(expectedUrl, activity.GetTagValue(SemanticConventions.AttributeUrlFull)); + } + [Theory] [InlineData(true, true)] [InlineData(true, false)] diff --git a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpWebRequestActivitySourceTests.netfx.cs b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpWebRequestActivitySourceTests.netfx.cs index 33708c34d10..4235f98e536 100644 --- a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpWebRequestActivitySourceTests.netfx.cs +++ b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpWebRequestActivitySourceTests.netfx.cs @@ -35,6 +35,8 @@ static HttpWebRequestActivitySourceTests() ValidateBaggage(httpWebRequest); } }, + + DisableUrlQueryRedaction = true, }; HttpWebRequestActivitySource.TracingOptions = options; diff --git a/test/OpenTelemetry.Tests/Internal/RedactionHelperTest.cs b/test/OpenTelemetry.Tests/Internal/RedactionHelperTest.cs new file mode 100644 index 00000000000..dda28455c72 --- /dev/null +++ b/test/OpenTelemetry.Tests/Internal/RedactionHelperTest.cs @@ -0,0 +1,32 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using OpenTelemetry.Internal; +using Xunit; + +namespace OpenTelemetry.Tests.Internal; + +public class RedactionHelperTest +{ + [Theory] + [InlineData("?a", "?a")] + [InlineData("?a=b", "?a=Redacted")] + [InlineData("?a=b&", "?a=Redacted&")] + [InlineData("?c=b&", "?c=Redacted&")] + [InlineData("?c=a", "?c=Redacted")] + [InlineData("?a=b&c", "?a=Redacted&c")] + [InlineData("?a=b&c=1&", "?a=Redacted&c=Redacted&")] + [InlineData("?a=b&c=1&a1", "?a=Redacted&c=Redacted&a1")] + [InlineData("?a=b&c=1&a1=", "?a=Redacted&c=Redacted&a1=Redacted")] + [InlineData("?a=b&c=11&a1=&", "?a=Redacted&c=Redacted&a1=Redacted&")] + [InlineData("?c&c&c&", "?c&c&c&")] + [InlineData("?a&a&a&a", "?a&a&a&a")] + [InlineData("?&&&&&&&", "?&&&&&&&")] + [InlineData("?c", "?c")] + [InlineData("?=c", "?=Redacted")] + [InlineData("?=c&=", "?=Redacted&=Redacted")] + public void QueryStringIsRedacted(string input, string expected) + { + Assert.Equal(expected, RedactionHelper.GetRedactedQueryString(input)); + } +} diff --git a/test/OpenTelemetry.Tests/OpenTelemetry.Tests.csproj b/test/OpenTelemetry.Tests/OpenTelemetry.Tests.csproj index 86fe7e36844..6d2b3619bd6 100644 --- a/test/OpenTelemetry.Tests/OpenTelemetry.Tests.csproj +++ b/test/OpenTelemetry.Tests/OpenTelemetry.Tests.csproj @@ -24,6 +24,7 @@ +