using w4c_workflows.Services.Security; using Xunit; namespace w4c_workflows.Tests; /// /// Guard tests: verify where DNS is consulted and how resolution results are /// combined with the policy. /// 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); } }