113 lines
3.8 KiB
C#
113 lines
3.8 KiB
C#
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
using StellaOps.Policy.Determinization;
|
|
using StellaOps.Policy.Determinization.Models;
|
|
|
|
namespace StellaOps.Policy.Engine.Policies;
|
|
|
|
/// <summary>
|
|
/// Implements allow/quarantine/escalate logic per advisory specification.
|
|
/// </summary>
|
|
public sealed class DeterminizationPolicy : IDeterminizationPolicy
|
|
{
|
|
private readonly DeterminizationOptions _options;
|
|
private readonly DeterminizationRuleSet _ruleSet;
|
|
private readonly ILogger<DeterminizationPolicy> _logger;
|
|
|
|
public DeterminizationPolicy(
|
|
IOptions<DeterminizationOptions> options,
|
|
ILogger<DeterminizationPolicy> logger)
|
|
{
|
|
_options = options.Value;
|
|
_ruleSet = DeterminizationRuleSet.Default(_options);
|
|
_logger = logger;
|
|
}
|
|
|
|
public DeterminizationResult Evaluate(DeterminizationContext ctx)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(ctx);
|
|
|
|
// Get environment-specific thresholds
|
|
var thresholds = GetEnvironmentThresholds(ctx.Environment);
|
|
|
|
// Evaluate rules in priority order
|
|
foreach (var rule in _ruleSet.Rules.OrderBy(r => r.Priority))
|
|
{
|
|
if (rule.Condition(ctx, thresholds))
|
|
{
|
|
var result = rule.Action(ctx, thresholds);
|
|
result = result with { MatchedRule = rule.Name };
|
|
|
|
_logger.LogDebug(
|
|
"Rule {RuleName} matched for CVE {CveId}: {Status}",
|
|
rule.Name,
|
|
ctx.SignalSnapshot.Cve,
|
|
result.Status);
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// Default: Deferred (no rule matched, needs more evidence)
|
|
return DeterminizationResult.Deferred(
|
|
"No determinization rule matched; additional evidence required");
|
|
}
|
|
|
|
private EnvironmentThresholds GetEnvironmentThresholds(DeploymentEnvironment env)
|
|
{
|
|
return env switch
|
|
{
|
|
DeploymentEnvironment.Production => DefaultEnvironmentThresholds.Production,
|
|
DeploymentEnvironment.Staging => DefaultEnvironmentThresholds.Staging,
|
|
DeploymentEnvironment.Testing => DefaultEnvironmentThresholds.Development,
|
|
DeploymentEnvironment.Development => DefaultEnvironmentThresholds.Development,
|
|
_ => DefaultEnvironmentThresholds.Development
|
|
};
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Environment-specific thresholds for determinization decisions.
|
|
/// </summary>
|
|
public sealed record EnvironmentThresholds
|
|
{
|
|
public required DeploymentEnvironment Environment { get; init; }
|
|
public required double MinConfidenceForNotAffected { get; init; }
|
|
public required double MaxEntropyForAllow { get; init; }
|
|
public required double EpssBlockThreshold { get; init; }
|
|
public required bool RequireReachabilityForAllow { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Default environment thresholds per advisory.
|
|
/// </summary>
|
|
public static class DefaultEnvironmentThresholds
|
|
{
|
|
public static EnvironmentThresholds Production => new()
|
|
{
|
|
Environment = DeploymentEnvironment.Production,
|
|
MinConfidenceForNotAffected = 0.75,
|
|
MaxEntropyForAllow = 0.3,
|
|
EpssBlockThreshold = 0.3,
|
|
RequireReachabilityForAllow = true
|
|
};
|
|
|
|
public static EnvironmentThresholds Staging => new()
|
|
{
|
|
Environment = DeploymentEnvironment.Staging,
|
|
MinConfidenceForNotAffected = 0.60,
|
|
MaxEntropyForAllow = 0.5,
|
|
EpssBlockThreshold = 0.4,
|
|
RequireReachabilityForAllow = true
|
|
};
|
|
|
|
public static EnvironmentThresholds Development => new()
|
|
{
|
|
Environment = DeploymentEnvironment.Development,
|
|
MinConfidenceForNotAffected = 0.40,
|
|
MaxEntropyForAllow = 0.7,
|
|
EpssBlockThreshold = 0.6,
|
|
RequireReachabilityForAllow = false
|
|
};
|
|
}
|