feat(audit): M5.3 response-capture increments — request headers, ceiling-hits counter, per-method body opt-out (T7)
1. Request headers in Extra JSON (AuditWriteMiddleware): adds a `requestHeaders` object to the existing Extra JSON alongside remoteIp/userAgent; headers whose names appear in AuditLogOptions.HeaderRedactList (Authorization, X-Api-Key, Cookie, Set-Cookie by default) are replaced with "<redacted>" using OrdinalIgnoreCase matching — same policy as ScadaBridgeAuditRedactor. 2. AuditInboundCeilingHits counter: new IAuditInboundCeilingHitsCounter interface + NoOpAuditInboundCeilingHitsCounter default; AuditCentralHealthSnapshot implements the interface (Interlocked field, thread-safe) and exposes AuditInboundCeilingHits on IAuditCentralHealthSnapshot; AddAuditLog registers the NoOp default, AddAuditLogCentralMaintenance forwards to the snapshot; AuditWriteMiddleware accepts the counter as an optional ctor arg and increments it once per request where either the request or response body hit the cap. 3. Per-method SkipBodyCapture opt-out: adds SkipBodyCapture bool to PerTargetRedactionOverride; AuditWriteMiddleware consults the per-target override map at the start of InvokeAsync (before EnableBuffering) and, when set, skips body read + capture entirely — the audit row still emits with headers/metadata but null RequestSummary/ResponseSummary; truncation flags are also cleared so the ceiling-hits counter is not bumped for opted-out methods.
This commit is contained in:
@@ -8,6 +8,7 @@ using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Audit;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Types.Audit;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
|
||||
using IAuditInboundCeilingHitsCounter = ZB.MOM.WW.ScadaBridge.AuditLog.Central.IAuditInboundCeilingHitsCounter;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.AuditLog.Tests.Central;
|
||||
|
||||
@@ -163,6 +164,69 @@ public class CentralAuditWriteFailuresTests : TestKit
|
||||
var snapshot = new AuditCentralHealthSnapshot();
|
||||
Assert.Equal(0, snapshot.CentralAuditWriteFailures);
|
||||
Assert.Equal(0, snapshot.AuditRedactionFailure);
|
||||
Assert.Equal(0, snapshot.AuditInboundCeilingHits);
|
||||
Assert.Empty(snapshot.SiteAuditTelemetryStalled);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// M5.3 (T7) AuditInboundCeilingHits counter
|
||||
// AuditCentralHealthSnapshot implements IAuditInboundCeilingHitsCounter.
|
||||
// Incrementing through the interface surface is reflected on the snapshot.
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
[Fact]
|
||||
public void AuditInboundCeilingHits_StartsAtZero()
|
||||
{
|
||||
var snapshot = new AuditCentralHealthSnapshot();
|
||||
Assert.Equal(0, snapshot.AuditInboundCeilingHits);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AuditInboundCeilingHits_IncrementedThroughInterface_ReflectedOnSnapshot()
|
||||
{
|
||||
var snapshot = new AuditCentralHealthSnapshot();
|
||||
var counter = (IAuditInboundCeilingHitsCounter)snapshot;
|
||||
|
||||
counter.Increment();
|
||||
counter.Increment();
|
||||
counter.Increment();
|
||||
|
||||
Assert.Equal(3, snapshot.AuditInboundCeilingHits);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AuditInboundCeilingHits_IsThreadSafe()
|
||||
{
|
||||
// Interlocked increment must produce the correct count under concurrent
|
||||
// increments — same shape as the existing counter tests.
|
||||
var snapshot = new AuditCentralHealthSnapshot();
|
||||
var counter = (IAuditInboundCeilingHitsCounter)snapshot;
|
||||
const int incrementCount = 1000;
|
||||
|
||||
Parallel.For(0, incrementCount, _ => counter.Increment());
|
||||
|
||||
Assert.Equal(incrementCount, snapshot.AuditInboundCeilingHits);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AuditInboundCeilingHits_IsIndependentOfOtherCounters()
|
||||
{
|
||||
// Ceiling-hits increments must not cross-contaminate the other counters
|
||||
// and vice versa — each Interlocked field is independent.
|
||||
var snapshot = new AuditCentralHealthSnapshot();
|
||||
var ceilingCounter = (IAuditInboundCeilingHitsCounter)snapshot;
|
||||
var writeCounter = (ICentralAuditWriteFailureCounter)snapshot;
|
||||
var redactCounter = (ZB.MOM.WW.ScadaBridge.AuditLog.Payload.IAuditRedactionFailureCounter)snapshot;
|
||||
|
||||
ceilingCounter.Increment();
|
||||
ceilingCounter.Increment();
|
||||
writeCounter.Increment();
|
||||
redactCounter.Increment();
|
||||
redactCounter.Increment();
|
||||
redactCounter.Increment();
|
||||
|
||||
Assert.Equal(2, snapshot.AuditInboundCeilingHits);
|
||||
Assert.Equal(1, snapshot.CentralAuditWriteFailures);
|
||||
Assert.Equal(3, snapshot.AuditRedactionFailure);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user