fix(inbound-api): RouteCall audit row honours the method's SkipBodyCapture override (arch-review F11)

RouteHelper/RouteTarget carry a new captureBodies flag (default true) threaded
through every With* copy, same shape as the site-scope field Task 3 added.
InboundScriptExecutor resolves IOptionsMonitor<AuditLogOptions> from the
per-execution DI scope and looks up PerTargetOverrides[method.Name]?.SkipBodyCapture
exactly as AuditWriteMiddleware.cs:183-187 does for the inbound row, then calls
.WithBodyCapture(!skip) when building the script's Route helper. When set, the
always-on ScriptTrace.RouteCall row omits its request/response summary, so an
operator who suppressed a method's body from the audit log doesn't get it back
via the routed call's arguments (derived from that same body).

Claude-Session: https://claude.ai/code/session_01NcMG6xXdsJbmD2wYAgkK29
This commit is contained in:
Joseph Doherty
2026-09-06 14:22:01 -04:00
parent 3f1981865a
commit 453c3dec18
4 changed files with 203 additions and 13 deletions
@@ -4,6 +4,8 @@ using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using ZB.MOM.WW.ScadaBridge.AuditLog.Configuration;
using ZB.MOM.WW.ScadaBridge.Commons.Entities.InboundApi;
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services;
@@ -411,12 +413,23 @@ public class InboundScriptExecutor
// Also bind the inbound request's ExecutionId (ParentExecutionId) so a
// routed call carries it as ParentExecutionId — the
// spawned site script execution points back at this inbound request.
// Arch-review F11: the always-on RouteCall row must honour the method's
// SkipBodyCapture override — the same per-target lookup AuditWriteMiddleware
// performs for the inbound row (AuditWriteMiddleware.cs:183-187) — so an
// operator who suppressed a method's body from the audit log doesn't get it
// back via the routed call's arguments (derived from that same body).
var auditOptions = scope?.ServiceProvider.GetService<IOptionsMonitor<AuditLogOptions>>()?.CurrentValue;
var skipBodyCapture = auditOptions != null
&& auditOptions.PerTargetOverrides.TryGetValue(method.Name, out var perTargetOverride)
&& perTargetOverride.SkipBodyCapture;
var context = new InboundScriptContext(
parameters,
route.WithDeadline(cts.Token)
.WithRequestAborted(cancellationToken)
.WithParentExecutionId(parentExecutionId)
.WithTrace(trace),
.WithTrace(trace)
.WithBodyCapture(!skipBodyCapture),
dbHelper,
cts.Token);