Add trusted forwarded-header config and tests
This commit is contained in:
@@ -4,9 +4,13 @@ using System.Reflection;
|
||||
using GameList.Infrastructure;
|
||||
using GameList.Endpoints;
|
||||
using GameList.Tests.Support;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System.Text.Json;
|
||||
using System.Net.Http.Json;
|
||||
|
||||
@@ -194,6 +198,60 @@ public class HelperTests
|
||||
Assert.Equal("Unexpected server error", json.GetProperty("error").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildForwardedHeadersOptions_reads_only_valid_proxies_and_networks()
|
||||
{
|
||||
var config = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary<string, string?>
|
||||
{
|
||||
["ForwardedHeaders:KnownProxies:0"] = "203.0.113.10",
|
||||
["ForwardedHeaders:KnownProxies:1"] = "not-an-ip",
|
||||
["ForwardedHeaders:KnownNetworks:0"] = "10.20.0.0/16",
|
||||
["ForwardedHeaders:KnownNetworks:1"] = "invalid"
|
||||
}).Build();
|
||||
|
||||
var options = BuildForwardedHeadersOptionsForTest(config);
|
||||
|
||||
Assert.Single(options.KnownProxies);
|
||||
Assert.Single(options.KnownIPNetworks);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Forwarded_headers_are_ignored_for_untrusted_proxy()
|
||||
{
|
||||
var config = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary<string, string?>
|
||||
{
|
||||
["ForwardedHeaders:KnownProxies:0"] = "203.0.113.10"
|
||||
}).Build();
|
||||
var options = BuildForwardedHeadersOptionsForTest(config);
|
||||
|
||||
var builder = WebApplication.CreateBuilder();
|
||||
builder.WebHost.UseTestServer();
|
||||
|
||||
await using var app = builder.Build();
|
||||
app.Use((ctx, next) =>
|
||||
{
|
||||
ctx.Connection.RemoteIpAddress = IPAddress.Parse("198.51.100.25");
|
||||
return next();
|
||||
});
|
||||
app.UseForwardedHeaders(options);
|
||||
app.MapGet("/scheme", (HttpContext ctx) => ctx.Request.Scheme);
|
||||
|
||||
await app.StartAsync();
|
||||
try
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "/scheme");
|
||||
request.Headers.TryAddWithoutValidation("X-Forwarded-Proto", "https");
|
||||
var response = await app.GetTestClient().SendAsync(request);
|
||||
var scheme = await response.Content.ReadAsStringAsync();
|
||||
|
||||
Assert.Equal("http", scheme);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await app.StopAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private class FakeEnv : IWebHostEnvironment
|
||||
{
|
||||
public string ApplicationName { get; set; } = "";
|
||||
@@ -204,6 +262,12 @@ public class HelperTests
|
||||
public IFileProvider ContentRootFileProvider { get; set; } = null!;
|
||||
}
|
||||
|
||||
private static ForwardedHeadersOptions BuildForwardedHeadersOptionsForTest(IConfiguration config)
|
||||
{
|
||||
var method = typeof(Program).GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public).First(m => m.Name.Contains("BuildForwardedHeadersOptions"));
|
||||
return (ForwardedHeadersOptions)method.Invoke(null, [config])!;
|
||||
}
|
||||
|
||||
private sealed class RedirectFollowingHandler : HttpMessageHandler
|
||||
{
|
||||
private static readonly Uri Source = new("http://example.com/img.png");
|
||||
|
||||
Reference in New Issue
Block a user