using System.Collections.Immutable; using Xunit; namespace StellaOps.Policy.Tests; public sealed class PolicyEvaluationTests { [Fact] public void EvaluateFinding_AppliesTrustAndReachabilityWeights() { var action = new PolicyAction(PolicyActionType.Block, null, null, null, false); var rule = PolicyRule.Create( "BlockMedium", action, ImmutableArray.Create(PolicySeverity.Medium), ImmutableArray.Empty, ImmutableArray.Empty, ImmutableArray.Empty, ImmutableArray.Empty, ImmutableArray.Empty, PolicyRuleMatchCriteria.Empty, expires: null, justification: null); var document = new PolicyDocument( PolicySchema.CurrentVersion, ImmutableArray.Create(rule), ImmutableDictionary.Empty, PolicyExceptionConfiguration.Empty); var config = PolicyScoringConfig.Default; var finding = PolicyFinding.Create( "finding-medium", PolicySeverity.Medium, source: "community", tags: ImmutableArray.Create("reachability:indirect")); var verdict = PolicyEvaluation.EvaluateFinding(document, config, finding); Assert.Equal(PolicyVerdictStatus.Blocked, verdict.Status); Assert.Equal(19.5, verdict.Score, 3); var inputs = verdict.GetInputs(); Assert.Equal(50, inputs["severityWeight"]); Assert.Equal(0.65, inputs["trustWeight"], 3); Assert.Equal(0.6, inputs["reachabilityWeight"], 3); Assert.Equal(19.5, inputs["baseScore"], 3); } [Fact] public void EvaluateFinding_QuietWithRequireVexAppliesQuietPenalty() { var ignoreOptions = new PolicyIgnoreOptions(null, null); var requireVexOptions = new PolicyRequireVexOptions( ImmutableArray.Empty, ImmutableArray.Empty); var action = new PolicyAction(PolicyActionType.Ignore, ignoreOptions, null, requireVexOptions, true); var rule = PolicyRule.Create( "QuietIgnore", action, ImmutableArray.Create(PolicySeverity.Critical), ImmutableArray.Empty, ImmutableArray.Empty, ImmutableArray.Empty, ImmutableArray.Empty, ImmutableArray.Empty, PolicyRuleMatchCriteria.Empty, expires: null, justification: null); var document = new PolicyDocument( PolicySchema.CurrentVersion, ImmutableArray.Create(rule), ImmutableDictionary.Empty, PolicyExceptionConfiguration.Empty); var config = PolicyScoringConfig.Default; var finding = PolicyFinding.Create( "finding-critical", PolicySeverity.Critical, tags: ImmutableArray.Create("reachability:entrypoint")); var verdict = PolicyEvaluation.EvaluateFinding(document, config, finding); Assert.Equal(PolicyVerdictStatus.Ignored, verdict.Status); Assert.True(verdict.Quiet); Assert.Equal("QuietIgnore", verdict.QuietedBy); Assert.Equal(10, verdict.Score, 3); var inputs = verdict.GetInputs(); Assert.Equal(90, inputs["baseScore"], 3); Assert.Equal(config.IgnorePenalty, inputs["ignorePenalty"]); Assert.Equal(config.QuietPenalty, inputs["quietPenalty"]); } [Fact] public void EvaluateFinding_UnknownSeverityComputesConfidence() { var action = new PolicyAction(PolicyActionType.Block, null, null, null, false); var rule = PolicyRule.Create( "BlockUnknown", action, ImmutableArray.Create(PolicySeverity.Unknown), ImmutableArray.Empty, ImmutableArray.Empty, ImmutableArray.Empty, ImmutableArray.Empty, ImmutableArray.Empty, PolicyRuleMatchCriteria.Empty, expires: null, justification: null); var document = new PolicyDocument( PolicySchema.CurrentVersion, ImmutableArray.Create(rule), ImmutableDictionary.Empty, PolicyExceptionConfiguration.Empty); var config = PolicyScoringConfig.Default; var finding = PolicyFinding.Create( "finding-unknown", PolicySeverity.Unknown, tags: ImmutableArray.Create("reachability:unknown", "unknown-age-days:5")); var verdict = PolicyEvaluation.EvaluateFinding(document, config, finding); Assert.Equal(PolicyVerdictStatus.Blocked, verdict.Status); Assert.Equal(30, verdict.Score, 3); // 60 * 1 * 0.5 Assert.Equal(0.55, verdict.UnknownConfidence ?? 0, 3); Assert.Equal("medium", verdict.ConfidenceBand); Assert.Equal(5, verdict.UnknownAgeDays ?? 0, 3); var inputs = verdict.GetInputs(); Assert.Equal(0.55, inputs["unknownConfidence"], 3); Assert.Equal(5, inputs["unknownAgeDays"], 3); } }