< Summary

Information
Class: ProjectTemplate.Web.Diagnostics.ForwardedHeadersTrustDiagnosticsHostedService
Assembly: ProjectTemplate.Web
File(s): /home/runner/work/NetCoreApplicationTemplate/NetCoreApplicationTemplate/src/ProjectTemplate.Web/Diagnostics/ForwardedHeadersTrustDiagnosticsHostedService.cs
Line coverage
100%
Covered lines: 38
Uncovered lines: 0
Coverable lines: 38
Total lines: 96
Line coverage: 100%
Branch coverage
88%
Covered branches: 16
Total branches: 18
Branch coverage: 88.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
StartAsync(...)100%44100%
StopAsync(...)100%11100%
RequiresExplicitProxyTrust(...)100%88100%
ProcessesForwardedFor(...)100%11100%
HasConfiguredTrustBoundary(...)100%22100%
NormalizeHeaderName(...)50%44100%

File(s)

/home/runner/work/NetCoreApplicationTemplate/NetCoreApplicationTemplate/src/ProjectTemplate.Web/Diagnostics/ForwardedHeadersTrustDiagnosticsHostedService.cs

#LineLine coverage
 1using Microsoft.Extensions.Options;
 2using ProjectTemplate.Web.Options;
 3
 4namespace ProjectTemplate.Web.Diagnostics;
 5
 6/// <summary>
 7/// Reports likely forwarded-header trust misconfiguration when client-IP rate limiting is enabled.
 8/// </summary>
 1449internal sealed partial class ForwardedHeadersTrustDiagnosticsHostedService(
 14410    IOptions<ApplicationForwardedHeadersOptions> forwardedHeadersOptions,
 14411    IOptions<ApplicationRateLimitingOptions> rateLimitingOptions,
 14412    IHostEnvironment environment,
 14413    ILogger<ForwardedHeadersTrustDiagnosticsHostedService> logger) : IHostedService
 14{
 15    private const string _strictValidationFailureMessage =
 16        "Forwarded headers and client-IP rate limiting are enabled, but no trusted proxy or network is configured. " +
 17        "Configure ProjectTemplate:ForwardedHeaders:KnownProxies or KnownNetworks before enabling strict proxy trust val
 18
 14419    private readonly ApplicationForwardedHeadersOptions _forwardedHeadersOptions = forwardedHeadersOptions.Value;
 14420    private readonly ApplicationRateLimitingOptions _rateLimitingOptions = rateLimitingOptions.Value;
 14421    private readonly IHostEnvironment _environment = environment;
 22
 23    /// <inheritdoc />
 24    public Task StartAsync(CancellationToken cancellationToken)
 25    {
 14426        if (!RequiresExplicitProxyTrust(
 14427                _forwardedHeadersOptions,
 14428                _rateLimitingOptions,
 14429                _environment))
 30        {
 1631            return Task.CompletedTask;
 32        }
 33
 12834        if (_forwardedHeadersOptions.RequireExplicitProxyTrust)
 35        {
 236            throw new InvalidOperationException(_strictValidationFailureMessage);
 37        }
 38
 12639        LogMissingExplicitProxyTrust(logger, _environment.EnvironmentName);
 12640        return Task.CompletedTask;
 41    }
 42
 43    /// <inheritdoc />
 44    public Task StopAsync(CancellationToken cancellationToken)
 45    {
 27246        return Task.CompletedTask;
 47    }
 48
 49    internal static bool RequiresExplicitProxyTrust(
 50        ApplicationForwardedHeadersOptions forwardedHeadersOptions,
 51        ApplicationRateLimitingOptions rateLimitingOptions,
 52        IHostEnvironment environment)
 53    {
 14454        ArgumentNullException.ThrowIfNull(forwardedHeadersOptions);
 14455        ArgumentNullException.ThrowIfNull(rateLimitingOptions);
 14456        ArgumentNullException.ThrowIfNull(environment);
 57
 14458        return !environment.IsDevelopment() &&
 14459               forwardedHeadersOptions.Enabled &&
 14460               ProcessesForwardedFor(forwardedHeadersOptions.Headers) &&
 14461               rateLimitingOptions.Enabled &&
 14462               !HasConfiguredTrustBoundary(forwardedHeadersOptions);
 63    }
 64
 65    private static bool ProcessesForwardedFor(IEnumerable<string> headers)
 66    {
 14067        return headers.Any(header =>
 28068            string.Equals(
 28069                NormalizeHeaderName(header),
 28070                "XForwardedFor",
 28071                StringComparison.OrdinalIgnoreCase));
 72    }
 73
 74    private static bool HasConfiguredTrustBoundary(ApplicationForwardedHeadersOptions options)
 75    {
 14676        return options.KnownProxies.Any(value => !string.IsNullOrWhiteSpace(value)) ||
 14077               options.KnownNetworks.Any(value => !string.IsNullOrWhiteSpace(value));
 78    }
 79
 80    private static string NormalizeHeaderName(string? value)
 81    {
 14082        return value?
 14083            .Trim()
 14084            .Replace("-", string.Empty, StringComparison.Ordinal)
 14085            .Replace("_", string.Empty, StringComparison.Ordinal)
 14086            ?? string.Empty;
 87    }
 88
 89    [LoggerMessage(
 90        EventId = 5101,
 91        Level = LogLevel.Warning,
 92        Message = "Forwarded headers and client-IP rate limiting are enabled in environment {EnvironmentName}, but no de
 93    private static partial void LogMissingExplicitProxyTrust(
 94        ILogger logger,
 95        string environmentName);
 96}