w4c-workflows-api/w4c-workflows-api.Tests/EgressGuardTests.cs
2026-09-12 01:02:46 +03:00

105 lines
3.5 KiB
C#

using w4c_workflows.Services.Security;
using Xunit;
namespace w4c_workflows.Tests;
/// <summary>
/// Guard tests: verify where DNS is consulted and how resolution results are
/// combined with the policy.
/// </summary>
public class EgressGuardTests
{
[Fact]
public async Task Literal_private_address_is_blocked_without_dns()
{
var resolver = StubHostAddressResolver.Returning(EgressTestData.PublicAddress);
var decision = await EgressTestData.Guard(resolver: resolver)
.AuthorizeAsync(new Uri("http://127.0.0.1/x"), default);
Assert.False(decision.Allowed);
Assert.Equal("blocked_address", decision.Code);
Assert.Empty(resolver.Queries);
}
[Fact]
public async Task Literal_public_address_is_allowed_without_dns()
{
var resolver = StubHostAddressResolver.Returning("10.0.0.1"); // ignored: literal wins
var decision = await EgressTestData.Guard(resolver: resolver)
.AuthorizeAsync(new Uri("http://93.184.216.34/x"), default);
Assert.True(decision.Allowed);
Assert.Empty(resolver.Queries);
}
[Fact]
public async Task Host_resolving_to_a_private_address_is_blocked()
{
var resolver = StubHostAddressResolver.Returning("10.1.2.3");
var decision = await EgressTestData.Guard(resolver: resolver)
.AuthorizeAsync(new Uri("https://internal.example.com/x"), default);
Assert.False(decision.Allowed);
Assert.Equal("blocked_address", decision.Code);
Assert.Equal(new[] { "internal.example.com" }, resolver.Queries);
}
[Fact]
public async Task Host_resolving_to_a_public_address_is_allowed()
{
var resolver = StubHostAddressResolver.Returning(EgressTestData.PublicAddress);
var decision = await EgressTestData.Guard(resolver: resolver)
.AuthorizeAsync(new Uri("https://example.com/x"), default);
Assert.True(decision.Allowed);
}
[Fact]
public async Task Mixed_public_and_private_answers_are_blocked()
{
var resolver = StubHostAddressResolver.Returning(EgressTestData.PublicAddress, "192.168.0.1");
var decision = await EgressTestData.Guard(resolver: resolver)
.AuthorizeAsync(new Uri("https://rebind.example.com/x"), default);
Assert.False(decision.Allowed);
Assert.Equal("blocked_address", decision.Code);
}
[Fact]
public async Task Resolution_failure_is_denied()
{
var decision = await EgressTestData.Guard(resolver: StubHostAddressResolver.Failing())
.AuthorizeAsync(new Uri("https://does-not-resolve.example.com/x"), default);
Assert.False(decision.Allowed);
Assert.Equal("dns_failure", decision.Code);
}
[Fact]
public async Task Blocked_host_is_rejected_before_resolution()
{
var resolver = StubHostAddressResolver.Returning(EgressTestData.PublicAddress);
var policy = EgressTestData.Policy(o => o.BlockedHosts = ["nope.example.com"]);
var decision = await EgressTestData.Guard(policy, resolver)
.AuthorizeAsync(new Uri("https://nope.example.com/x"), default);
Assert.False(decision.Allowed);
Assert.Equal("host_blocked", decision.Code);
Assert.Empty(resolver.Queries);
}
[Fact]
public async Task Max_redirects_is_exposed_from_the_policy()
{
var guard = EgressTestData.Guard(EgressTestData.Policy(o => o.MaxRedirects = 2));
Assert.Equal(2, guard.MaxRedirects);
}
}