sln build fix (again), tests fixes, audit work and doctors work
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
using System.Globalization;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using StellaOps.Doctor.Models;
|
||||
using StellaOps.Doctor.Plugins;
|
||||
|
||||
namespace StellaOps.Doctor.Plugins.Security.Checks;
|
||||
|
||||
/// <summary>
|
||||
/// Validates rate limiting configuration.
|
||||
/// </summary>
|
||||
public sealed class RateLimitingCheck : IDoctorCheck
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string CheckId => "check.security.ratelimit";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => "Rate Limiting";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Description => "Validates rate limiting is configured to prevent abuse";
|
||||
|
||||
/// <inheritdoc />
|
||||
public DoctorSeverity DefaultSeverity => DoctorSeverity.Warn;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<string> Tags => ["security", "ratelimit", "api"];
|
||||
|
||||
/// <inheritdoc />
|
||||
public TimeSpan EstimatedDuration => TimeSpan.FromMilliseconds(50);
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool CanRun(DoctorPluginContext context) => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<DoctorCheckResult> RunAsync(DoctorPluginContext context, CancellationToken ct)
|
||||
{
|
||||
var result = context.CreateResult(CheckId, "stellaops.doctor.security", DoctorCategory.Security.ToString());
|
||||
|
||||
var rateLimitEnabled = context.Configuration.GetValue<bool?>("RateLimiting:Enabled")
|
||||
?? context.Configuration.GetValue<bool?>("Security:RateLimiting:Enabled");
|
||||
|
||||
if (rateLimitEnabled == null)
|
||||
{
|
||||
return Task.FromResult(result
|
||||
.Info("Rate limiting configuration not found")
|
||||
.WithEvidence("Rate limiting", e =>
|
||||
{
|
||||
e.Add("Configured", "false");
|
||||
e.Add("Recommendation", "Consider enabling rate limiting for API protection");
|
||||
})
|
||||
.Build());
|
||||
}
|
||||
|
||||
if (rateLimitEnabled != true)
|
||||
{
|
||||
return Task.FromResult(result
|
||||
.Warn("Rate limiting is disabled")
|
||||
.WithEvidence("Rate limiting", e =>
|
||||
{
|
||||
e.Add("Enabled", "false");
|
||||
e.Add("Recommendation", "Enable rate limiting to prevent API abuse");
|
||||
})
|
||||
.WithCauses("Rate limiting explicitly disabled in configuration")
|
||||
.WithRemediation(r => r
|
||||
.AddManualStep(1, "Enable rate limiting", "Set RateLimiting:Enabled to true"))
|
||||
.WithVerification("stella doctor --check check.security.ratelimit")
|
||||
.Build());
|
||||
}
|
||||
|
||||
var permitLimit = context.Configuration.GetValue<int?>("RateLimiting:PermitLimit")
|
||||
?? context.Configuration.GetValue<int?>("Security:RateLimiting:PermitLimit")
|
||||
?? 100;
|
||||
|
||||
var windowSeconds = context.Configuration.GetValue<int?>("RateLimiting:WindowSeconds")
|
||||
?? context.Configuration.GetValue<int?>("Security:RateLimiting:WindowSeconds")
|
||||
?? 60;
|
||||
|
||||
var queueLimit = context.Configuration.GetValue<int?>("RateLimiting:QueueLimit")
|
||||
?? context.Configuration.GetValue<int?>("Security:RateLimiting:QueueLimit")
|
||||
?? 0;
|
||||
|
||||
var issues = new List<string>();
|
||||
|
||||
if (permitLimit > 10000)
|
||||
{
|
||||
issues.Add($"Rate limit permit count ({permitLimit}) is very high");
|
||||
}
|
||||
|
||||
if (windowSeconds < 1)
|
||||
{
|
||||
issues.Add("Rate limit window is less than 1 second");
|
||||
}
|
||||
else if (windowSeconds > 3600)
|
||||
{
|
||||
issues.Add($"Rate limit window ({windowSeconds}s) is very long - may not effectively prevent bursts");
|
||||
}
|
||||
|
||||
var requestsPerSecond = (double)permitLimit / windowSeconds;
|
||||
if (requestsPerSecond > 1000)
|
||||
{
|
||||
issues.Add($"Effective rate ({requestsPerSecond:F0} req/s) may be too permissive");
|
||||
}
|
||||
|
||||
if (issues.Count > 0)
|
||||
{
|
||||
return Task.FromResult(result
|
||||
.Warn($"{issues.Count} rate limiting configuration issue(s)")
|
||||
.WithEvidence("Rate limiting", e =>
|
||||
{
|
||||
e.Add("Enabled", "true");
|
||||
e.Add("PermitLimit", permitLimit.ToString(CultureInfo.InvariantCulture));
|
||||
e.Add("WindowSeconds", windowSeconds.ToString(CultureInfo.InvariantCulture));
|
||||
e.Add("QueueLimit", queueLimit.ToString(CultureInfo.InvariantCulture));
|
||||
e.Add("EffectiveRatePerSecond", requestsPerSecond.ToString("F2", CultureInfo.InvariantCulture));
|
||||
})
|
||||
.WithCauses(issues.ToArray())
|
||||
.Build());
|
||||
}
|
||||
|
||||
return Task.FromResult(result
|
||||
.Pass("Rate limiting is properly configured")
|
||||
.WithEvidence("Rate limiting", e =>
|
||||
{
|
||||
e.Add("Enabled", "true");
|
||||
e.Add("PermitLimit", permitLimit.ToString(CultureInfo.InvariantCulture));
|
||||
e.Add("WindowSeconds", windowSeconds.ToString(CultureInfo.InvariantCulture));
|
||||
e.Add("QueueLimit", queueLimit.ToString(CultureInfo.InvariantCulture));
|
||||
e.Add("EffectiveRatePerSecond", requestsPerSecond.ToString("F2", CultureInfo.InvariantCulture));
|
||||
})
|
||||
.Build());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user