< Summary

Information
Class: ProjectTemplate.Web.Middleware.SecurityHeadersMiddleware
Assembly: ProjectTemplate.Web
File(s): /home/runner/work/NetCoreApplicationTemplate/NetCoreApplicationTemplate/src/ProjectTemplate.Web/Middleware/SecurityHeadersMiddleware.cs
Line coverage
100%
Covered lines: 53
Uncovered lines: 0
Coverable lines: 53
Total lines: 99
Line coverage: 100%
Branch coverage
75%
Covered branches: 9
Total branches: 12
Branch coverage: 75%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)50%66100%
InvokeAsync()100%44100%
IsExcludedPath(...)100%11100%
AddHeaderIfMissing(...)100%22100%

File(s)

/home/runner/work/NetCoreApplicationTemplate/NetCoreApplicationTemplate/src/ProjectTemplate.Web/Middleware/SecurityHeadersMiddleware.cs

#LineLine coverage
 1using Microsoft.Extensions.Options;
 2using ProjectTemplate.Web.Options;
 3
 4namespace ProjectTemplate.Web.Middleware;
 5
 6/// <summary>
 7/// Middleware that applies a set of security-related HTTP headers to responses.
 8/// </summary>
 9/// <remarks>
 10/// The middleware can be configured via <see cref="ApplicationSecurityHeadersOptions"/> to enable/disable
 11/// individual headers and to exclude certain request path prefixes.
 12/// </remarks>
 9813public sealed class SecurityHeadersMiddleware(RequestDelegate next, IOptions<ApplicationSecurityHeadersOptions> options)
 14{
 9815    private readonly RequestDelegate _next = next ?? throw new ArgumentNullException(nameof(next));
 9816    private readonly ApplicationSecurityHeadersOptions _options = options?.Value ?? throw new ArgumentNullException(name
 17
 18    /// <summary>
 19    /// Invokes the middleware for the given <paramref name="context"/>, applying configured
 20    /// security headers to the response when appropriate.
 21    /// </summary>
 22    /// <param name="context">The current HTTP context.</param>
 23    /// <returns>A <see cref="Task"/> that completes when the middleware and the next delegate finish processing.</retur
 24    public async Task InvokeAsync(HttpContext context)
 25    {
 11526        if (!_options.Enabled)
 27        {
 128            await _next(context);
 129            return;
 30        }
 31
 11432        if (IsExcludedPath(context.Request.Path))
 33        {
 34            // Excluded endpoints (health, metrics) skip the document-oriented headers, but MIME sniffing
 35            // protection costs nothing and applies to every response.
 1636            context.Response.OnStarting(() =>
 1637            {
 1638                AddHeaderIfMissing(context.Response.Headers, "X-Content-Type-Options", "nosniff");
 1639                return Task.CompletedTask;
 1640            });
 41
 1642            await _next(context);
 1643            return;
 44        }
 45
 9846        context.Response.OnStarting(() =>
 9847        {
 9848            IHeaderDictionary headers = context.Response.Headers;
 9849
 9850            AddHeaderIfMissing(headers, "X-Content-Type-Options", "nosniff");
 9851            AddHeaderIfMissing(headers, "X-Frame-Options", "DENY");
 9852            AddHeaderIfMissing(headers, "Referrer-Policy", "strict-origin-when-cross-origin");
 9853            AddHeaderIfMissing(headers, "X-Permitted-Cross-Domain-Policies", "none");
 9854
 9855            // Modern browser isolation / cross-origin protections.
 9856            if (_options.EnableCrossOriginHeaders)
 9857            {
 9858                AddHeaderIfMissing(headers, "Cross-Origin-Opener-Policy", "same-origin");
 9859                AddHeaderIfMissing(headers, "Cross-Origin-Resource-Policy", "same-origin");
 9860            }
 9861
 9862            if (_options.EnablePermissionsPolicy &&
 9863                !string.IsNullOrWhiteSpace(_options.PermissionsPolicy))
 9864            {
 9865                AddHeaderIfMissing(headers, "Permissions-Policy", _options.PermissionsPolicy);
 9866            }
 9867
 9868            if (_options.EnableContentSecurityPolicy &&
 9869                !string.IsNullOrWhiteSpace(_options.ContentSecurityPolicy))
 9870            {
 9871                AddHeaderIfMissing(headers, "Content-Security-Policy", _options.ContentSecurityPolicy);
 9872            }
 9873
 9874            // Do not add X-XSS-Protection. It is obsolete and can cause problems in some browsers.
 9875
 9876            return Task.CompletedTask;
 9877        });
 78
 9879        await _next(context);
 11280    }
 81
 82    private bool IsExcludedPath(PathString path)
 83    {
 11484        return _options.ExcludedPathPrefixes.Any(prefix =>
 11485            path.StartsWithSegments(prefix, StringComparison.OrdinalIgnoreCase));
 86    }
 87
 88    private static void AddHeaderIfMissing(
 89        IHeaderDictionary headers,
 90        string name,
 91        string value)
 92    {
 79693        if (!headers.ContainsKey(name))
 94        {
 61095            headers[name] = value;
 96        }
 79697    }
 98}
 99