fix(mgmt): re-assert MxGateway protocol at secured-write execute (D2 TOCTOU guard, T14b)
This commit is contained in:
@@ -1045,6 +1045,35 @@ public class ManagementActor : ReceiveActor
|
||||
await EmitSecuredWriteAuditAsync(
|
||||
sp, AuditKind.SecuredWriteApprove, AuditStatus.Submitted, row, actor: user.Username);
|
||||
|
||||
// D2 / T14 TOCTOU guard: re-assert the MxGateway protocol AT EXECUTE, not only at
|
||||
// submit. The connection named on the row may have been reconfigured/recreated as a
|
||||
// non-MxGateway (e.g. OPC UA) connection between submit and approval; relaying then
|
||||
// would execute the secured write against a non-MxGateway adapter, violating the
|
||||
// feature's core safety invariant. Re-load the connection with the same lookup
|
||||
// submit uses (resolve site -> its data connections -> match by name) and fail the
|
||||
// row deterministically (mirrors the decode-error containment below) if it is
|
||||
// missing or no longer MxGateway — WITHOUT relaying.
|
||||
var siteRepo = sp.GetRequiredService<ISiteRepository>();
|
||||
var execSite = await siteRepo.GetSiteByIdentifierAsync(row.SiteId);
|
||||
var execConn = execSite is null
|
||||
? null
|
||||
: (await siteRepo.GetDataConnectionsBySiteIdAsync(execSite.Id))
|
||||
.FirstOrDefault(c => string.Equals(c.Name, row.ConnectionName, StringComparison.Ordinal));
|
||||
if (execConn is null ||
|
||||
!string.Equals(execConn.Protocol, "MxGateway", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
row.Status = "Failed";
|
||||
row.ExecutedAtUtc = DateTime.UtcNow;
|
||||
row.ExecutionError = execConn is null
|
||||
? $"connection '{row.ConnectionName}' not found"
|
||||
: $"connection '{row.ConnectionName}' is no longer an MxGateway connection";
|
||||
await repo.UpdateAsync(row);
|
||||
await EmitSecuredWriteAuditAsync(
|
||||
sp, AuditKind.SecuredWriteExecute, AuditStatus.Failed, row,
|
||||
actor: user.Username, errorMessage: row.ExecutionError);
|
||||
return ToSecuredWriteDto(row);
|
||||
}
|
||||
|
||||
// Validate the value type BEFORE attempting the relay. An unknown type can
|
||||
// never be decoded/written, so fail the row deterministically rather than
|
||||
// leaving it stuck Approved. (Addresses the C2 reviewer's deferred
|
||||
|
||||
Reference in New Issue
Block a user