From bdff1a06a1f7373e9468a8300a9873576a41586d Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sat, 10 Oct 2026 05:58:34 +0200 Subject: [PATCH] HostTrustTests: key ring comes only from KeysPath, canary can fail, Dockerfile switch guarded Co-Authored-By: Claude Opus 5.5 --- tests/Bifrost.Web.Tests/HostTrustTests.cs | 80 ++++++++++++++++------- 1 file changed, 58 insertions(+), 22 deletions(-) diff --git a/tests/Bifrost.Web.Tests/HostTrustTests.cs b/tests/Bifrost.Web.Tests/HostTrustTests.cs index 0c4e201..78f9057 100644 --- a/tests/Bifrost.Web.Tests/HostTrustTests.cs +++ b/tests/Bifrost.Web.Tests/HostTrustTests.cs @@ -72,25 +72,17 @@ public sealed class HostTrustTests : IClassFixture [Fact] public void Payload_protected_under_one_content_root_unprotects_under_another() { - var root = Path.Combine(Path.GetTempPath(), "bifrost-dp-" + Guid.NewGuid().ToString("N")); + var root = NewRoot(); var keys = Path.Combine(root, "keys"); - var releaseA = Directory.CreateDirectory(Path.Combine(root, "release-a")).FullName; - var releaseB = Directory.CreateDirectory(Path.Combine(root, "release-b")).FullName; try { - using var hostA = WithContentRoot(releaseA, keys); - using var hostB = WithContentRoot(releaseB, keys); + using var hostA = WithContentRoot(MakeRelease(root, "release-a"), keys); + using var hostB = WithContentRoot(MakeRelease(root, "release-b"), keys); - var protectedPayload = hostA.Services - .GetRequiredService() - .CreateProtector("P") - .Protect("payload"); - var opened = hostB.Services - .GetRequiredService() - .CreateProtector("P") - .Unprotect(protectedPayload); + var sealedPayload = Protect(hostA, "payload"); - Assert.Equal("payload", opened); + Assert.Equal("payload", Unprotect(hostB, sealedPayload)); + Assert.NotEmpty(Directory.GetFiles(keys, "key-*.xml")); } finally { @@ -98,6 +90,41 @@ public sealed class HostTrustTests : IClassFixture } } + [Fact] + public void A_different_keys_path_does_not_unprotect_the_payload() + { + var root = NewRoot(); + try + { + using var hostA = WithContentRoot(MakeRelease(root, "release-a"), Path.Combine(root, "keys-a")); + using var hostB = WithContentRoot(MakeRelease(root, "release-b"), Path.Combine(root, "keys-b")); + + var sealedPayload = Protect(hostA, "payload"); + + Assert.ThrowsAny( + () => Unprotect(hostB, sealedPayload)); + } + finally + { + Directory.Delete(root, recursive: true); + } + } + + [Fact] + public void The_container_image_does_not_set_the_forwarded_headers_switch() + { + var directory = new DirectoryInfo(AppContext.BaseDirectory); + while (directory is not null && !File.Exists(Path.Combine(directory.FullName, "Dockerfile"))) + { + directory = directory.Parent; + } + + Assert.NotNull(directory); + Assert.DoesNotContain( + "ASPNETCORE_FORWARDEDHEADERS_ENABLED", + File.ReadAllText(Path.Combine(directory!.FullName, "Dockerfile"))); + } + [Fact] public void Production_without_keys_path_fails_startup_validation() { @@ -108,12 +135,13 @@ public sealed class HostTrustTests : IClassFixture } [Fact] - public void Startup_refusal_names_the_key_and_never_echoes_its_value() + public void Startup_refusal_names_the_key_and_never_echoes_configured_values() { - var canary = "canary-secret-path-4f1e"; + var canary = "canary-secret-4f1e"; var refusal = Assert.Throws( - () => Validate(keysPath: canary, connectionString: null)); + () => Validate(keysPath: null, connectionString: canary)); + Assert.Contains("DataProtection:KeysPath", refusal.Message); Assert.DoesNotContain(canary, refusal.Message); } @@ -143,6 +171,18 @@ public sealed class HostTrustTests : IClassFixture return context; } + private static string NewRoot() => + Path.Combine(Path.GetTempPath(), "bifrost-dp-" + Guid.NewGuid().ToString("N")); + + private static string MakeRelease(string root, string name) => + Directory.CreateDirectory(Path.Combine(root, name)).FullName; + + private static string Protect(TestWebApplicationFactory host, string value) => + host.Services.GetRequiredService().CreateProtector("P").Protect(value); + + private static string Unprotect(TestWebApplicationFactory host, string value) => + host.Services.GetRequiredService().CreateProtector("P").Unprotect(value); + private TestWebApplicationFactory WithContentRoot(string contentRoot, string keysPath) => new KeyRingFactory(contentRoot, keysPath); @@ -152,11 +192,7 @@ public sealed class HostTrustTests : IClassFixture { base.ConfigureWebHost(builder); builder.UseContentRoot(contentRoot); - builder.ConfigureAppConfiguration((_, configuration) => - configuration.AddInMemoryCollection(new Dictionary - { - ["DataProtection:KeysPath"] = keysPath - })); + builder.UseSetting("DataProtection:KeysPath", keysPath); } }