Add comprehensive security tests for OWASP A02, A05, A07, and A08 categories
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Findings Ledger CI / build-test (push) Has been cancelled
Findings Ledger CI / migration-validation (push) Has been cancelled
Findings Ledger CI / generate-manifest (push) Has been cancelled
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Lighthouse CI / Lighthouse Audit (push) Has been cancelled
Lighthouse CI / Axe Accessibility Audit (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Policy Simulation / policy-simulate (push) Has been cancelled

- Implemented tests for Cryptographic Failures (A02) to ensure proper handling of sensitive data, secure algorithms, and key management.
- Added tests for Security Misconfiguration (A05) to validate production configurations, security headers, CORS settings, and feature management.
- Developed tests for Authentication Failures (A07) to enforce strong password policies, rate limiting, session management, and MFA support.
- Created tests for Software and Data Integrity Failures (A08) to verify artifact signatures, SBOM integrity, attestation chains, and feed updates.
This commit is contained in:
master
2025-12-16 16:40:19 +02:00
parent 415eff1207
commit 2170a58734
206 changed files with 30547 additions and 534 deletions

View File

@@ -0,0 +1,208 @@
// -----------------------------------------------------------------------------
// FidelityMetricsTelemetry.cs
// Sprint: SPRINT_3403_0001_0001_fidelity_metrics
// Task: FID-3403-008
// Description: Prometheus gauges for Bitwise, Semantic, and Policy fidelity metrics
// -----------------------------------------------------------------------------
using System.Diagnostics.Metrics;
namespace StellaOps.Telemetry.Core;
/// <summary>
/// Prometheus gauges for fidelity metrics (BF, SF, PF).
/// </summary>
public sealed class FidelityMetricsTelemetry : IDisposable
{
/// <summary>
/// Meter name for fidelity metrics.
/// </summary>
public const string MeterName = "StellaOps.Fidelity";
private readonly Meter _meter;
private readonly object _lock = new();
private bool _disposed;
// Latest fidelity values per (tenant, surface)
private readonly Dictionary<string, FidelitySnapshot> _snapshots = new();
// Observable gauges
private readonly ObservableGauge<double> _bitwiseFidelityGauge;
private readonly ObservableGauge<double> _semanticFidelityGauge;
private readonly ObservableGauge<double> _policyFidelityGauge;
private readonly ObservableGauge<int> _totalReplaysGauge;
// Counters for SLO tracking
private readonly Counter<long> _sloBreachCounter;
/// <summary>
/// Initializes a new instance of <see cref="FidelityMetricsTelemetry"/>.
/// </summary>
public FidelityMetricsTelemetry(FidelityTelemetryOptions? options = null)
{
var opts = options ?? new FidelityTelemetryOptions();
_meter = new Meter(MeterName, opts.Version);
_bitwiseFidelityGauge = _meter.CreateObservableGauge(
name: "fidelity_bitwise_ratio",
observeValue: () => ObserveMetric(s => s.BitwiseFidelity),
unit: "{ratio}",
description: "Bitwise fidelity ratio (identical_outputs / total_replays).");
_semanticFidelityGauge = _meter.CreateObservableGauge(
name: "fidelity_semantic_ratio",
observeValue: () => ObserveMetric(s => s.SemanticFidelity),
unit: "{ratio}",
description: "Semantic fidelity ratio (semantically equivalent outputs / total).");
_policyFidelityGauge = _meter.CreateObservableGauge(
name: "fidelity_policy_ratio",
observeValue: () => ObserveMetric(s => s.PolicyFidelity),
unit: "{ratio}",
description: "Policy fidelity ratio (matching policy decisions / total).");
_totalReplaysGauge = _meter.CreateObservableGauge(
name: "fidelity_total_replays",
observeValue: () => ObserveMetric(s => s.TotalReplays),
unit: "{replays}",
description: "Total number of replay runs measured.");
_sloBreachCounter = _meter.CreateCounter<long>(
name: "fidelity_slo_breach_total",
unit: "{breach}",
description: "Total number of fidelity SLO breaches.");
}
/// <summary>
/// Records a fidelity snapshot for a tenant/surface.
/// </summary>
public void RecordSnapshot(
double bitwiseFidelity,
double semanticFidelity,
double policyFidelity,
int totalReplays,
string? tenantId = null,
string? surfaceId = null)
{
var key = BuildKey(tenantId, surfaceId);
var snapshot = new FidelitySnapshot
{
BitwiseFidelity = bitwiseFidelity,
SemanticFidelity = semanticFidelity,
PolicyFidelity = policyFidelity,
TotalReplays = totalReplays,
TenantId = tenantId,
SurfaceId = surfaceId,
RecordedAt = DateTimeOffset.UtcNow
};
lock (_lock)
{
_snapshots[key] = snapshot;
}
}
/// <summary>
/// Records an SLO breach for fidelity metrics.
/// </summary>
public void RecordSloBreachDirect(
FidelityBreachType breachType,
double actualValue,
double thresholdValue,
string? tenantId = null,
string? surfaceId = null)
{
var tags = new TagList
{
{ "breach_type", breachType.ToString().ToLowerInvariant() },
{ "actual_value", actualValue },
{ "threshold_value", thresholdValue }
};
if (!string.IsNullOrEmpty(tenantId)) tags.Add("tenant_id", tenantId);
if (!string.IsNullOrEmpty(surfaceId)) tags.Add("surface_id", surfaceId);
_sloBreachCounter.Add(1, tags);
}
private IEnumerable<Measurement<double>> ObserveMetric(Func<FidelitySnapshot, double> selector)
{
lock (_lock)
{
foreach (var snapshot in _snapshots.Values)
{
var tags = new KeyValuePair<string, object?>[]
{
new("tenant_id", snapshot.TenantId ?? ""),
new("surface_id", snapshot.SurfaceId ?? "")
};
yield return new Measurement<double>(selector(snapshot), tags);
}
}
}
private IEnumerable<Measurement<int>> ObserveMetric(Func<FidelitySnapshot, int> selector)
{
lock (_lock)
{
foreach (var snapshot in _snapshots.Values)
{
var tags = new KeyValuePair<string, object?>[]
{
new("tenant_id", snapshot.TenantId ?? ""),
new("surface_id", snapshot.SurfaceId ?? "")
};
yield return new Measurement<int>(selector(snapshot), tags);
}
}
}
private static string BuildKey(string? tenantId, string? surfaceId)
{
return $"{tenantId ?? ""}|{surfaceId ?? ""}";
}
/// <inheritdoc/>
public void Dispose()
{
if (_disposed) return;
_disposed = true;
_meter.Dispose();
}
private sealed record FidelitySnapshot
{
public required double BitwiseFidelity { get; init; }
public required double SemanticFidelity { get; init; }
public required double PolicyFidelity { get; init; }
public required int TotalReplays { get; init; }
public string? TenantId { get; init; }
public string? SurfaceId { get; init; }
public DateTimeOffset RecordedAt { get; init; }
}
}
/// <summary>
/// Options for fidelity telemetry.
/// </summary>
public sealed class FidelityTelemetryOptions
{
/// <summary>
/// Metric version.
/// </summary>
public string Version { get; init; } = "1.0.0";
}
/// <summary>
/// Type of fidelity SLO breach.
/// </summary>
public enum FidelityBreachType
{
/// <summary>Bitwise fidelity below threshold</summary>
Bitwise,
/// <summary>Semantic fidelity below threshold</summary>
Semantic,
/// <summary>Policy fidelity below threshold</summary>
Policy
}

View File

@@ -0,0 +1,208 @@
// -----------------------------------------------------------------------------
// FidelitySloAlertingService.cs
// Sprint: SPRINT_3403_0001_0001_fidelity_metrics
// Task: FID-3403-009
// Description: SLO alerting for fidelity thresholds
// -----------------------------------------------------------------------------
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace StellaOps.Telemetry.Core;
/// <summary>
/// SLO alerting service for fidelity metrics.
/// Checks fidelity scores against thresholds and records breaches.
/// </summary>
public sealed class FidelitySloAlertingService
{
private readonly FidelityMetricsTelemetry _telemetry;
private readonly FidelitySloOptions _options;
private readonly ILogger<FidelitySloAlertingService> _logger;
public FidelitySloAlertingService(
FidelityMetricsTelemetry telemetry,
IOptions<FidelitySloOptions> options,
ILogger<FidelitySloAlertingService> logger)
{
_telemetry = telemetry ?? throw new ArgumentNullException(nameof(telemetry));
_options = options?.Value ?? new FidelitySloOptions();
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
/// <summary>
/// Evaluate fidelity metrics against SLO thresholds.
/// </summary>
public FidelitySloResult Evaluate(
double bitwiseFidelity,
double semanticFidelity,
double policyFidelity,
int totalReplays,
string? tenantId = null,
string? surfaceId = null)
{
var breaches = new List<FidelitySloBreachInfo>();
// Record the snapshot
_telemetry.RecordSnapshot(
bitwiseFidelity,
semanticFidelity,
policyFidelity,
totalReplays,
tenantId,
surfaceId);
// Check bitwise fidelity
if (bitwiseFidelity < _options.BitwiseFidelityThreshold)
{
var breach = new FidelitySloBreachInfo
{
BreachType = FidelityBreachType.Bitwise,
ActualValue = bitwiseFidelity,
ThresholdValue = _options.BitwiseFidelityThreshold,
Severity = GetSeverity(bitwiseFidelity, _options.BitwiseFidelityThreshold, _options.BitwiseFidelityCritical)
};
breaches.Add(breach);
_telemetry.RecordSloBreachDirect(
FidelityBreachType.Bitwise,
bitwiseFidelity,
_options.BitwiseFidelityThreshold,
tenantId,
surfaceId);
_logger.LogWarning(
"Bitwise fidelity SLO breach: {Actual:P2} < {Threshold:P2} (tenant={Tenant})",
bitwiseFidelity, _options.BitwiseFidelityThreshold, tenantId ?? "global");
}
// Check semantic fidelity
if (semanticFidelity < _options.SemanticFidelityThreshold)
{
var breach = new FidelitySloBreachInfo
{
BreachType = FidelityBreachType.Semantic,
ActualValue = semanticFidelity,
ThresholdValue = _options.SemanticFidelityThreshold,
Severity = GetSeverity(semanticFidelity, _options.SemanticFidelityThreshold, _options.SemanticFidelityCritical)
};
breaches.Add(breach);
_telemetry.RecordSloBreachDirect(
FidelityBreachType.Semantic,
semanticFidelity,
_options.SemanticFidelityThreshold,
tenantId,
surfaceId);
_logger.LogWarning(
"Semantic fidelity SLO breach: {Actual:P2} < {Threshold:P2} (tenant={Tenant})",
semanticFidelity, _options.SemanticFidelityThreshold, tenantId ?? "global");
}
// Check policy fidelity
if (policyFidelity < _options.PolicyFidelityThreshold)
{
var breach = new FidelitySloBreachInfo
{
BreachType = FidelityBreachType.Policy,
ActualValue = policyFidelity,
ThresholdValue = _options.PolicyFidelityThreshold,
Severity = GetSeverity(policyFidelity, _options.PolicyFidelityThreshold, _options.PolicyFidelityCritical)
};
breaches.Add(breach);
_telemetry.RecordSloBreachDirect(
FidelityBreachType.Policy,
policyFidelity,
_options.PolicyFidelityThreshold,
tenantId,
surfaceId);
_logger.LogError(
"Policy fidelity SLO breach: {Actual:P2} < {Threshold:P2} (tenant={Tenant})",
policyFidelity, _options.PolicyFidelityThreshold, tenantId ?? "global");
}
return new FidelitySloResult
{
Passed = breaches.Count == 0,
Breaches = breaches,
EvaluatedAt = DateTimeOffset.UtcNow
};
}
private static FidelityBreachSeverity GetSeverity(double actual, double warning, double critical)
{
if (actual < critical) return FidelityBreachSeverity.Critical;
if (actual < warning) return FidelityBreachSeverity.Warning;
return FidelityBreachSeverity.None;
}
}
/// <summary>
/// Options for fidelity SLO thresholds.
/// </summary>
public sealed class FidelitySloOptions
{
/// <summary>
/// Bitwise fidelity warning threshold.
/// </summary>
public double BitwiseFidelityThreshold { get; init; } = 0.98;
/// <summary>
/// Bitwise fidelity critical threshold.
/// </summary>
public double BitwiseFidelityCritical { get; init; } = 0.90;
/// <summary>
/// Semantic fidelity warning threshold.
/// </summary>
public double SemanticFidelityThreshold { get; init; } = 0.99;
/// <summary>
/// Semantic fidelity critical threshold.
/// </summary>
public double SemanticFidelityCritical { get; init; } = 0.95;
/// <summary>
/// Policy fidelity warning threshold.
/// </summary>
public double PolicyFidelityThreshold { get; init; } = 1.0;
/// <summary>
/// Policy fidelity critical threshold.
/// </summary>
public double PolicyFidelityCritical { get; init; } = 0.99;
}
/// <summary>
/// Result of fidelity SLO evaluation.
/// </summary>
public sealed record FidelitySloResult
{
public required bool Passed { get; init; }
public required IReadOnlyList<FidelitySloBreachInfo> Breaches { get; init; }
public DateTimeOffset EvaluatedAt { get; init; }
}
/// <summary>
/// Information about a specific fidelity SLO breach.
/// </summary>
public sealed record FidelitySloBreachInfo
{
public required FidelityBreachType BreachType { get; init; }
public required double ActualValue { get; init; }
public required double ThresholdValue { get; init; }
public FidelityBreachSeverity Severity { get; init; }
}
/// <summary>
/// Severity level for fidelity breaches.
/// </summary>
public enum FidelityBreachSeverity
{
None,
Warning,
Critical
}

View File

@@ -0,0 +1,222 @@
// -----------------------------------------------------------------------------
// TtePercentileExporter.cs
// Sprint: SPRINT_3406_0001_0001_metrics_tables
// Task: METRICS-3406-010
// Description: Exports TTE percentiles to Prometheus via OpenTelemetry
// -----------------------------------------------------------------------------
using System.Diagnostics.Metrics;
namespace StellaOps.Telemetry.Core;
/// <summary>
/// Exports Time-to-Evidence (TTE) percentiles to Prometheus.
/// Provides p50, p90, p99 latency metrics for each TTE phase.
/// </summary>
public sealed class TtePercentileExporter : IDisposable
{
/// <summary>
/// Meter name for TTE percentile metrics.
/// </summary>
public const string MeterName = "StellaOps.TimeToEvidence.Percentiles";
private readonly Meter _meter;
private readonly object _lock = new();
private bool _disposed;
// Rolling window data per phase (tenant, surface)
private readonly Dictionary<string, LatencyWindow> _windows = new();
private readonly int _windowSizeSeconds;
private readonly int _maxSamplesPerWindow;
// Observable gauges for percentiles
private readonly ObservableGauge<double> _p50Gauge;
private readonly ObservableGauge<double> _p90Gauge;
private readonly ObservableGauge<double> _p99Gauge;
private readonly ObservableGauge<double> _maxGauge;
/// <summary>
/// Initializes a new instance of <see cref="TtePercentileExporter"/>.
/// </summary>
public TtePercentileExporter(TtePercentileOptions? options = null)
{
var opts = options ?? new TtePercentileOptions();
_windowSizeSeconds = opts.WindowSizeSeconds;
_maxSamplesPerWindow = opts.MaxSamplesPerWindow;
_meter = new Meter(MeterName, opts.Version);
_p50Gauge = _meter.CreateObservableGauge(
name: "tte_latency_p50_seconds",
observeValue: () => ObservePercentile(0.50),
unit: "s",
description: "50th percentile (median) TTE latency in seconds.");
_p90Gauge = _meter.CreateObservableGauge(
name: "tte_latency_p90_seconds",
observeValue: () => ObservePercentile(0.90),
unit: "s",
description: "90th percentile TTE latency in seconds.");
_p99Gauge = _meter.CreateObservableGauge(
name: "tte_latency_p99_seconds",
observeValue: () => ObservePercentile(0.99),
unit: "s",
description: "99th percentile TTE latency in seconds.");
_maxGauge = _meter.CreateObservableGauge(
name: "tte_latency_max_seconds",
observeValue: () => ObservePercentile(1.0),
unit: "s",
description: "Maximum TTE latency in seconds.");
}
/// <summary>
/// Record a latency sample for a TTE phase.
/// </summary>
public void RecordLatency(TtePhase phase, double latencySeconds, string? tenantId = null, string? surface = null)
{
var key = BuildKey(phase, tenantId, surface);
lock (_lock)
{
if (!_windows.TryGetValue(key, out var window))
{
window = new LatencyWindow(_windowSizeSeconds, _maxSamplesPerWindow);
_windows[key] = window;
}
window.Add(latencySeconds, DateTimeOffset.UtcNow);
}
}
/// <summary>
/// Get a specific percentile for a phase.
/// </summary>
public double? GetPercentile(TtePhase phase, double percentile, string? tenantId = null, string? surface = null)
{
var key = BuildKey(phase, tenantId, surface);
lock (_lock)
{
if (!_windows.TryGetValue(key, out var window))
{
return null;
}
return window.GetPercentile(percentile);
}
}
private IEnumerable<Measurement<double>> ObservePercentile(double percentile)
{
lock (_lock)
{
foreach (var (key, window) in _windows)
{
var value = window.GetPercentile(percentile);
if (value.HasValue)
{
var (phase, tenantId, surface) = ParseKey(key);
var tags = new KeyValuePair<string, object?>[]
{
new("phase", phase),
new("tenant_id", tenantId ?? ""),
new("surface", surface ?? "")
};
yield return new Measurement<double>(value.Value, tags);
}
}
}
}
private static string BuildKey(TtePhase phase, string? tenantId, string? surface)
{
return $"{phase}|{tenantId ?? ""}|{surface ?? ""}";
}
private static (string phase, string? tenantId, string? surface) ParseKey(string key)
{
var parts = key.Split('|');
return (
parts[0],
string.IsNullOrEmpty(parts[1]) ? null : parts[1],
string.IsNullOrEmpty(parts[2]) ? null : parts[2]
);
}
/// <inheritdoc/>
public void Dispose()
{
if (_disposed) return;
_disposed = true;
_meter.Dispose();
}
/// <summary>
/// Rolling window for latency samples.
/// </summary>
private sealed class LatencyWindow
{
private readonly int _windowSizeSeconds;
private readonly int _maxSamples;
private readonly List<(double Latency, DateTimeOffset Timestamp)> _samples = new();
public LatencyWindow(int windowSizeSeconds, int maxSamples)
{
_windowSizeSeconds = windowSizeSeconds;
_maxSamples = maxSamples;
}
public void Add(double latency, DateTimeOffset timestamp)
{
// Evict old samples
var cutoff = timestamp.AddSeconds(-_windowSizeSeconds);
_samples.RemoveAll(s => s.Timestamp < cutoff);
// Add new sample
if (_samples.Count < _maxSamples)
{
_samples.Add((latency, timestamp));
}
else
{
// Reservoir sampling for large windows
var index = Random.Shared.Next(_samples.Count + 1);
if (index < _samples.Count)
{
_samples[index] = (latency, timestamp);
}
}
}
public double? GetPercentile(double percentile)
{
if (_samples.Count == 0) return null;
var sorted = _samples.Select(s => s.Latency).OrderBy(x => x).ToList();
var index = (int)Math.Ceiling(percentile * sorted.Count) - 1;
index = Math.Max(0, Math.Min(sorted.Count - 1, index));
return sorted[index];
}
}
}
/// <summary>
/// Options for TTE percentile exporter.
/// </summary>
public sealed class TtePercentileOptions
{
/// <summary>
/// Metric version.
/// </summary>
public string Version { get; init; } = "1.0.0";
/// <summary>
/// Rolling window size in seconds for percentile calculation.
/// </summary>
public int WindowSizeSeconds { get; init; } = 300; // 5 minutes
/// <summary>
/// Maximum samples to keep per window.
/// </summary>
public int MaxSamplesPerWindow { get; init; } = 1000;
}