Files
git.stella-ops.org/src/Policy/__Tests/StellaOps.Policy.Engine.Tests/Policies/DeterminizationPolicyTests.cs
2026-01-07 09:43:12 +02:00

277 lines
8.6 KiB
C#

using FluentAssertions;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using StellaOps.Policy;
using StellaOps.Policy.Determinization;
using StellaOps.Policy.Determinization.Models;
using StellaOps.Policy.Engine.Policies;
namespace StellaOps.Policy.Engine.Tests.Policies;
public class DeterminizationPolicyTests
{
private readonly DeterminizationPolicy _policy;
public DeterminizationPolicyTests()
{
var options = Options.Create(new DeterminizationOptions());
_policy = new DeterminizationPolicy(options, NullLogger<DeterminizationPolicy>.Instance);
}
[Fact]
public void Evaluate_RuntimeEvidenceLoaded_ReturnsEscalated()
{
// Arrange
var context = CreateContext(
runtime: new SignalState<RuntimeEvidence>
{
HasValue = true,
Value = new RuntimeEvidence { ObservedLoaded = true }
});
// Act
var result = _policy.Evaluate(context);
// Assert
result.Status.Should().Be(PolicyVerdictStatus.Escalated);
result.MatchedRule.Should().Be("RuntimeEscalation");
result.Reason.Should().Contain("Runtime evidence shows vulnerable code loaded");
}
[Fact]
public void Evaluate_HighEpss_ReturnsQuarantined()
{
// Arrange
var context = CreateContext(
epss: new SignalState<EpssEvidence>
{
HasValue = true,
Value = new EpssEvidence { Score = 0.8 }
},
environment: DeploymentEnvironment.Production);
// Act
var result = _policy.Evaluate(context);
// Assert
result.Status.Should().Be(PolicyVerdictStatus.Blocked);
result.MatchedRule.Should().Be("EpssQuarantine");
result.Reason.Should().Contain("EPSS score");
}
[Fact]
public void Evaluate_ReachableCode_ReturnsQuarantined()
{
// Arrange
var context = CreateContext(
reachability: new SignalState<ReachabilityEvidence>
{
HasValue = true,
Value = new ReachabilityEvidence { IsReachable = true, Confidence = 0.9 }
});
// Act
var result = _policy.Evaluate(context);
// Assert
result.Status.Should().Be(PolicyVerdictStatus.Blocked);
result.MatchedRule.Should().Be("ReachabilityQuarantine");
result.Reason.Should().Contain("reachable");
}
[Fact]
public void Evaluate_HighEntropyInProduction_ReturnsQuarantined()
{
// Arrange
var context = CreateContext(
entropy: 0.5,
environment: DeploymentEnvironment.Production);
// Act
var result = _policy.Evaluate(context);
// Assert
result.Status.Should().Be(PolicyVerdictStatus.Blocked);
result.MatchedRule.Should().Be("ProductionEntropyBlock");
result.Reason.Should().Contain("High uncertainty");
}
[Fact]
public void Evaluate_StaleEvidence_ReturnsDeferred()
{
// Arrange
var context = CreateContext(
isStale: true);
// Act
var result = _policy.Evaluate(context);
// Assert
result.Status.Should().Be(PolicyVerdictStatus.Deferred);
result.MatchedRule.Should().Be("StaleEvidenceDefer");
result.Reason.Should().Contain("stale");
}
[Fact]
public void Evaluate_ModerateUncertaintyInDev_ReturnsGuardedPass()
{
// Arrange
var context = CreateContext(
entropy: 0.5,
trustScore: 0.3,
environment: DeploymentEnvironment.Development);
// Act
var result = _policy.Evaluate(context);
// Assert
result.Status.Should().Be(PolicyVerdictStatus.GuardedPass);
result.MatchedRule.Should().Be("GuardedAllowNonProd");
result.GuardRails.Should().NotBeNull();
result.GuardRails!.EnableMonitoring.Should().BeTrue();
}
[Fact]
public void Evaluate_UnreachableWithHighConfidence_ReturnsAllowed()
{
// Arrange
var context = CreateContext(
reachability: new SignalState<ReachabilityEvidence>
{
HasValue = true,
Value = new ReachabilityEvidence { IsReachable = false, Confidence = 0.9 }
},
trustScore: 0.8);
// Act
var result = _policy.Evaluate(context);
// Assert
result.Status.Should().Be(PolicyVerdictStatus.Pass);
result.MatchedRule.Should().Be("UnreachableAllow");
result.Reason.Should().Contain("unreachable");
}
[Fact]
public void Evaluate_VexNotAffected_ReturnsAllowed()
{
// Arrange
var context = CreateContext(
vex: new SignalState<VexClaimSummary>
{
HasValue = true,
Value = new VexClaimSummary { IsNotAffected = true, IssuerTrust = 0.9 }
},
trustScore: 0.8);
// Act
var result = _policy.Evaluate(context);
// Assert
result.Status.Should().Be(PolicyVerdictStatus.Pass);
result.MatchedRule.Should().Be("VexNotAffectedAllow");
result.Reason.Should().Contain("not_affected");
}
[Fact]
public void Evaluate_SufficientEvidenceLowEntropy_ReturnsAllowed()
{
// Arrange
var context = CreateContext(
entropy: 0.2,
trustScore: 0.8,
environment: DeploymentEnvironment.Production);
// Act
var result = _policy.Evaluate(context);
// Assert
result.Status.Should().Be(PolicyVerdictStatus.Pass);
result.MatchedRule.Should().Be("SufficientEvidenceAllow");
result.Reason.Should().Contain("Sufficient evidence");
}
[Fact]
public void Evaluate_ModerateUncertaintyTier_ReturnsGuardedPass()
{
// Arrange
var context = CreateContext(
tier: UncertaintyTier.Moderate,
trustScore: 0.5,
entropy: 0.5);
// Act
var result = _policy.Evaluate(context);
// Assert
result.Status.Should().Be(PolicyVerdictStatus.GuardedPass);
result.MatchedRule.Should().Be("GuardedAllowModerateUncertainty");
result.GuardRails.Should().NotBeNull();
}
[Fact]
public void Evaluate_NoMatchingRule_ReturnsDeferred()
{
// Arrange
var context = CreateContext(
entropy: 0.9,
trustScore: 0.1,
environment: DeploymentEnvironment.Production);
// Act
var result = _policy.Evaluate(context);
// Assert
result.Status.Should().Be(PolicyVerdictStatus.Deferred);
result.MatchedRule.Should().Be("DefaultDefer");
result.Reason.Should().Contain("Insufficient evidence");
}
private static DeterminizationContext CreateContext(
SignalState<EpssEvidence>? epss = null,
SignalState<VexClaimSummary>? vex = null,
SignalState<ReachabilityEvidence>? reachability = null,
SignalState<RuntimeEvidence>? runtime = null,
double entropy = 0.0,
double trustScore = 0.0,
UncertaintyTier tier = UncertaintyTier.Minimal,
DeploymentEnvironment environment = DeploymentEnvironment.Development,
bool isStale = false)
{
var snapshot = new SignalSnapshot
{
Cve = "CVE-2024-0001",
Purl = "pkg:npm/test@1.0.0",
Epss = epss ?? SignalState<EpssEvidence>.NotQueried(),
Vex = vex ?? SignalState<VexClaimSummary>.NotQueried(),
Reachability = reachability ?? SignalState<ReachabilityEvidence>.NotQueried(),
Runtime = runtime ?? SignalState<RuntimeEvidence>.NotQueried(),
Backport = SignalState<BackportEvidence>.NotQueried(),
Sbom = SignalState<SbomLineageEvidence>.NotQueried(),
Cvss = SignalState<CvssEvidence>.NotQueried(),
SnapshotAt = DateTimeOffset.UtcNow
};
return new DeterminizationContext
{
SignalSnapshot = snapshot,
UncertaintyScore = new UncertaintyScore
{
Entropy = entropy,
Tier = tier,
Completeness = 1.0 - entropy,
MissingSignals = []
},
Decay = new ObservationDecay
{
LastSignalUpdate = DateTimeOffset.UtcNow.AddDays(-1),
AgeDays = 1,
DecayedMultiplier = isStale ? 0.3 : 0.9,
IsStale = isStale
},
TrustScore = trustScore,
Environment = environment
};
}
}