/
githubmirror
/
aspnetcore
Обзор
Документация
Войти
/
githubmirror
/
aspnetcore
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Http/Routing/test/UnitTests/EndpointMiddlewareTest.cs
553 строки
19 KB
Grant Totinov
Unhandled-security-metadata guard misses AuthorizationPolicy and IAuthorizationRequirementData (#67742)
22 июл 2026, 14:25
Не верифицирован
22 июл 2026, 14:25
51a14e4
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Cors.Infrastructure; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Metadata; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using Moq; namespace Microsoft.AspNetCore.Routing; public class EndpointMiddlewareTest { private readonly IOptions<RouteOptions> RouteOptions = Options.Create(new RouteOptions()); // This test project sees internals of both Microsoft.AspNetCore.Http and Microsoft.AspNetCore.Routing, // which each embed an internal copy of the shared MiddlewareInvokedKeys, so the symbolic name is // ambiguous here. Pinning the literal values keeps the test compiling and guards the wire contract. private const string AntiforgeryInvokedKey = "__AntiforgeryMiddlewareWithEndpointInvoked"; private const string CsrfProtectionInvokedKey = "__CsrfProtectionMiddlewareWithEndpointInvoked"; [Fact] public async Task Invoke_NoFeature_NoOps() { // Arrange var httpContext = new DefaultHttpContext(); httpContext.RequestServices = new ServiceProvider(); var calledNext = false; RequestDelegate next = (c) => { calledNext = true; return Task.CompletedTask; }; var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, next, RouteOptions); // Act await middleware.Invoke(httpContext); // Assert Assert.True(calledNext); } [Fact] public async Task Invoke_NoEndpoint_NoOps() { // Arrange var httpContext = new DefaultHttpContext(); httpContext.RequestServices = new ServiceProvider(); httpContext.SetEndpoint(null); var calledNext = false; RequestDelegate next = (c) => { calledNext = true; return Task.CompletedTask; }; var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, next, RouteOptions); // Act await middleware.Invoke(httpContext); // Assert Assert.True(calledNext); } [Fact] public async Task Invoke_WithEndpoint_InvokesDelegate() { // Arrange var httpContext = new DefaultHttpContext(); httpContext.RequestServices = new ServiceProvider(); var calledEndpoint = false; RequestDelegate endpointFunc = (c) => { calledEndpoint = true; return Task.CompletedTask; }; httpContext.SetEndpoint(new Endpoint(endpointFunc, EndpointMetadataCollection.Empty, "Test")); RequestDelegate next = (c) => { throw new InvalidTimeZoneException("Should not be called"); }; var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, next, RouteOptions); // Act await middleware.Invoke(httpContext); // Assert Assert.True(calledEndpoint); } [Fact] public async Task Invoke_WithEndpoint_ThrowsIfAuthAttributesWereFound_ButAuthMiddlewareNotInvoked() { // Arrange var expected = "Endpoint Test contains authorization metadata, but a middleware was not found that supports authorization." + Environment.NewLine + "Configure your application startup by adding app.UseAuthorization() in the application startup code. " + "If there are calls to app.UseRouting() and app.UseEndpoints(...), the call to app.UseAuthorization() must go between them."; var httpContext = new DefaultHttpContext { RequestServices = new ServiceProvider() }; RequestDelegate throwIfCalled = (c) => { throw new InvalidTimeZoneException("Should not be called"); }; httpContext.SetEndpoint(new Endpoint(throwIfCalled, new EndpointMetadataCollection(Mock.Of<IAuthorizeData>()), "Test")); var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, throwIfCalled, RouteOptions); // Act & Assert var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => middleware.Invoke(httpContext)); // Assert Assert.Equal(expected, ex.Message); } [Fact] public async Task Invoke_WithEndpointWithNullRequestDelegate_ThrowsIfAuthAttributesWereFound_ButAuthMiddlewareNotInvoked() { // Arrange var expected = "Endpoint Test contains authorization metadata, but a middleware was not found that supports authorization." + Environment.NewLine + "Configure your application startup by adding app.UseAuthorization() in the application startup code. " + "If there are calls to app.UseRouting() and app.UseEndpoints(...), the call to app.UseAuthorization() must go between them."; var httpContext = new DefaultHttpContext { RequestServices = new ServiceProvider() }; RequestDelegate throwIfCalled = (c) => { throw new InvalidTimeZoneException("Should not be called"); }; httpContext.SetEndpoint(new Endpoint(requestDelegate: null, new EndpointMetadataCollection(Mock.Of<IAuthorizeData>()), "Test")); var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, throwIfCalled, RouteOptions); // Act & Assert var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => middleware.Invoke(httpContext)); // Assert Assert.Equal(expected, ex.Message); } [Fact] public async Task Invoke_WithEndpoint_ThrowsIfAuthorizationPolicyWasFound_ButAuthMiddlewareNotInvoked() { // Arrange var expected = "Endpoint Test contains authorization metadata, but a middleware was not found that supports authorization." + Environment.NewLine + "Configure your application startup by adding app.UseAuthorization() in the application startup code. " + "If there are calls to app.UseRouting() and app.UseEndpoints(...), the call to app.UseAuthorization() must go between them."; var httpContext = new DefaultHttpContext { RequestServices = new ServiceProvider() }; RequestDelegate throwIfCalled = (c) => { throw new InvalidTimeZoneException("Should not be called"); }; var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build(); httpContext.SetEndpoint(new Endpoint(throwIfCalled, new EndpointMetadataCollection(policy), "Test")); var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, throwIfCalled, RouteOptions); // Act & Assert var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => middleware.Invoke(httpContext)); // Assert Assert.Equal(expected, ex.Message); } [Fact] public async Task Invoke_WithEndpoint_ThrowsIfAuthorizationRequirementDataWasFound_ButAuthMiddlewareNotInvoked() { // Arrange var expected = "Endpoint Test contains authorization metadata, but a middleware was not found that supports authorization." + Environment.NewLine + "Configure your application startup by adding app.UseAuthorization() in the application startup code. " + "If there are calls to app.UseRouting() and app.UseEndpoints(...), the call to app.UseAuthorization() must go between them."; var httpContext = new DefaultHttpContext { RequestServices = new ServiceProvider() }; RequestDelegate throwIfCalled = (c) => { throw new InvalidTimeZoneException("Should not be called"); }; httpContext.SetEndpoint(new Endpoint(throwIfCalled, new EndpointMetadataCollection(Mock.Of<IAuthorizationRequirementData>()), "Test")); var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, throwIfCalled, RouteOptions); // Act & Assert var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => middleware.Invoke(httpContext)); // Assert Assert.Equal(expected, ex.Message); } [Fact] public async Task Invoke_WithEndpoint_WorksIfAuthAttributesWereFound_AndAuthMiddlewareInvoked() { // Arrange var httpContext = new DefaultHttpContext { RequestServices = new ServiceProvider() }; var calledEndpoint = false; RequestDelegate endpointFunc = (c) => { calledEndpoint = true; return Task.CompletedTask; }; httpContext.SetEndpoint(new Endpoint(endpointFunc, new EndpointMetadataCollection(Mock.Of<IAuthorizeData>()), "Test")); httpContext.Items[EndpointMiddleware.AuthorizationMiddlewareInvokedKey] = true; RequestDelegate next = (c) => { throw new InvalidTimeZoneException("Should not be called"); }; var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, next, RouteOptions); // Act await middleware.Invoke(httpContext); // Assert Assert.True(calledEndpoint); } [Fact] public async Task Invoke_WithEndpoint_DoesNotThrowIfUnhandledAuthAttributesWereFound_ButSuppressedViaOptions() { // Arrange var httpContext = new DefaultHttpContext { RequestServices = new ServiceProvider() }; var calledEndpoint = false; RequestDelegate endpointFunc = (c) => { calledEndpoint = true; return Task.CompletedTask; }; httpContext.SetEndpoint(new Endpoint(endpointFunc, new EndpointMetadataCollection(Mock.Of<IAuthorizeData>()), "Test")); var routeOptions = Options.Create(new RouteOptions { SuppressCheckForUnhandledSecurityMetadata = true }); RequestDelegate next = (c) => { throw new InvalidTimeZoneException("Should not be called"); }; var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, next, routeOptions); // Act await middleware.Invoke(httpContext); // Assert Assert.True(calledEndpoint); } [Fact] public async Task Invoke_WithEndpoint_ThrowsIfCorsMetadataWasFound_ButCorsMiddlewareNotInvoked() { // Arrange var expected = "Endpoint Test contains CORS metadata, but a middleware was not found that supports CORS." + Environment.NewLine + "Configure your application startup by adding app.UseCors() in the application startup code. " + "If there are calls to app.UseRouting() and app.UseEndpoints(...), the call to app.UseCors() must go between them."; var httpContext = new DefaultHttpContext { RequestServices = new ServiceProvider() }; RequestDelegate throwIfCalled = (c) => { throw new InvalidTimeZoneException("Should not be called"); }; httpContext.SetEndpoint(new Endpoint(throwIfCalled, new EndpointMetadataCollection(Mock.Of<ICorsMetadata>()), "Test")); var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, throwIfCalled, RouteOptions); // Act & Assert var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => middleware.Invoke(httpContext)); // Assert Assert.Equal(expected, ex.Message); } [Fact] public async Task Invoke_WithEndpoint_WorksIfCorsMetadataWasFound_AndCorsMiddlewareInvoked() { // Arrange var httpContext = new DefaultHttpContext { RequestServices = new ServiceProvider() }; var calledEndpoint = false; RequestDelegate endpointFunc = (c) => { calledEndpoint = true; return Task.CompletedTask; }; httpContext.SetEndpoint(new Endpoint(endpointFunc, new EndpointMetadataCollection(Mock.Of<ICorsMetadata>()), "Test")); httpContext.Items[EndpointMiddleware.CorsMiddlewareInvokedKey] = true; RequestDelegate next = (c) => { throw new InvalidTimeZoneException("Should not be called"); }; var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, next, RouteOptions); // Act await middleware.Invoke(httpContext); // Assert Assert.True(calledEndpoint); } [Fact] public async Task Invoke_WithEndpoint_DoesNotThrowIfUnhandledCorsAttributesWereFound_ButSuppressedViaOptions() { // Arrange var httpContext = new DefaultHttpContext { RequestServices = new ServiceProvider() }; var calledEndpoint = false; RequestDelegate endpointFunc = (c) => { calledEndpoint = true; return Task.CompletedTask; }; httpContext.SetEndpoint(new Endpoint(endpointFunc, new EndpointMetadataCollection(Mock.Of<IAuthorizeData>()), "Test")); var routeOptions = Options.Create(new RouteOptions { SuppressCheckForUnhandledSecurityMetadata = true }); RequestDelegate next = (c) => { throw new InvalidTimeZoneException("Should not be called"); }; var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, next, routeOptions); // Act await middleware.Invoke(httpContext); // Assert Assert.True(calledEndpoint); } [Fact] public async Task Invoke_WithEndpoint_ThrowsIfAntiforgeryMetadataWasFound_ButAntiforgeryMiddlewareNotInvoked() { // Arrange var expected = "Endpoint Test contains anti-forgery metadata, but a middleware was not found that supports anti-forgery." + Environment.NewLine + "Configure your application startup by adding app.UseAntiforgery() in the application startup code. " + "If there are calls to app.UseRouting() and app.UseEndpoints(...), the call to app.UseAntiforgery() must go between them. " + "Calls to app.UseAntiforgery() must be placed after calls to app.UseAuthentication() and app.UseAuthorization()."; var httpContext = new DefaultHttpContext { RequestServices = new ServiceProvider() }; RequestDelegate throwIfCalled = (c) => { throw new InvalidTimeZoneException("Should not be called"); }; httpContext.SetEndpoint(new Endpoint(throwIfCalled, new EndpointMetadataCollection(AntiforgeryMetadata.ValidationRequired), "Test")); var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, throwIfCalled, RouteOptions); // Act & Assert var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => middleware.Invoke(httpContext)); // Assert Assert.Equal(expected, ex.Message); } [Fact] public async Task Invoke_WithEndpoint_WorksIfAntiforgeryMetadataWasFound_AndCsrfProtectionMiddlewareInvoked() { var httpContext = new DefaultHttpContext { RequestServices = new ServiceProvider() }; var calledEndpoint = false; RequestDelegate endpointFunc = (c) => { calledEndpoint = true; return Task.CompletedTask; }; httpContext.SetEndpoint(new Endpoint(endpointFunc, new EndpointMetadataCollection(AntiforgeryMetadata.ValidationRequired), "Test")); httpContext.Items[CsrfProtectionInvokedKey] = true; RequestDelegate next = (c) => { throw new InvalidTimeZoneException("Should not be called"); }; var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, next, RouteOptions); await middleware.Invoke(httpContext); Assert.True(calledEndpoint); } [Fact] public async Task Invoke_WithEndpoint_WorksIfAntiforgeryMetadataWasFound_AndBothMiddlewaresInvoked() { var httpContext = new DefaultHttpContext { RequestServices = new ServiceProvider() }; var calledEndpoint = false; RequestDelegate endpointFunc = (c) => { calledEndpoint = true; return Task.CompletedTask; }; httpContext.SetEndpoint(new Endpoint(endpointFunc, new EndpointMetadataCollection(AntiforgeryMetadata.ValidationRequired), "Test")); httpContext.Items[AntiforgeryInvokedKey] = true; httpContext.Items[CsrfProtectionInvokedKey] = true; RequestDelegate next = (c) => { throw new InvalidTimeZoneException("Should not be called"); }; var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, next, RouteOptions); await middleware.Invoke(httpContext); Assert.True(calledEndpoint); } [Fact] public async Task Invoke_WithEndpoint_WorksIfAntiforgeryMetadataWasFound_AndAntiforgeryMiddlewareInvoked() { // Arrange var httpContext = new DefaultHttpContext { RequestServices = new ServiceProvider() }; var calledEndpoint = false; RequestDelegate endpointFunc = (c) => { calledEndpoint = true; return Task.CompletedTask; }; httpContext.SetEndpoint(new Endpoint(endpointFunc, new EndpointMetadataCollection(AntiforgeryMetadata.ValidationRequired), "Test")); httpContext.Items[AntiforgeryInvokedKey] = true; RequestDelegate next = (c) => { throw new InvalidTimeZoneException("Should not be called"); }; var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, next, RouteOptions); // Act await middleware.Invoke(httpContext); // Assert Assert.True(calledEndpoint); } [Fact] public async Task Invoke_WithEndpoint_DoesNotThrowIfUnhandledAntiforgeryMetadataWereFound_ButSuppressedViaOptions() { // Arrange var httpContext = new DefaultHttpContext { RequestServices = new ServiceProvider() }; var calledEndpoint = false; RequestDelegate endpointFunc = (c) => { calledEndpoint = true; return Task.CompletedTask; }; httpContext.SetEndpoint(new Endpoint(endpointFunc, new EndpointMetadataCollection(AntiforgeryMetadata.ValidationRequired), "Test")); var routeOptions = Options.Create(new RouteOptions { SuppressCheckForUnhandledSecurityMetadata = true }); RequestDelegate next = (c) => { throw new InvalidTimeZoneException("Should not be called"); }; var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, next, routeOptions); // Act await middleware.Invoke(httpContext); // Assert Assert.True(calledEndpoint); } private class ServiceProvider : IServiceProvider { public object GetService(Type serviceType) { throw new NotImplementedException(); } } }