feat(rate-limiting): Implement core rate limiting functionality with configuration, decision-making, metrics, middleware, and service registration

- Add RateLimitConfig for configuration management with YAML binding support.
- Introduce RateLimitDecision to encapsulate the result of rate limit checks.
- Implement RateLimitMetrics for OpenTelemetry metrics tracking.
- Create RateLimitMiddleware for enforcing rate limits on incoming requests.
- Develop RateLimitService to orchestrate instance and environment rate limit checks.
- Add RateLimitServiceCollectionExtensions for dependency injection registration.
This commit is contained in:
master
2025-12-17 18:02:37 +02:00
parent 394b57f6bf
commit 8bbfe4d2d2
211 changed files with 47179 additions and 1590 deletions

View File

@@ -0,0 +1,352 @@
// -----------------------------------------------------------------------------
// SmartDiffScoringConfig.cs
// Sprint: SPRINT_3500_0004_0001_smart_diff_binary_output
// Task: SDIFF-BIN-019 - Implement SmartDiffScoringConfig with presets
// Task: SDIFF-BIN-021 - Implement ToDetectorOptions() conversion
// Description: Configurable scoring weights for Smart-Diff detection
// -----------------------------------------------------------------------------
using System.Text.Json.Serialization;
namespace StellaOps.Scanner.SmartDiff.Detection;
/// <summary>
/// Comprehensive configuration for Smart-Diff scoring.
/// Exposes all configurable weights and thresholds for risk detection.
/// Per Sprint 3500.4 - Smart-Diff Scoring Configuration.
/// </summary>
public sealed class SmartDiffScoringConfig
{
/// <summary>
/// Configuration name/identifier.
/// </summary>
[JsonPropertyName("name")]
public string Name { get; init; } = "default";
/// <summary>
/// Configuration version for compatibility tracking.
/// </summary>
[JsonPropertyName("version")]
public string Version { get; init; } = "1.0";
#region Rule R1: Reachability
/// <summary>
/// Weight for reachability flip from unreachable to reachable (risk increase).
/// </summary>
[JsonPropertyName("reachabilityFlipUpWeight")]
public double ReachabilityFlipUpWeight { get; init; } = 1.0;
/// <summary>
/// Weight for reachability flip from reachable to unreachable (risk decrease).
/// </summary>
[JsonPropertyName("reachabilityFlipDownWeight")]
public double ReachabilityFlipDownWeight { get; init; } = 0.8;
/// <summary>
/// Whether to consider lattice confidence in reachability scoring.
/// </summary>
[JsonPropertyName("useLatticeConfidence")]
public bool UseLatticeConfidence { get; init; } = true;
#endregion
#region Rule R2: VEX Status
/// <summary>
/// Weight for VEX status flip to affected.
/// </summary>
[JsonPropertyName("vexFlipToAffectedWeight")]
public double VexFlipToAffectedWeight { get; init; } = 0.9;
/// <summary>
/// Weight for VEX status flip to not_affected.
/// </summary>
[JsonPropertyName("vexFlipToNotAffectedWeight")]
public double VexFlipToNotAffectedWeight { get; init; } = 0.7;
/// <summary>
/// Weight for VEX status flip to fixed.
/// </summary>
[JsonPropertyName("vexFlipToFixedWeight")]
public double VexFlipToFixedWeight { get; init; } = 0.6;
/// <summary>
/// Weight for VEX status flip to under_investigation.
/// </summary>
[JsonPropertyName("vexFlipToUnderInvestigationWeight")]
public double VexFlipToUnderInvestigationWeight { get; init; } = 0.3;
#endregion
#region Rule R3: Affected Range
/// <summary>
/// Weight for entering the affected version range.
/// </summary>
[JsonPropertyName("rangeEntryWeight")]
public double RangeEntryWeight { get; init; } = 0.8;
/// <summary>
/// Weight for exiting the affected version range.
/// </summary>
[JsonPropertyName("rangeExitWeight")]
public double RangeExitWeight { get; init; } = 0.6;
#endregion
#region Rule R4: Intelligence Signals
/// <summary>
/// Weight for KEV (Known Exploited Vulnerability) addition.
/// </summary>
[JsonPropertyName("kevAddedWeight")]
public double KevAddedWeight { get; init; } = 1.0;
/// <summary>
/// Weight for KEV removal.
/// </summary>
[JsonPropertyName("kevRemovedWeight")]
public double KevRemovedWeight { get; init; } = 0.5;
/// <summary>
/// Weight for EPSS threshold crossing.
/// </summary>
[JsonPropertyName("epssThresholdWeight")]
public double EpssThresholdWeight { get; init; } = 0.6;
/// <summary>
/// EPSS score threshold for R4 detection (0.0 - 1.0).
/// </summary>
[JsonPropertyName("epssThreshold")]
public double EpssThreshold { get; init; } = 0.5;
/// <summary>
/// Weight for policy decision flip.
/// </summary>
[JsonPropertyName("policyFlipWeight")]
public double PolicyFlipWeight { get; init; } = 0.7;
#endregion
#region Hardening Detection
/// <summary>
/// Weight for hardening regression detection.
/// </summary>
[JsonPropertyName("hardeningRegressionWeight")]
public double HardeningRegressionWeight { get; init; } = 0.7;
/// <summary>
/// Minimum hardening score difference to trigger a finding.
/// </summary>
[JsonPropertyName("hardeningScoreThreshold")]
public double HardeningScoreThreshold { get; init; } = 0.2;
/// <summary>
/// Whether to include hardening flags in diff output.
/// </summary>
[JsonPropertyName("includeHardeningFlags")]
public bool IncludeHardeningFlags { get; init; } = true;
#endregion
#region Priority Score Factors
/// <summary>
/// Multiplier applied when finding is in KEV.
/// </summary>
[JsonPropertyName("kevBoost")]
public double KevBoost { get; init; } = 1.5;
/// <summary>
/// Minimum priority score to emit a finding.
/// </summary>
[JsonPropertyName("minPriorityScore")]
public double MinPriorityScore { get; init; } = 0.1;
/// <summary>
/// Threshold for "high priority" classification.
/// </summary>
[JsonPropertyName("highPriorityThreshold")]
public double HighPriorityThreshold { get; init; } = 0.7;
/// <summary>
/// Threshold for "critical priority" classification.
/// </summary>
[JsonPropertyName("criticalPriorityThreshold")]
public double CriticalPriorityThreshold { get; init; } = 0.9;
#endregion
#region Presets
/// <summary>
/// Default configuration - balanced detection.
/// </summary>
public static SmartDiffScoringConfig Default => new()
{
Name = "default"
};
/// <summary>
/// Security-focused preset - aggressive detection, lower thresholds.
/// </summary>
public static SmartDiffScoringConfig SecurityFocused => new()
{
Name = "security-focused",
ReachabilityFlipUpWeight = 1.2,
VexFlipToAffectedWeight = 1.0,
KevAddedWeight = 1.5,
EpssThreshold = 0.3,
EpssThresholdWeight = 0.8,
HardeningRegressionWeight = 0.9,
HardeningScoreThreshold = 0.15,
MinPriorityScore = 0.05,
HighPriorityThreshold = 0.5,
CriticalPriorityThreshold = 0.8
};
/// <summary>
/// Compliance-focused preset - stricter thresholds for regulated environments.
/// </summary>
public static SmartDiffScoringConfig ComplianceFocused => new()
{
Name = "compliance-focused",
ReachabilityFlipUpWeight = 1.0,
VexFlipToAffectedWeight = 1.0,
VexFlipToNotAffectedWeight = 0.9,
KevAddedWeight = 2.0,
EpssThreshold = 0.2,
PolicyFlipWeight = 1.0,
HardeningRegressionWeight = 1.0,
HardeningScoreThreshold = 0.1,
MinPriorityScore = 0.0,
HighPriorityThreshold = 0.4,
CriticalPriorityThreshold = 0.7
};
/// <summary>
/// Developer-friendly preset - reduced noise, focus on actionable changes.
/// </summary>
public static SmartDiffScoringConfig DeveloperFriendly => new()
{
Name = "developer-friendly",
ReachabilityFlipUpWeight = 0.8,
VexFlipToAffectedWeight = 0.7,
KevAddedWeight = 1.0,
EpssThreshold = 0.7,
EpssThresholdWeight = 0.4,
HardeningRegressionWeight = 0.5,
HardeningScoreThreshold = 0.3,
MinPriorityScore = 0.2,
HighPriorityThreshold = 0.8,
CriticalPriorityThreshold = 0.95
};
/// <summary>
/// Get a preset configuration by name.
/// </summary>
public static SmartDiffScoringConfig GetPreset(string name) => name.ToLowerInvariant() switch
{
"default" => Default,
"security-focused" or "security" => SecurityFocused,
"compliance-focused" or "compliance" => ComplianceFocused,
"developer-friendly" or "developer" => DeveloperFriendly,
_ => throw new ArgumentException($"Unknown scoring preset: {name}")
};
#endregion
#region Conversion Methods
/// <summary>
/// Convert to MaterialRiskChangeOptions for use with the detector.
/// Task: SDIFF-BIN-021.
/// </summary>
public MaterialRiskChangeOptions ToDetectorOptions() => new()
{
ReachabilityFlipUpWeight = ReachabilityFlipUpWeight,
ReachabilityFlipDownWeight = ReachabilityFlipDownWeight,
VexFlipToAffectedWeight = VexFlipToAffectedWeight,
VexFlipToNotAffectedWeight = VexFlipToNotAffectedWeight,
RangeEntryWeight = RangeEntryWeight,
RangeExitWeight = RangeExitWeight,
KevAddedWeight = KevAddedWeight,
KevRemovedWeight = KevRemovedWeight,
EpssThreshold = EpssThreshold,
EpssThresholdWeight = EpssThresholdWeight,
PolicyFlipWeight = PolicyFlipWeight
};
/// <summary>
/// Create a detector configured with these options.
/// </summary>
public MaterialRiskChangeDetector CreateDetector() => new(ToDetectorOptions());
/// <summary>
/// Validate configuration values.
/// </summary>
public SmartDiffScoringConfigValidation Validate()
{
var errors = new List<string>();
// Weight validations (should be 0.0 - 2.0)
ValidateWeight(nameof(ReachabilityFlipUpWeight), ReachabilityFlipUpWeight, errors);
ValidateWeight(nameof(ReachabilityFlipDownWeight), ReachabilityFlipDownWeight, errors);
ValidateWeight(nameof(VexFlipToAffectedWeight), VexFlipToAffectedWeight, errors);
ValidateWeight(nameof(VexFlipToNotAffectedWeight), VexFlipToNotAffectedWeight, errors);
ValidateWeight(nameof(RangeEntryWeight), RangeEntryWeight, errors);
ValidateWeight(nameof(RangeExitWeight), RangeExitWeight, errors);
ValidateWeight(nameof(KevAddedWeight), KevAddedWeight, errors);
ValidateWeight(nameof(KevRemovedWeight), KevRemovedWeight, errors);
ValidateWeight(nameof(EpssThresholdWeight), EpssThresholdWeight, errors);
ValidateWeight(nameof(PolicyFlipWeight), PolicyFlipWeight, errors);
ValidateWeight(nameof(HardeningRegressionWeight), HardeningRegressionWeight, errors);
// Threshold validations (should be 0.0 - 1.0)
ValidateThreshold(nameof(EpssThreshold), EpssThreshold, errors);
ValidateThreshold(nameof(HardeningScoreThreshold), HardeningScoreThreshold, errors);
ValidateThreshold(nameof(MinPriorityScore), MinPriorityScore, errors);
ValidateThreshold(nameof(HighPriorityThreshold), HighPriorityThreshold, errors);
ValidateThreshold(nameof(CriticalPriorityThreshold), CriticalPriorityThreshold, errors);
// Logical validations
if (HighPriorityThreshold >= CriticalPriorityThreshold)
{
errors.Add($"HighPriorityThreshold ({HighPriorityThreshold}) must be less than CriticalPriorityThreshold ({CriticalPriorityThreshold})");
}
if (MinPriorityScore >= HighPriorityThreshold)
{
errors.Add($"MinPriorityScore ({MinPriorityScore}) should be less than HighPriorityThreshold ({HighPriorityThreshold})");
}
return new SmartDiffScoringConfigValidation(errors.Count == 0, [.. errors]);
}
private static void ValidateWeight(string name, double value, List<string> errors)
{
if (value < 0.0 || value > 2.0)
{
errors.Add($"{name} must be between 0.0 and 2.0, got {value}");
}
}
private static void ValidateThreshold(string name, double value, List<string> errors)
{
if (value < 0.0 || value > 1.0)
{
errors.Add($"{name} must be between 0.0 and 1.0, got {value}");
}
}
#endregion
}
/// <summary>
/// Result of scoring config validation.
/// </summary>
public sealed record SmartDiffScoringConfigValidation(
[property: JsonPropertyName("isValid")] bool IsValid,
[property: JsonPropertyName("errors")] string[] Errors);