fix(management): Flow Debugger Live result is the redacted, capped trace copy (arch-review F1, central half)
RunMethodAsync collected sink events into a plain ConcurrentQueue and always reported Truncated: false, so the Live result carried RAW audit events — the per-target SQL-parameter overrides, header scrubbing and 8 KB / 64 KB payload caps that Replay reads back never applied to what the operator saw, and the reply had no upper bound at all. It now collects through the same FlowTraceCollector the site launcher uses, constructed with the host's IAuditRedactor (AddAuditLog's singleton; a host without one collects raw but is still capped by count and bytes), and reports Truncated from the collector. Complete() is called as soon as the run returns, so a late un-awaited call cannot mutate the snapshot the reply is built from. Claude-Session: https://claude.ai/code/session_01NcMG6xXdsJbmD2wYAgkK29
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
@@ -83,6 +82,14 @@ public sealed class FlowDebugRunner
|
||||
/// Live run of an inbound API method (design §7.1): active-node gate, schema validation,
|
||||
/// synthesized inbound row (actor = operator), traced execution through the real
|
||||
/// <see cref="InboundScriptExecutor"/> with a trace-sink <see cref="RouteHelper"/>.
|
||||
///
|
||||
/// <para>
|
||||
/// Arch-review F1: the sink is a <see cref="FlowTraceCollector"/> — the same collector the
|
||||
/// site launcher fills — so the returned copy is REDACTED and capped by count and bytes,
|
||||
/// and a Live result shows exactly the filtered rows a later Replay reads back. The
|
||||
/// redactor is <c>AddAuditLog</c>'s <see cref="IAuditRedactor"/> singleton; a host that
|
||||
/// registered none (a test composition root) collects raw but is still capped.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public async Task<FlowDebugRunResult> RunMethodAsync(
|
||||
IServiceProvider sp, FlowDebugRunMethodCommand cmd, AuthenticatedUser user, CancellationToken ct)
|
||||
@@ -106,7 +113,7 @@ public sealed class FlowDebugRunner
|
||||
}
|
||||
|
||||
var root = Guid.NewGuid();
|
||||
var events = new ConcurrentQueue<AuditEvent>();
|
||||
var collector = new FlowTraceCollector(redactor: sp.GetService<IAuditRedactor>());
|
||||
var startedAt = DateTimeOffset.UtcNow;
|
||||
var sw = Stopwatch.StartNew();
|
||||
var timeout = TimeSpan.FromSeconds(Math.Max(1, method.TimeoutSeconds));
|
||||
@@ -116,7 +123,7 @@ public sealed class FlowDebugRunner
|
||||
// only known once the script runs. So the operator's scope is pushed into the
|
||||
// RouteHelper and enforced at route time, before any site traffic.
|
||||
var scope = await ResolvePermittedSiteIdentifiersAsync(sp, user, ct).ConfigureAwait(false);
|
||||
var route = sp.GetRequiredService<RouteHelper>().WithTraceSink(events.Enqueue).WithSiteScope(scope);
|
||||
var route = sp.GetRequiredService<RouteHelper>().WithTraceSink(collector.Add).WithSiteScope(scope);
|
||||
var executor = sp.GetRequiredService<InboundScriptExecutor>();
|
||||
|
||||
InboundScriptResult result;
|
||||
@@ -137,6 +144,9 @@ public sealed class FlowDebugRunner
|
||||
}
|
||||
|
||||
var finishedAt = DateTimeOffset.UtcNow;
|
||||
// Close the sink here, not at the point the list is built: an un-awaited call finishing
|
||||
// after the run returned must not mutate the snapshot the reply is assembled from.
|
||||
var events = collector.Complete();
|
||||
var requestJson = cmd.Parameters is { } p ? p.GetRawText() : null;
|
||||
var inboundRow = ScadaBridgeAuditEventFactory.Create(
|
||||
AuditChannel.ApiInbound, AuditKind.InboundRequest,
|
||||
@@ -168,7 +178,7 @@ public sealed class FlowDebugRunner
|
||||
|
||||
return new FlowDebugRunResult(
|
||||
root, result.Success, result.ErrorMessage, result.Success ? null : result.ErrorCode,
|
||||
ordered, Truncated: false, startedAt, finishedAt);
|
||||
ordered, Truncated: collector.Truncated, startedAt, finishedAt);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user