feat(metrics): Implement scan metrics repository and PostgreSQL integration
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled

- Added IScanMetricsRepository interface for scan metrics persistence and retrieval.
- Implemented PostgresScanMetricsRepository for PostgreSQL database interactions, including methods for saving and retrieving scan metrics and execution phases.
- Introduced methods for obtaining TTE statistics and recent scans for tenants.
- Implemented deletion of old metrics for retention purposes.

test(tests): Add SCA Failure Catalogue tests for FC6-FC10

- Created ScaCatalogueDeterminismTests to validate determinism properties of SCA Failure Catalogue fixtures.
- Developed ScaFailureCatalogueTests to ensure correct handling of specific failure modes in the scanner.
- Included tests for manifest validation, file existence, and expected findings across multiple failure cases.

feat(telemetry): Integrate scan completion metrics into the pipeline

- Introduced IScanCompletionMetricsIntegration interface and ScanCompletionMetricsIntegration class to record metrics upon scan completion.
- Implemented proof coverage and TTE metrics recording with logging for scan completion summaries.
This commit is contained in:
master
2025-12-16 14:00:35 +02:00
parent b55d9fa68d
commit 415eff1207
27 changed files with 3620 additions and 35 deletions

View File

@@ -0,0 +1,111 @@
// -----------------------------------------------------------------------------
// ScanCompletionMetricsIntegration.cs
// Sprint: SPRINT_3401_0001_0001_determinism_scoring_foundations
// Task: DET-3401-007
// Description: Integrates proof coverage calculation into scan completion pipeline
// -----------------------------------------------------------------------------
using Microsoft.Extensions.Logging;
namespace StellaOps.Telemetry.Core;
/// <summary>
/// Integrates proof coverage metrics into the scan completion pipeline.
/// </summary>
public interface IScanCompletionMetricsIntegration
{
/// <summary>
/// Records metrics at scan completion.
/// </summary>
void OnScanCompleted(ScanCompletionData data);
}
/// <summary>
/// Data required for scan completion metrics calculation.
/// </summary>
public sealed record ScanCompletionData
{
public required string TenantId { get; init; }
public required string SurfaceId { get; init; }
public required string ScanId { get; init; }
// Finding counts
public required int TotalFindings { get; init; }
public required int FindingsWithReceipts { get; init; }
// VEX counts
public required int TotalVexItems { get; init; }
public required int VexWithReceipts { get; init; }
// Reachability counts
public required int TotalReachableFindings { get; init; }
public required int ReachableWithProofs { get; init; }
// Timing
public required DateTimeOffset StartedAt { get; init; }
public required DateTimeOffset CompletedAt { get; init; }
/// <summary>
/// Total scan duration in milliseconds.
/// </summary>
public int DurationMs => (int)(CompletedAt - StartedAt).TotalMilliseconds;
}
public sealed class ScanCompletionMetricsIntegration : IScanCompletionMetricsIntegration
{
private readonly ProofCoverageMetrics _proofCoverage;
private readonly TimeToEvidenceMetrics _tteMetrics;
private readonly ILogger<ScanCompletionMetricsIntegration> _logger;
public ScanCompletionMetricsIntegration(
ProofCoverageMetrics proofCoverage,
TimeToEvidenceMetrics tteMetrics,
ILogger<ScanCompletionMetricsIntegration> logger)
{
_proofCoverage = proofCoverage ?? throw new ArgumentNullException(nameof(proofCoverage));
_tteMetrics = tteMetrics ?? throw new ArgumentNullException(nameof(tteMetrics));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public void OnScanCompleted(ScanCompletionData data)
{
ArgumentNullException.ThrowIfNull(data);
// Record proof coverage metrics
_proofCoverage.RecordScanCoverage(
tenantId: data.TenantId,
surfaceId: data.SurfaceId,
findingsWithReceipts: data.FindingsWithReceipts,
totalFindings: data.TotalFindings,
vexWithReceipts: data.VexWithReceipts,
totalVex: data.TotalVexItems,
reachableWithProofs: data.ReachableWithProofs,
totalReachable: data.TotalReachableFindings);
// Record TTE metrics
_tteMetrics.RecordScanDuration(
tenantId: data.TenantId,
surfaceId: data.SurfaceId,
durationMs: data.DurationMs,
findingCount: data.TotalFindings);
// Log summary
var allCoverage = ComputePercentage(data.FindingsWithReceipts, data.TotalFindings);
var vexCoverage = ComputePercentage(data.VexWithReceipts, data.TotalVexItems);
var reachCoverage = ComputePercentage(data.ReachableWithProofs, data.TotalReachableFindings);
_logger.LogInformation(
"Scan {ScanId} completed. TTE={DurationMs}ms, Proof coverage: all={AllCov}%, vex={VexCov}%, reachable={ReachCov}%",
data.ScanId,
data.DurationMs,
allCoverage,
vexCoverage,
reachCoverage);
}
private static int ComputePercentage(int numerator, int denominator)
{
if (denominator <= 0) return 100;
return (numerator * 100) / denominator;
}
}