fix(security): preflight reads the latest sealed artifact; byte[] length predicate untranslatable on SQL Server (#815 live gate)

This commit is contained in:
Joseph Doherty
2026-11-11 03:29:41 -05:00
parent 2a87d96fac
commit 481985e20b
3 changed files with 74 additions and 3 deletions
@@ -0,0 +1,57 @@
using System.Text;
using Microsoft.EntityFrameworkCore;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.AdminUI.Deployments;
using ZB.MOM.WW.OtOpcUa.Configuration.Entities;
using ZB.MOM.WW.OtOpcUa.Configuration.Enums;
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Deployments;
/// <summary>
/// The SQL-Server-backed half of <see cref="CredentialReferencePreflightTests"/> (lmxopcua#815). EF InMemory runs
/// <c>ScanAsync</c>'s deployment query as LINQ-to-Objects, so an untranslatable predicate passes there and throws on
/// every real boot — exactly what the #815 live gate found (<c>ArtifactBlob.Length &gt; 0</c>). Here it must translate.
/// </summary>
[Collection(nameof(AdminUiSqlServerCollection))]
public sealed class CredentialReferencePreflightSqlServerTests(AdminUiSqlServerFixture sql)
{
[SqlServerFact]
public async Task ScanAsync_translates_and_reads_the_latest_sealed_artifact()
{
// Far-future timestamps so this test's rows are the latest whatever else the shared database holds.
var anchor = new DateTime(2999, 1, 1, 0, 0, 0, DateTimeKind.Utc);
var sealedId = Guid.NewGuid();
var failedId = Guid.NewGuid();
const string artifact = """
{"DriverInstances":[{"DriverInstanceId":"ouc-sql","Name":"UpstreamSql","DriverType":"OpcUaClient",
"DriverConfig":"{\"Password\":\"secret:plant/opcua-pw\"}"}]}
""";
await using (var db = await sql.Factory.CreateDbContextAsync(TestContext.Current.CancellationToken))
{
db.Deployments.Add(new Deployment
{
DeploymentId = sealedId, RevisionHash = "preflight-sealed", CreatedBy = "t", Status = DeploymentStatus.Sealed,
CreatedAtUtc = anchor, ArtifactBlob = Encoding.UTF8.GetBytes(artifact),
});
db.Deployments.Add(new Deployment
{
DeploymentId = failedId, RevisionHash = "preflight-failed", CreatedBy = "t", Status = DeploymentStatus.PartiallyFailed,
CreatedAtUtc = anchor.AddMinutes(1), ArtifactBlob = Encoding.UTF8.GetBytes("""{"DriverInstances":[]}"""),
});
await db.SaveChangesAsync(TestContext.Current.CancellationToken);
}
try
{
var findings = await new CredentialReferencePreflight(sql.Factory).ScanAsync(TestContext.Current.CancellationToken);
findings.ShouldContain(f => f.Source == CredentialFindingSource.Deployed && f.DriverName == "UpstreamSql");
}
finally
{
await using var db = await sql.Factory.CreateDbContextAsync(CancellationToken.None);
await db.Deployments.Where(d => d.DeploymentId == sealedId || d.DeploymentId == failedId).ExecuteDeleteAsync(CancellationToken.None);
}
}
}
@@ -9,6 +9,7 @@ using ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Uns;
using ZB.MOM.WW.OtOpcUa.Commons.Messages.Drivers;
using ZB.MOM.WW.OtOpcUa.Configuration;
using ZB.MOM.WW.OtOpcUa.Configuration.Entities;
using ZB.MOM.WW.OtOpcUa.Configuration.Enums;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Deployments;
@@ -48,7 +49,16 @@ public sealed class CredentialReferencePreflightTests
{"DriverInstances":[{"DriverInstanceId":"ouc","Name":"Upstream","DriverType":"OpcUaClient",
"DriverConfig":"{\"Password\":\"secret:plant/opcua-pw\"}"}]}
""";
db.Deployments.Add(new Deployment { RevisionHash = "abc", CreatedBy = "t", ArtifactBlob = Encoding.UTF8.GetBytes(artifact) });
db.Deployments.Add(new Deployment
{
RevisionHash = "abc", CreatedBy = "t", Status = DeploymentStatus.Sealed, ArtifactBlob = Encoding.UTF8.GetBytes(artifact),
});
// A newer deployment that never sealed is not what the fleet runs — it must not mask the sealed one.
db.Deployments.Add(new Deployment
{
RevisionHash = "failed", CreatedBy = "t", Status = DeploymentStatus.PartiallyFailed,
CreatedAtUtc = DateTime.UtcNow.AddMinutes(5), ArtifactBlob = Encoding.UTF8.GetBytes("""{"DriverInstances":[]}"""),
});
db.SaveChanges();
return name;
}