feat(audit): Apply TreatWarningsAsErrors=true to 160+ production csproj files
Sprint: SPRINT_20251229_049_BE_csproj_audit_maint_tests Tasks: AUDIT-0001 through AUDIT-0147 APPLY tasks (approved decisions 1-9) Changes: - Set TreatWarningsAsErrors=true for all production .NET projects - Fixed nullable warnings in Scanner.EntryTrace, Scanner.Evidence, Scheduler.Worker, Concelier connectors, and other modules - Injected TimeProvider/IGuidProvider for deterministic time/ID generation - Added path traversal validation in AirGap.Bundle - Fixed NULL handling in various cursor classes - Third-party GostCryptography retains TreatWarningsAsErrors=false (preserves original) - Test projects excluded per user decision (rejected decision 10) Note: All 17 ACSC connector tests pass after snapshot fixture sync
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using StellaOps.AdvisoryAI.Orchestration;
|
||||
|
||||
namespace StellaOps.AdvisoryAI.WebService.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Consolidated authorization service for advisory-ai endpoints.
|
||||
/// Provides consistent scope-based authorization checks.
|
||||
/// </summary>
|
||||
public interface IAuthorizationService
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if the request is authorized for the given task type.
|
||||
/// </summary>
|
||||
bool IsAuthorized(HttpContext context, AdvisoryTaskType taskType);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the request is authorized for explanation operations.
|
||||
/// </summary>
|
||||
bool IsExplainAuthorized(HttpContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the request is authorized for remediation operations.
|
||||
/// </summary>
|
||||
bool IsRemediationAuthorized(HttpContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the request is authorized for policy studio operations.
|
||||
/// </summary>
|
||||
bool IsPolicyAuthorized(HttpContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the request is authorized for justification operations.
|
||||
/// </summary>
|
||||
bool IsJustifyAuthorized(HttpContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tenant ID from the request headers.
|
||||
/// </summary>
|
||||
string GetTenantId(HttpContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user ID from the request headers.
|
||||
/// </summary>
|
||||
string GetUserId(HttpContext context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of authorization service using header-based scopes.
|
||||
/// </summary>
|
||||
public sealed class HeaderBasedAuthorizationService : IAuthorizationService
|
||||
{
|
||||
private const string ScopesHeader = "X-StellaOps-Scopes";
|
||||
private const string TenantHeader = "X-StellaOps-Tenant";
|
||||
private const string UserHeader = "X-StellaOps-User";
|
||||
|
||||
public bool IsAuthorized(HttpContext context, AdvisoryTaskType taskType)
|
||||
{
|
||||
var scopes = GetScopes(context);
|
||||
if (scopes.Contains("advisory:run"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return scopes.Contains($"advisory:{taskType.ToString().ToLowerInvariant()}");
|
||||
}
|
||||
|
||||
public bool IsExplainAuthorized(HttpContext context)
|
||||
{
|
||||
var scopes = GetScopes(context);
|
||||
return scopes.Contains("advisory:run") || scopes.Contains("advisory:explain");
|
||||
}
|
||||
|
||||
public bool IsRemediationAuthorized(HttpContext context)
|
||||
{
|
||||
var scopes = GetScopes(context);
|
||||
return scopes.Contains("advisory:run") || scopes.Contains("advisory:remediate");
|
||||
}
|
||||
|
||||
public bool IsPolicyAuthorized(HttpContext context)
|
||||
{
|
||||
var scopes = GetScopes(context);
|
||||
return scopes.Contains("advisory:run") || scopes.Contains("policy:write");
|
||||
}
|
||||
|
||||
public bool IsJustifyAuthorized(HttpContext context)
|
||||
{
|
||||
var scopes = GetScopes(context);
|
||||
return scopes.Contains("advisory:run") || scopes.Contains("advisory:justify");
|
||||
}
|
||||
|
||||
public string GetTenantId(HttpContext context)
|
||||
{
|
||||
return context.Request.Headers.TryGetValue(TenantHeader, out var value)
|
||||
? value.ToString()
|
||||
: "default";
|
||||
}
|
||||
|
||||
public string GetUserId(HttpContext context)
|
||||
{
|
||||
return context.Request.Headers.TryGetValue(UserHeader, out var value)
|
||||
? value.ToString()
|
||||
: "anonymous";
|
||||
}
|
||||
|
||||
private static HashSet<string> GetScopes(HttpContext context)
|
||||
{
|
||||
if (!context.Request.Headers.TryGetValue(ScopesHeader, out var scopes))
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
return scopes
|
||||
.SelectMany(value => value?.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) ?? [])
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace StellaOps.AdvisoryAI.WebService.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Configuration for feature-specific rate limits.
|
||||
/// </summary>
|
||||
public sealed class RateLimitsOptions
|
||||
{
|
||||
public const string SectionName = "AdvisoryAI:RateLimits";
|
||||
|
||||
/// <summary>
|
||||
/// Rate limit for the explain feature.
|
||||
/// </summary>
|
||||
public FeatureRateLimitOptions Explain { get; set; } = new() { Limit = 10, PeriodMinutes = 1 };
|
||||
|
||||
/// <summary>
|
||||
/// Rate limit for the remediate feature.
|
||||
/// </summary>
|
||||
public FeatureRateLimitOptions Remediate { get; set; } = new() { Limit = 5, PeriodMinutes = 1 };
|
||||
|
||||
/// <summary>
|
||||
/// Rate limit for the justify feature.
|
||||
/// </summary>
|
||||
public FeatureRateLimitOptions Justify { get; set; } = new() { Limit = 3, PeriodMinutes = 1 };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rate limit configuration for a single feature.
|
||||
/// </summary>
|
||||
public sealed class FeatureRateLimitOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Maximum number of requests allowed per period.
|
||||
/// </summary>
|
||||
public int Limit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Period duration in minutes.
|
||||
/// </summary>
|
||||
public int PeriodMinutes { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents rate limit information for a feature.
|
||||
/// </summary>
|
||||
public sealed class RateLimitInfo
|
||||
{
|
||||
public required string Feature { get; init; }
|
||||
public required int Limit { get; init; }
|
||||
public required int Remaining { get; init; }
|
||||
public required DateTimeOffset ResetsAt { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Service for managing rate limit state and reporting.
|
||||
/// </summary>
|
||||
public interface IRateLimitsService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the current rate limit information for all features.
|
||||
/// </summary>
|
||||
IReadOnlyList<RateLimitInfo> GetRateLimits(TimeProvider timeProvider);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of rate limits service using configuration.
|
||||
/// In production, this would integrate with the actual rate limiter state.
|
||||
/// </summary>
|
||||
public sealed class ConfigDrivenRateLimitsService : IRateLimitsService
|
||||
{
|
||||
private readonly RateLimitsOptions _options;
|
||||
|
||||
public ConfigDrivenRateLimitsService(IOptions<RateLimitsOptions> options)
|
||||
{
|
||||
_options = options.Value;
|
||||
}
|
||||
|
||||
public IReadOnlyList<RateLimitInfo> GetRateLimits(TimeProvider timeProvider)
|
||||
{
|
||||
var now = timeProvider.GetUtcNow();
|
||||
|
||||
return
|
||||
[
|
||||
new RateLimitInfo
|
||||
{
|
||||
Feature = "explain",
|
||||
Limit = _options.Explain.Limit,
|
||||
Remaining = _options.Explain.Limit, // Would integrate with actual limiter state
|
||||
ResetsAt = now.AddMinutes(_options.Explain.PeriodMinutes)
|
||||
},
|
||||
new RateLimitInfo
|
||||
{
|
||||
Feature = "remediate",
|
||||
Limit = _options.Remediate.Limit,
|
||||
Remaining = _options.Remediate.Limit, // Would integrate with actual limiter state
|
||||
ResetsAt = now.AddMinutes(_options.Remediate.PeriodMinutes)
|
||||
},
|
||||
new RateLimitInfo
|
||||
{
|
||||
Feature = "justify",
|
||||
Limit = _options.Justify.Limit,
|
||||
Remaining = _options.Justify.Limit, // Would integrate with actual limiter state
|
||||
ResetsAt = now.AddMinutes(_options.Justify.PeriodMinutes)
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user