117 lines
3.7 KiB
C#
117 lines
3.7 KiB
C#
// -----------------------------------------------------------------------------
|
|
// 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;
|
|
|
|
/// <summary>
|
|
/// Default implementation of <see cref="IVexGatePolicy"/>.
|
|
/// Evaluates evidence against policy rules in priority order.
|
|
/// </summary>
|
|
public sealed class VexGatePolicyEvaluator : IVexGatePolicy
|
|
{
|
|
private readonly ILogger<VexGatePolicyEvaluator> _logger;
|
|
private readonly VexGatePolicy _policy;
|
|
|
|
public VexGatePolicyEvaluator(
|
|
IOptions<VexGatePolicyOptions> options,
|
|
ILogger<VexGatePolicyEvaluator> logger)
|
|
{
|
|
_logger = logger;
|
|
_policy = options.Value.Policy ?? VexGatePolicy.Default;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates an evaluator with the default policy.
|
|
/// </summary>
|
|
public VexGatePolicyEvaluator(ILogger<VexGatePolicyEvaluator> logger)
|
|
{
|
|
_logger = logger;
|
|
_policy = VexGatePolicy.Default;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public VexGatePolicy Policy => _policy;
|
|
|
|
/// <inheritdoc />
|
|
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)
|
|
};
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Options for VEX gate policy configuration.
|
|
/// </summary>
|
|
public sealed class VexGatePolicyOptions
|
|
{
|
|
/// <summary>
|
|
/// Custom policy to use instead of default.
|
|
/// </summary>
|
|
public VexGatePolicy? Policy { get; set; }
|
|
|
|
/// <summary>
|
|
/// Whether the gate is enabled.
|
|
/// </summary>
|
|
public bool Enabled { get; set; } = true;
|
|
}
|