< 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: 45
Uncovered lines: 0
Coverable lines: 45
Total lines: 85
Line coverage: 100%
Branch coverage
77%
Covered branches: 17
Total branches: 22
Branch coverage: 77.2%
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>
 12613public sealed class SecurityHeadersMiddleware(RequestDelegate next, IOptions<ApplicationSecurityHeadersOptions> options)
 14{
 12615    private readonly RequestDelegate _next = next ?? throw new ArgumentNullException(nameof(next));
 12616    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    {
 12026        if (!_options.Enabled || IsExcludedPath(context.Request.Path))
 27        {
 2228            await _next(context);
 2229            return;
 30        }
 31
 9832        context.Response.OnStarting(() =>
 9833        {
 9834            IHeaderDictionary headers = context.Response.Headers;
 9835
 9836            AddHeaderIfMissing(headers, "X-Content-Type-Options", "nosniff");
 9837            AddHeaderIfMissing(headers, "X-Frame-Options", "DENY");
 9838            AddHeaderIfMissing(headers, "Referrer-Policy", "strict-origin-when-cross-origin");
 9839            AddHeaderIfMissing(headers, "X-Permitted-Cross-Domain-Policies", "none");
 9840
 9841            // Modern browser isolation / cross-origin protections.
 9842            if (_options.EnableCrossOriginHeaders)
 9843            {
 9644                AddHeaderIfMissing(headers, "Cross-Origin-Opener-Policy", "same-origin");
 9645                AddHeaderIfMissing(headers, "Cross-Origin-Resource-Policy", "same-origin");
 9846            }
 9847
 9848            if (_options.EnablePermissionsPolicy &&
 9849                !string.IsNullOrWhiteSpace(_options.PermissionsPolicy))
 9850            {
 9651                AddHeaderIfMissing(headers, "Permissions-Policy", _options.PermissionsPolicy);
 9852            }
 9853
 9854            if (_options.EnableContentSecurityPolicy &&
 9855                !string.IsNullOrWhiteSpace(_options.ContentSecurityPolicy))
 9856            {
 9657                AddHeaderIfMissing(headers, "Content-Security-Policy", _options.ContentSecurityPolicy);
 9858            }
 9859
 9860            // Do not add X-XSS-Protection. It is obsolete and can cause problems in some browsers.
 9861
 9862            return Task.CompletedTask;
 9863        });
 64
 9865        await _next(context);
 12066    }
 67
 68    private bool IsExcludedPath(PathString path)
 69    {
 11870        return _options.ExcludedPathPrefixes.Any(prefix =>
 53271            path.StartsWithSegments(prefix, StringComparison.OrdinalIgnoreCase));
 72    }
 73
 74    private static void AddHeaderIfMissing(
 75        IHeaderDictionary headers,
 76        string name,
 77        string value)
 78    {
 77679        if (!headers.ContainsKey(name))
 80        {
 63281            headers[name] = value;
 82        }
 77683    }
 84}
 85