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,364 @@
// -----------------------------------------------------------------------------
// UnknownRankerTests.cs
// Sprint: SPRINT_3600_0002_0001_unknowns_ranking_containment
// Task: UNK-RANK-009 - Unit tests for ranking function
// Description: Tests for unknown ranking determinism and edge cases
// -----------------------------------------------------------------------------
using FluentAssertions;
using StellaOps.Unknowns.Core.Models;
using StellaOps.Unknowns.Core.Services;
using Xunit;
namespace StellaOps.Unknowns.Core.Tests.Services;
/// <summary>
/// Unit tests for UnknownRanker.
/// </summary>
public class UnknownRankerTests
{
private readonly UnknownRanker _ranker = new();
#region Basic Ranking Tests
[Fact]
public void Rank_HighBlastHighPressure_ReturnsHighScore()
{
// Arrange
var blast = new BlastRadius(100, NetFacing: true, Privilege: "root");
var pressure = new ExploitPressure(0.90, Kev: true);
var containment = ContainmentSignals.Unknown;
// Act
var score = _ranker.Rank(blast, scarcity: 0.8, pressure, containment);
// Assert - should be very high (close to 1.0)
score.Should().BeGreaterOrEqualTo(0.8);
}
[Fact]
public void Rank_LowBlastLowPressure_ReturnsLowScore()
{
// Arrange
var blast = new BlastRadius(1, NetFacing: false, Privilege: "none");
var pressure = new ExploitPressure(0.01, Kev: false);
var containment = ContainmentSignals.Unknown;
// Act
var score = _ranker.Rank(blast, scarcity: 0.1, pressure, containment);
// Assert - should be low
score.Should().BeLessThan(0.3);
}
[Fact]
public void Rank_WithContainment_ReducesScore()
{
// Arrange
var blast = new BlastRadius(50, NetFacing: true, Privilege: "user");
var pressure = new ExploitPressure(0.5, Kev: false);
var noContainment = ContainmentSignals.Unknown;
var wellContained = ContainmentSignals.WellSandboxed;
// Act
var scoreNoContainment = _ranker.Rank(blast, scarcity: 0.5, pressure, noContainment);
var scoreWellContained = _ranker.Rank(blast, scarcity: 0.5, pressure, wellContained);
// Assert - containment should reduce score
scoreWellContained.Should().BeLessThan(scoreNoContainment);
(scoreNoContainment - scoreWellContained).Should().BeGreaterOrEqualTo(0.15); // At least 0.15 reduction
}
#endregion
#region Containment Signal Tests
[Fact]
public void ContainmentSignals_SeccompEnforced_ProvidesDeduction()
{
// Arrange
var containment = new ContainmentSignals("enforced", "rw");
// Act
var deduction = containment.Deduction();
// Assert
deduction.Should().BeApproximately(0.10, 0.001);
}
[Fact]
public void ContainmentSignals_ReadOnlyFs_ProvidesDeduction()
{
// Arrange
var containment = new ContainmentSignals("disabled", "ro");
// Act
var deduction = containment.Deduction();
// Assert
deduction.Should().BeApproximately(0.10, 0.001);
}
[Fact]
public void ContainmentSignals_WellSandboxed_ProvidesMaxDeduction()
{
// Arrange
var containment = ContainmentSignals.WellSandboxed; // seccomp=enforced, fs=ro, netpol=enforced, caps=20
// Act
var deduction = containment.Deduction();
// Assert - should be significant
deduction.Should().BeGreaterOrEqualTo(0.25);
deduction.Should().BeLessOrEqualTo(0.30);
}
[Fact]
public void ContainmentSignals_Unknown_ProvidesNoDeduction()
{
// Arrange
var containment = ContainmentSignals.Unknown;
// Act
var deduction = containment.Deduction();
// Assert
deduction.Should().Be(0);
}
#endregion
#region Blast Radius Tests
[Fact]
public void BlastRadius_HighDependents_IncreasesScore()
{
// Arrange
var lowDeps = new BlastRadius(5, NetFacing: false, Privilege: "none");
var highDeps = new BlastRadius(100, NetFacing: false, Privilege: "none");
// Act
var lowScore = lowDeps.Score();
var highScore = highDeps.Score();
// Assert
highScore.Should().BeGreaterThan(lowScore);
}
[Fact]
public void BlastRadius_NetFacing_IncreasesScore()
{
// Arrange
var notNetFacing = new BlastRadius(10, NetFacing: false, Privilege: "none");
var netFacing = new BlastRadius(10, NetFacing: true, Privilege: "none");
// Act
var notNetScore = notNetFacing.Score();
var netScore = netFacing.Score();
// Assert
netScore.Should().BeGreaterThan(notNetScore);
(netScore - notNetScore).Should().BeApproximately(0.25, 0.01); // 0.5 / 2 = 0.25
}
[Fact]
public void BlastRadius_RootPrivilege_IncreasesScore()
{
// Arrange
var userPriv = new BlastRadius(10, NetFacing: false, Privilege: "user");
var rootPriv = new BlastRadius(10, NetFacing: false, Privilege: "root");
// Act
var userScore = userPriv.Score();
var rootScore = rootPriv.Score();
// Assert
rootScore.Should().BeGreaterThan(userScore);
}
#endregion
#region Exploit Pressure Tests
[Fact]
public void ExploitPressure_HighEpss_IncreasesScore()
{
// Arrange
var lowEpss = new ExploitPressure(0.01, Kev: false);
var highEpss = new ExploitPressure(0.90, Kev: false);
// Act
var lowScore = lowEpss.Score();
var highScore = highEpss.Score();
// Assert
highScore.Should().BeGreaterThan(lowScore);
}
[Fact]
public void ExploitPressure_Kev_IncreasesScore()
{
// Arrange
var noKev = new ExploitPressure(0.5, Kev: false);
var withKev = new ExploitPressure(0.5, Kev: true);
// Act
var noKevScore = noKev.Score();
var withKevScore = withKev.Score();
// Assert
withKevScore.Should().BeGreaterThan(noKevScore);
(withKevScore - noKevScore).Should().BeApproximately(0.30, 0.001);
}
[Fact]
public void ExploitPressure_NullEpss_UsesDefault()
{
// Arrange
var unknownEpss = ExploitPressure.Unknown;
// Act
var score = unknownEpss.Score();
// Assert - should use 0.35 default
score.Should().BeApproximately(0.35, 0.01);
}
#endregion
#region Determinism Tests
[Fact]
public void Rank_SameInputs_ReturnsSameScore()
{
// Arrange
var blast = new BlastRadius(42, NetFacing: true, Privilege: "user");
var pressure = new ExploitPressure(0.67, Kev: true);
var containment = new ContainmentSignals("enforced", "ro");
// Act - rank multiple times
var score1 = _ranker.Rank(blast, scarcity: 0.55, pressure, containment);
var score2 = _ranker.Rank(blast, scarcity: 0.55, pressure, containment);
var score3 = _ranker.Rank(blast, scarcity: 0.55, pressure, containment);
// Assert - all scores should be identical
score1.Should().Be(score2);
score2.Should().Be(score3);
}
[Fact]
public void Rank_SlightlyDifferentInputs_ReturnsDifferentScores()
{
// Arrange
var blast1 = new BlastRadius(42, NetFacing: true, Privilege: "user");
var blast2 = new BlastRadius(43, NetFacing: true, Privilege: "user"); // Just 1 more dependent
var pressure = new ExploitPressure(0.67, Kev: false);
var containment = ContainmentSignals.Unknown;
// Act
var score1 = _ranker.Rank(blast1, scarcity: 0.55, pressure, containment);
var score2 = _ranker.Rank(blast2, scarcity: 0.55, pressure, containment);
// Assert - scores should be different
score1.Should().NotBe(score2);
}
#endregion
#region Boundary Tests
[Fact]
public void Rank_AlwaysReturnsScoreInRange()
{
// Test many combinations to ensure score is always [0, 1]
var testCases = new[]
{
(new BlastRadius(0, false, "none"), 0.0, new ExploitPressure(0, false), ContainmentSignals.Unknown),
(new BlastRadius(1000, true, "root"), 1.0, new ExploitPressure(1.0, true), ContainmentSignals.Unknown),
(new BlastRadius(50, true, "root"), 0.5, new ExploitPressure(0.5, true), ContainmentSignals.WellSandboxed),
};
foreach (var (blast, scarcity, pressure, containment) in testCases)
{
var score = _ranker.Rank(blast, scarcity, pressure, containment);
score.Should().BeInRange(0, 1);
}
}
[Fact]
public void Rank_NegativeValues_ClampedToZero()
{
// Arrange - minimal risk with high containment
var blast = new BlastRadius(0, NetFacing: false, Privilege: "none");
var pressure = new ExploitPressure(0, Kev: false);
var containment = ContainmentSignals.WellSandboxed;
// Act
var score = _ranker.Rank(blast, scarcity: 0, pressure, containment);
// Assert - should be clamped to 0, not negative
score.Should().BeGreaterOrEqualTo(0);
}
#endregion
#region Triage Band Tests
[Theory]
[InlineData(0.9, "Hot")]
[InlineData(0.7, "Hot")]
[InlineData(0.5, "Warm")]
[InlineData(0.4, "Warm")]
[InlineData(0.3, "Cold")]
[InlineData(0.1, "Cold")]
public void ToTriageBand_ReturnsCorrectBand(double score, string expected)
{
// Act
var band = score.ToTriageBand();
// Assert
band.ToString().Should().Be(expected);
}
[Theory]
[InlineData(0.9, "Critical")]
[InlineData(0.8, "Critical")]
[InlineData(0.7, "High")]
[InlineData(0.6, "High")]
[InlineData(0.5, "Medium")]
[InlineData(0.3, "Low")]
[InlineData(0.1, "Info")]
public void ToPriorityLabel_ReturnsCorrectLabel(double score, string expected)
{
// Act
var label = score.ToPriorityLabel();
// Assert
label.Should().Be(expected);
}
#endregion
#region Custom Weights Tests
[Fact]
public void Rank_WithExploitFocusedWeights_PrioritizesExploitPressure()
{
// Arrange
var rankerDefault = new UnknownRanker(RankingWeights.Default);
var rankerExploitFocused = new UnknownRanker(RankingWeights.ExploitFocused);
var blast = new BlastRadius(10, NetFacing: false, Privilege: "none"); // Low blast
var pressure = new ExploitPressure(0.95, Kev: true); // High pressure
var containment = ContainmentSignals.Unknown;
// Act
var scoreDefault = rankerDefault.Rank(blast, scarcity: 0.3, pressure, containment);
var scoreExploitFocused = rankerExploitFocused.Rank(blast, scarcity: 0.3, pressure, containment);
// Assert - exploit-focused should rank this higher
scoreExploitFocused.Should().BeGreaterThan(scoreDefault);
}
#endregion
}