Harden image URL validation against followed redirects

This commit is contained in:
2026-02-07 00:46:03 +01:00
parent 714914bb33
commit b86343a59d
3 changed files with 47 additions and 1 deletions

View File

@@ -113,6 +113,14 @@ public class HelperTests
Assert.False(tooLarge);
}
[Fact]
public async Task IsReachableImageAsync_rejects_followed_redirect_chain()
{
var handler = new RedirectFollowingHandler();
var reachable = await EndpointHelpers.IsReachableImageAsync("http://example.com/img.png", new StubHttpClientFactory(new StubHttpMessageHandler()), handler);
Assert.False(reachable);
}
[Fact]
public async Task IsReachableImageAsync_rejects_non_image_content()
{
@@ -195,4 +203,29 @@ public class HelperTests
public string ContentRootPath { get; set; } = "";
public IFileProvider ContentRootFileProvider { get; set; } = null!;
}
private sealed class RedirectFollowingHandler : HttpMessageHandler
{
private static readonly Uri Source = new("http://example.com/img.png");
private static readonly Uri Destination = new("http://example.com/final.png");
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request.RequestUri != Source)
{
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.NotFound) { RequestMessage = request });
}
var redirectedRequest = new HttpRequestMessage(request.Method, Destination);
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
RequestMessage = redirectedRequest,
Content = new ByteArrayContent("PNG"u8.ToArray())
};
response.Content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
response.Content.Headers.ContentLength = 3;
return Task.FromResult(response);
}
}
}