diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Audit/CertAuditEvents.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Audit/CertAuditEvents.cs
index 910bfb2b..3244a9b5 100644
--- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Audit/CertAuditEvents.cs
+++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Audit/CertAuditEvents.cs
@@ -28,18 +28,17 @@ public static class CertAuditEvents
/// ); otherwise (Outcome
/// ).
/// On failure, the error text carried in the details payload; ignored on
- /// success (the details payload serializes for the error field).
+ /// success (the error field is omitted from the details payload entirely).
/// A fully populated with a fresh
/// and set to now (UTC).
public static AuditEvent Build(
string action, string store, string thumbprint, string actor, bool success, string? error)
{
- var detailsJson = JsonSerializer.Serialize(new
- {
- store,
- thumbprint,
- error = success ? null : error,
- });
+ // On success the error field is omitted entirely (not serialized as null) so the common
+ // success-path audit row carries no dead "error" key.
+ var detailsJson = JsonSerializer.Serialize(success
+ ? (object)new { store, thumbprint }
+ : new { store, thumbprint, error });
return new AuditEvent
{
diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Audit/CertAuditEventsTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Audit/CertAuditEventsTests.cs
index 68cf6f7b..ee10c8f3 100644
--- a/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Audit/CertAuditEventsTests.cs
+++ b/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Audit/CertAuditEventsTests.cs
@@ -26,6 +26,7 @@ public sealed class CertAuditEventsTests
evt.Actor.ShouldBe("alice");
evt.Outcome.ShouldBe(AuditOutcome.Success);
evt.EventId.ShouldNotBe(Guid.Empty);
+ evt.OccurredAtUtc.ShouldBeGreaterThan(DateTimeOffset.UtcNow.AddSeconds(-5));
evt.DetailsJson.ShouldNotBeNull();
evt.DetailsJson!.ShouldContain(Thumb);
}
@@ -88,13 +89,14 @@ public sealed class CertAuditEventsTests
evt.DetailsJson!.ShouldContain("store write failed");
}
- /// On success the error text is omitted from the details payload (it serializes null).
+ /// On success the error text AND the error key are omitted from the details payload.
[Fact]
public void Build_success_omits_error_text()
{
var evt = CertAuditEvents.Build("Trust", "rejected", Thumb, "alice", success: true, error: "ignored");
evt.DetailsJson!.ShouldNotContain("ignored");
+ evt.DetailsJson!.ShouldNotContain("error");
}
/// The public Category constant matches the value stamped onto built events.