Skip to content

HttpRouteFormatter - Log redaction leaks replayable bearer token #7826

Closed

Description

@evgenyfedorov2

Description

When one path segment of a route has a parameter followed by fixed text, such as . in /a/{token}.{format}, route parameter redaction splits the path in the wrong place. Part or all of a sensitive parameter then appears unredacted in another parameter that isn't redacted. In Formatted mode a parameter ends at the first occurrence of the first character of the fixed text after it (src/Libraries/Microsoft.Extensions.Telemetry/Http/HttpRouteFormatter.cs#L83), while in Structured mode every parameter ends at the next / (src/Libraries/Microsoft.Extensions.Telemetry/Http/HttpRouteParser.cs#L72). ASP.NET Core instead matches the whole fixed text from right to left, and both Microsoft.Extensions.Http.Diagnostics and Microsoft.AspNetCore.Diagnostics.Middleware use this shared code

Reproduction Steps

  1.  dotnet new console 
  2.  dotnet add package Microsoft.Extensions.Http.Diagnostics , then do the same for  Microsoft.Extensions.Compliance.Testing  and  Microsoft.Extensions.Logging.Console .
  3. Replace Program.cs with the code below and run  dotnet run .
using Microsoft.Extensions.Compliance.Classification;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Http.Diagnostics;
using Microsoft.Extensions.Http.Logging;
using Microsoft.Extensions.Logging;

var services = new ServiceCollection()
    .AddLogging(b => b.AddJsonConsole())
    .AddFakeRedaction(o => o.RedactionFormat = "[REDACTED]");

foreach (var mode in new[] { OutgoingPathLoggingMode.Formatted, OutgoingPathLoggingMode.Structured })
{
    services.AddHttpClient(mode.ToString())
        .ConfigurePrimaryHttpMessageHandler(() => new OkHandler())
        .AddExtendedHttpClientLogging(o =>
        {
            o.RequestPathLoggingMode = mode;
            o.RouteParameterDataClasses["token"] = new DataClassification("Demo", "Secret");
            o.RouteParameterDataClasses["format"] = DataClassification.None;
        });
}

using (var provider = services.BuildServiceProvider())
{
    var factory = provider.GetRequiredService<IHttpClientFactory>();
    await SendAsync(factory.CreateClient("Formatted"), "/a/{token}.{format}", "/a/part1.part2.part3.json");
    await SendAsync(factory.CreateClient("Structured"), "/b/{format}.{token}", "/b/json.secret");
}

static async Task SendAsync(HttpClient client, string route, string path)
{
    using var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost" + path);
    request.SetRequestMetadata(new RequestMetadata { RequestRoute = route });
    using var response = await client.SendAsync(request);
}

sealed class OkHandler : HttpMessageHandler
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        => Task.FromResult(new HttpResponseMessage(System.Net.HttpStatusCode.OK));
}

Expected behavior

For these routes, ASP.NET Core binds token=part1.part2.part3, format=json for the first request and  format=json, token=secret for the second. The whole token value should be redacted:

  • Formatted: url.path  =  a/[REDACTED].json 
  • Structured: format = json, token = [REDACTED]

Actual behavior

Formatted: "url.path":"a/[REDACTED].part2.part3.json"
Structured: "format":"json.secret","token":""

Regression?

No response

Known Workarounds

No response

Configuration

No response

Other information

No response

Activity

  1. added
    bugThis issue describes a behavior which is not expected - a bug.
    on Oct 12, 2026
  2. added a commit that references this issue on Oct 12, 2026
    77a8c1c
  3. added a commit that references this issue on Oct 13, 2026
    e234d0d
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

area-telemetrybugThis issue describes a behavior which is not expected - a bug.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions