// ----------------------------------------------------------------------------- // VexGatePolicyEvaluator.cs // Sprint: SPRINT_20260106_003_002_SCANNER_vex_gate_service // Description: Policy evaluator for VEX gate decisions. // ----------------------------------------------------------------------------- using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace StellaOps.Scanner.Gate; /// /// Default implementation of . /// Evaluates evidence against policy rules in priority order. /// public sealed class VexGatePolicyEvaluator : IVexGatePolicy { private readonly ILogger _logger; private readonly VexGatePolicy _policy; public VexGatePolicyEvaluator( IOptions options, ILogger logger) { _logger = logger; _policy = options.Value.Policy ?? VexGatePolicy.Default; } /// /// Creates an evaluator with the default policy. /// public VexGatePolicyEvaluator(ILogger logger) { _logger = logger; _policy = VexGatePolicy.Default; } /// public VexGatePolicy Policy => _policy; /// public (VexGateDecision Decision, string RuleId, string Rationale) Evaluate(VexGateEvidence evidence) { // Sort rules by priority descending and evaluate in order var sortedRules = _policy.Rules .OrderByDescending(r => r.Priority) .ToList(); foreach (var rule in sortedRules) { if (rule.Condition.Matches(evidence)) { var rationale = BuildRationale(rule, evidence); _logger.LogDebug( "VEX gate rule matched: {RuleId} -> {Decision} for evidence with vendor status {VendorStatus}", rule.RuleId, rule.Decision, evidence.VendorStatus); return (rule.Decision, rule.RuleId, rationale); } } // No rule matched, return default var defaultRationale = "No policy rule matched; applying default decision"; _logger.LogDebug( "No VEX gate rule matched; defaulting to {Decision}", _policy.DefaultDecision); return (_policy.DefaultDecision, "default", defaultRationale); } private static string BuildRationale(VexGatePolicyRule rule, VexGateEvidence evidence) { return rule.RuleId switch { "block-exploitable-reachable" => "Exploitable + reachable, no compensating control", "warn-high-not-reachable" => string.Format( System.Globalization.CultureInfo.InvariantCulture, "{0} severity but not reachable from entrypoints", evidence.SeverityLevel ?? "High"), "pass-vendor-not-affected" => "Vendor VEX statement declares not_affected", "pass-backport-confirmed" => "Vendor VEX statement confirms fixed via backport", _ => string.Format( System.Globalization.CultureInfo.InvariantCulture, "Policy rule '{0}' matched", rule.RuleId) }; } } /// /// Options for VEX gate policy configuration. /// public sealed class VexGatePolicyOptions { /// /// Custom policy to use instead of default. /// public VexGatePolicy? Policy { get; set; } /// /// Whether the gate is enabled. /// public bool Enabled { get; set; } = true; }