using Bunit; using Bunit.TestDoubles; using Microsoft.Extensions.DependencyInjection; using ZB.MOM.WW.Audit; using ZB.MOM.WW.Secrets.Abstractions; using ZB.MOM.WW.Secrets.Ui.Components; using ZB.MOM.WW.Secrets.Ui.Tests.Fakes; namespace ZB.MOM.WW.Secrets.Ui.Tests; public class RevealButtonTests : TestContext { private const string PlainValue = "the-plaintext-value-42"; private (RecordingSecretStore Store, CapturingAuditWriter Audit, FakeCipher Cipher) RegisterServices() { var store = new RecordingSecretStore(); var audit = new CapturingAuditWriter(); var cipher = new FakeCipher(); Services.AddSingleton(store); Services.AddSingleton(audit); Services.AddSingleton(cipher); return (store, audit, cipher); } private static StoredSecret Row(SecretName name, string plaintext) => new FakeCipher().Encrypt(name, plaintext, SecretContentType.Text) with { Revision = 1, CreatedUtc = DateTimeOffset.UnixEpoch, UpdatedUtc = DateTimeOffset.UnixEpoch, }; [Fact] public void RevealButton_ShowsValueOnClick_AndAudits() { var (store, audit, _) = RegisterServices(); var name = new SecretName("api/token"); store.Seed(Row(name, PlainValue)); var auth = this.AddTestAuthorization(); auth.SetAuthorized("carol"); auth.SetPolicies(SecretsAuthorization.RevealPolicy); var cut = RenderComponent(p => p.Add(c => c.Name, name)); // The value is NOT present before the reveal click. Assert.DoesNotContain(PlainValue, cut.Markup); Assert.Empty(audit.Events); cut.Find("button.reveal-secret").Click(); // After the click the plaintext is shown and a Success reveal audit is recorded. Assert.Contains(PlainValue, cut.Markup); Assert.Single(audit.Events); AuditEvent evt = audit.Events[0]; Assert.Equal("secret.reveal", evt.Action); Assert.Equal(AuditOutcome.Success, evt.Outcome); Assert.Equal("Secrets", evt.Category); Assert.Equal("api/token", evt.Target); Assert.Equal("carol", evt.Actor); AssertNoValueLeak(evt); } [Fact] public void RevealButton_MissingSecret_AuditsFailure() { var (_, audit, _) = RegisterServices(); var name = new SecretName("api/missing"); var auth = this.AddTestAuthorization(); auth.SetAuthorized("carol"); auth.SetPolicies(SecretsAuthorization.RevealPolicy); var cut = RenderComponent(p => p.Add(c => c.Name, name)); cut.Find("button.reveal-secret").Click(); // No value shown; a Failure reveal audit recorded. Assert.DoesNotContain(PlainValue, cut.Markup); Assert.Single(audit.Events); AuditEvent evt = audit.Events[0]; Assert.Equal("secret.reveal", evt.Action); Assert.Equal(AuditOutcome.Failure, evt.Outcome); Assert.Equal("api/missing", evt.Target); } private static void AssertNoValueLeak(AuditEvent evt) { foreach (string? field in new[] { evt.Action, evt.Actor, evt.Category, evt.Target, evt.DetailsJson, evt.ToString() }) { if (field is not null) { Assert.DoesNotContain(PlainValue, field, StringComparison.Ordinal); } } } }