221 lines
9.5 KiB
C#
221 lines
9.5 KiB
C#
using StellaOps.Policy;
|
|
using StellaOps.Policy.Determinization;
|
|
using StellaOps.Policy.Determinization.Models;
|
|
|
|
namespace StellaOps.Policy.Engine.Policies;
|
|
|
|
/// <summary>
|
|
/// Rule set for determinization policy evaluation.
|
|
/// Rules are evaluated in priority order (lower = higher priority).
|
|
/// </summary>
|
|
public sealed class DeterminizationRuleSet
|
|
{
|
|
public IReadOnlyList<DeterminizationRule> Rules { get; }
|
|
|
|
private DeterminizationRuleSet(IReadOnlyList<DeterminizationRule> rules)
|
|
{
|
|
Rules = rules;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates the default rule set per advisory specification.
|
|
/// </summary>
|
|
public static DeterminizationRuleSet Default(DeterminizationOptions options) =>
|
|
new(new List<DeterminizationRule>
|
|
{
|
|
// Rule 1: Escalate if runtime evidence shows vulnerable code loaded
|
|
new DeterminizationRule
|
|
{
|
|
Name = "RuntimeEscalation",
|
|
Priority = 10,
|
|
Condition = (ctx, _) =>
|
|
ctx.SignalSnapshot.Runtime.HasValue &&
|
|
ctx.SignalSnapshot.Runtime.Value!.ObservedLoaded,
|
|
Action = (ctx, _) =>
|
|
DeterminizationResult.Escalated(
|
|
"Runtime evidence shows vulnerable code loaded in memory")
|
|
},
|
|
|
|
// Rule 2: Quarantine if EPSS exceeds threshold
|
|
new DeterminizationRule
|
|
{
|
|
Name = "EpssQuarantine",
|
|
Priority = 20,
|
|
Condition = (ctx, thresholds) =>
|
|
ctx.SignalSnapshot.Epss.HasValue &&
|
|
ctx.SignalSnapshot.Epss.Value!.Score >= thresholds.EpssBlockThreshold,
|
|
Action = (ctx, thresholds) =>
|
|
DeterminizationResult.Quarantined(
|
|
$"EPSS score {ctx.SignalSnapshot.Epss.Value!.Score:P1} exceeds threshold {thresholds.EpssBlockThreshold:P1}")
|
|
},
|
|
|
|
// Rule 3: Quarantine if proven reachable
|
|
new DeterminizationRule
|
|
{
|
|
Name = "ReachabilityQuarantine",
|
|
Priority = 25,
|
|
Condition = (ctx, _) =>
|
|
ctx.SignalSnapshot.Reachability.HasValue &&
|
|
ctx.SignalSnapshot.Reachability.Value!.IsReachable,
|
|
Action = (ctx, _) =>
|
|
DeterminizationResult.Quarantined(
|
|
$"Vulnerable code is reachable via call graph analysis")
|
|
},
|
|
|
|
// Rule 4: Block high entropy in production
|
|
new DeterminizationRule
|
|
{
|
|
Name = "ProductionEntropyBlock",
|
|
Priority = 30,
|
|
Condition = (ctx, thresholds) =>
|
|
ctx.Environment == DeploymentEnvironment.Production &&
|
|
ctx.UncertaintyScore.Entropy > thresholds.MaxEntropyForAllow,
|
|
Action = (ctx, thresholds) =>
|
|
DeterminizationResult.Quarantined(
|
|
$"High uncertainty (entropy={ctx.UncertaintyScore.Entropy:F2}) exceeds production threshold ({thresholds.MaxEntropyForAllow:F2})")
|
|
},
|
|
|
|
// Rule 5: Defer if evidence is stale
|
|
new DeterminizationRule
|
|
{
|
|
Name = "StaleEvidenceDefer",
|
|
Priority = 40,
|
|
Condition = (ctx, _) => ctx.Decay.IsStale,
|
|
Action = (ctx, _) =>
|
|
DeterminizationResult.Deferred(
|
|
$"Evidence is stale (last update: {ctx.Decay.LastSignalUpdate:u}, age: {ctx.Decay.AgeDays:F1} days)")
|
|
},
|
|
|
|
// Rule 6: Guarded allow for uncertain observations in non-prod
|
|
new DeterminizationRule
|
|
{
|
|
Name = "GuardedAllowNonProd",
|
|
Priority = 50,
|
|
Condition = (ctx, _) =>
|
|
ctx.TrustScore < 0.5 &&
|
|
ctx.UncertaintyScore.Entropy > 0.4 &&
|
|
ctx.Environment != DeploymentEnvironment.Production,
|
|
Action = (ctx, _) =>
|
|
DeterminizationResult.GuardedPass(
|
|
$"Uncertain observation (entropy={ctx.UncertaintyScore.Entropy:F2}, trust={ctx.TrustScore:F2}) allowed with guardrails in {ctx.Environment}",
|
|
BuildGuardrails(ctx, GuardRailsLevel.Moderate))
|
|
},
|
|
|
|
// Rule 7: Allow if unreachable with high confidence
|
|
new DeterminizationRule
|
|
{
|
|
Name = "UnreachableAllow",
|
|
Priority = 60,
|
|
Condition = (ctx, thresholds) =>
|
|
ctx.SignalSnapshot.Reachability.HasValue &&
|
|
!ctx.SignalSnapshot.Reachability.Value!.IsReachable &&
|
|
ctx.SignalSnapshot.Reachability.Value.Confidence >= thresholds.MinConfidenceForNotAffected,
|
|
Action = (ctx, _) =>
|
|
DeterminizationResult.Allowed(
|
|
$"Vulnerable code is unreachable (confidence={ctx.SignalSnapshot.Reachability.Value!.Confidence:P0})")
|
|
},
|
|
|
|
// Rule 8: Allow if VEX not_affected with trusted issuer
|
|
new DeterminizationRule
|
|
{
|
|
Name = "VexNotAffectedAllow",
|
|
Priority = 65,
|
|
Condition = (ctx, thresholds) =>
|
|
ctx.SignalSnapshot.Vex.HasValue &&
|
|
ctx.SignalSnapshot.Vex.Value!.IsNotAffected &&
|
|
ctx.SignalSnapshot.Vex.Value.IssuerTrust >= thresholds.MinConfidenceForNotAffected,
|
|
Action = (ctx, _) =>
|
|
DeterminizationResult.Allowed(
|
|
$"VEX statement indicates not_affected (trust={ctx.SignalSnapshot.Vex.Value!.IssuerTrust:P0})")
|
|
},
|
|
|
|
// Rule 9: Allow if sufficient evidence and low entropy
|
|
new DeterminizationRule
|
|
{
|
|
Name = "SufficientEvidenceAllow",
|
|
Priority = 70,
|
|
Condition = (ctx, thresholds) =>
|
|
ctx.UncertaintyScore.Entropy <= thresholds.MaxEntropyForAllow &&
|
|
ctx.TrustScore >= thresholds.MinConfidenceForNotAffected,
|
|
Action = (ctx, _) =>
|
|
DeterminizationResult.Allowed(
|
|
$"Sufficient evidence (entropy={ctx.UncertaintyScore.Entropy:F2}, trust={ctx.TrustScore:F2}) for confident determination")
|
|
},
|
|
|
|
// Rule 10: Guarded allow for moderate uncertainty
|
|
new DeterminizationRule
|
|
{
|
|
Name = "GuardedAllowModerateUncertainty",
|
|
Priority = 80,
|
|
Condition = (ctx, _) =>
|
|
ctx.UncertaintyScore.Tier <= UncertaintyTier.Moderate &&
|
|
ctx.TrustScore >= 0.4,
|
|
Action = (ctx, _) =>
|
|
DeterminizationResult.GuardedPass(
|
|
$"Moderate uncertainty (tier={ctx.UncertaintyScore.Tier}, trust={ctx.TrustScore:F2}) allowed with monitoring",
|
|
BuildGuardrails(ctx, GuardRailsLevel.Light))
|
|
},
|
|
|
|
// Rule 11: Default - require more evidence
|
|
new DeterminizationRule
|
|
{
|
|
Name = "DefaultDefer",
|
|
Priority = 100,
|
|
Condition = (_, _) => true,
|
|
Action = (ctx, _) =>
|
|
DeterminizationResult.Deferred(
|
|
$"Insufficient evidence for determination (entropy={ctx.UncertaintyScore.Entropy:F2}, tier={ctx.UncertaintyScore.Tier})")
|
|
}
|
|
});
|
|
|
|
private enum GuardRailsLevel { Light, Moderate, Strict }
|
|
|
|
private static GuardRails BuildGuardrails(DeterminizationContext ctx, GuardRailsLevel level) =>
|
|
level switch
|
|
{
|
|
GuardRailsLevel.Light => new GuardRails
|
|
{
|
|
EnableMonitoring = true,
|
|
RestrictToNonProd = false,
|
|
RequireApproval = false,
|
|
ReevalAfter = TimeSpan.FromDays(14),
|
|
Notes = $"Light guardrails: entropy={ctx.UncertaintyScore.Entropy:F2}, trust={ctx.TrustScore:F2}, env={ctx.Environment}"
|
|
},
|
|
GuardRailsLevel.Moderate => new GuardRails
|
|
{
|
|
EnableMonitoring = true,
|
|
RestrictToNonProd = ctx.Environment == DeploymentEnvironment.Production,
|
|
RequireApproval = false,
|
|
ReevalAfter = TimeSpan.FromDays(7),
|
|
Notes = $"Moderate guardrails: entropy={ctx.UncertaintyScore.Entropy:F2}, trust={ctx.TrustScore:F2}, env={ctx.Environment}"
|
|
},
|
|
GuardRailsLevel.Strict => new GuardRails
|
|
{
|
|
EnableMonitoring = true,
|
|
RestrictToNonProd = true,
|
|
RequireApproval = true,
|
|
ReevalAfter = TimeSpan.FromDays(3),
|
|
Notes = $"Strict guardrails: entropy={ctx.UncertaintyScore.Entropy:F2}, trust={ctx.TrustScore:F2}, env={ctx.Environment}"
|
|
},
|
|
_ => GuardRails.Default()
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// A single determinization rule.
|
|
/// </summary>
|
|
public sealed record DeterminizationRule
|
|
{
|
|
/// <summary>Rule name for audit/logging.</summary>
|
|
public required string Name { get; init; }
|
|
|
|
/// <summary>Priority (lower = evaluated first).</summary>
|
|
public required int Priority { get; init; }
|
|
|
|
/// <summary>Condition function.</summary>
|
|
public required Func<DeterminizationContext, EnvironmentThresholds, bool> Condition { get; init; }
|
|
|
|
/// <summary>Action function.</summary>
|
|
public required Func<DeterminizationContext, EnvironmentThresholds, DeterminizationResult> Action { get; init; }
|
|
}
|