fix(security): enforce template locks on native alarm source overrides at flatten and management-command level
Add IsLocked to ResolvedNativeAlarmSource; FlatteningService skips overrides on a locked source (mirrors attribute/alarm lock rules); ManagementActor SetInstanceNativeAlarmSourceOverride flattens the instance and rejects an override on a locked source (and rejects a canonical name that does not resolve, preventing dangling overrides). Claude-Session: https://claude.ai/code/session_01MtdgwpEeCUn6cUA5f1LMPj
This commit is contained in:
@@ -12,6 +12,8 @@ using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Management;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Types;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Types.Flattening;
|
||||
using ZB.MOM.WW.ScadaBridge.DeploymentManager;
|
||||
using ZB.MOM.WW.ScadaBridge.ManagementService;
|
||||
using ZB.MOM.WW.ScadaBridge.TemplateEngine;
|
||||
using ZB.MOM.WW.ScadaBridge.TemplateEngine.Services;
|
||||
@@ -232,6 +234,39 @@ public class ManagementActorTests : TestKit, IDisposable
|
||||
Assert.Contains("Pump1", response.JsonData);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetNativeAlarmSourceOverride_OnLockedSource_ReturnsManagementError()
|
||||
{
|
||||
// #05-T13: the ManagementActor must reject an override targeting a source
|
||||
// that is locked at the template level, before persisting anything.
|
||||
var pipeline = Substitute.For<IFlatteningPipeline>();
|
||||
var flattened = new FlattenedConfiguration
|
||||
{
|
||||
NativeAlarmSources = new List<ResolvedNativeAlarmSource>
|
||||
{
|
||||
new() { CanonicalName = "Pressure", IsLocked = true, Source = "Template" }
|
||||
}
|
||||
};
|
||||
pipeline.FlattenAndValidateAsync(10, Arg.Any<CancellationToken>(), Arg.Any<bool>())
|
||||
.Returns(Result<FlatteningPipelineResult>.Success(
|
||||
new FlatteningPipelineResult(flattened, "hash", ValidationResult.Success())));
|
||||
_services.AddScoped(_ => pipeline);
|
||||
|
||||
var actor = CreateActor();
|
||||
var envelope = Envelope(
|
||||
new SetInstanceNativeAlarmSourceOverrideCommand(10, "Pressure", "OtherOpc", null, null),
|
||||
"Deployer");
|
||||
|
||||
actor.Tell(envelope);
|
||||
|
||||
var response = ExpectMsg<ManagementError>(TimeSpan.FromSeconds(5));
|
||||
Assert.Equal(envelope.CorrelationId, response.CorrelationId);
|
||||
Assert.Contains("locked", response.Error, StringComparison.OrdinalIgnoreCase);
|
||||
// The override must NOT be persisted.
|
||||
_templateRepo.DidNotReceive().AddInstanceNativeAlarmSourceOverrideAsync(
|
||||
Arg.Any<InstanceNativeAlarmSourceOverride>());
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// 5. Error handling test -- service throws exception
|
||||
// ========================================================================
|
||||
@@ -2479,7 +2514,21 @@ public class ManagementActorTests : TestKit, IDisposable
|
||||
[Fact]
|
||||
public void SetInstanceNativeAlarmSourceOverride_WithDeploymentRole_ReturnsSuccess()
|
||||
{
|
||||
// No prior override → Add path.
|
||||
// No prior override → Add path. The source resolves and is UNLOCKED, so the
|
||||
// lock guard (#05-T13) lets the override through.
|
||||
var pipeline = Substitute.For<IFlatteningPipeline>();
|
||||
var flattened = new FlattenedConfiguration
|
||||
{
|
||||
NativeAlarmSources = new List<ResolvedNativeAlarmSource>
|
||||
{
|
||||
new() { CanonicalName = "Pressure", IsLocked = false, Source = "Template" }
|
||||
}
|
||||
};
|
||||
pipeline.FlattenAndValidateAsync(1, Arg.Any<CancellationToken>(), Arg.Any<bool>())
|
||||
.Returns(Result<FlatteningPipelineResult>.Success(
|
||||
new FlatteningPipelineResult(flattened, "hash", ValidationResult.Success())));
|
||||
_services.AddScoped(_ => pipeline);
|
||||
|
||||
var actor = CreateActor();
|
||||
var envelope = Envelope(
|
||||
new SetInstanceNativeAlarmSourceOverrideCommand(1, "Pressure", "Opc2", "ns=2;s=NEW", null),
|
||||
|
||||
Reference in New Issue
Block a user