feat: Add PathViewer and RiskDriftCard components with templates and styles

- Implemented PathViewerComponent for visualizing reachability call paths.
- Added RiskDriftCardComponent to display reachability drift results.
- Created corresponding HTML templates and SCSS styles for both components.
- Introduced test fixtures for reachability analysis in JSON format.
- Enhanced user interaction with collapsible and expandable features in PathViewer.
- Included risk trend visualization and summary metrics in RiskDriftCard.
This commit is contained in:
master
2025-12-18 18:35:30 +02:00
parent 811f35cba7
commit 0dc71e760a
70 changed files with 8904 additions and 163 deletions

View File

@@ -0,0 +1,233 @@
// -----------------------------------------------------------------------------
// VulnSurfaceMetrics.cs
// Sprint: SPRINT_3700_0002_0001_vuln_surfaces_core
// Task: SURF-019
// Description: Metrics for vulnerability surface computation.
// -----------------------------------------------------------------------------
using System.Diagnostics.Metrics;
namespace StellaOps.Scanner.VulnSurfaces.Diagnostics;
/// <summary>
/// Metrics for vulnerability surface computation and caching.
/// </summary>
public static class VulnSurfaceMetrics
{
private static readonly Meter Meter = new("StellaOps.Scanner.VulnSurfaces", "1.0.0");
// ===== BUILD COUNTERS =====
/// <summary>
/// Total surface build requests by ecosystem.
/// </summary>
public static readonly Counter<long> BuildRequests = Meter.CreateCounter<long>(
"stellaops_vulnsurface_build_requests_total",
description: "Total vulnerability surface build requests");
/// <summary>
/// Successful surface builds by ecosystem.
/// </summary>
public static readonly Counter<long> BuildSuccesses = Meter.CreateCounter<long>(
"stellaops_vulnsurface_build_successes_total",
description: "Total successful vulnerability surface builds");
/// <summary>
/// Failed surface builds by ecosystem and reason.
/// </summary>
public static readonly Counter<long> BuildFailures = Meter.CreateCounter<long>(
"stellaops_vulnsurface_build_failures_total",
description: "Total failed vulnerability surface builds");
/// <summary>
/// Cache hits when surface already computed.
/// </summary>
public static readonly Counter<long> CacheHits = Meter.CreateCounter<long>(
"stellaops_vulnsurface_cache_hits_total",
description: "Total cache hits for pre-computed surfaces");
// ===== DOWNLOAD COUNTERS =====
/// <summary>
/// Package downloads attempted by ecosystem.
/// </summary>
public static readonly Counter<long> DownloadAttempts = Meter.CreateCounter<long>(
"stellaops_vulnsurface_downloads_attempted_total",
description: "Total package download attempts");
/// <summary>
/// Successful package downloads.
/// </summary>
public static readonly Counter<long> DownloadSuccesses = Meter.CreateCounter<long>(
"stellaops_vulnsurface_downloads_succeeded_total",
description: "Total successful package downloads");
/// <summary>
/// Failed package downloads.
/// </summary>
public static readonly Counter<long> DownloadFailures = Meter.CreateCounter<long>(
"stellaops_vulnsurface_downloads_failed_total",
description: "Total failed package downloads");
// ===== FINGERPRINT COUNTERS =====
/// <summary>
/// Methods fingerprinted by ecosystem.
/// </summary>
public static readonly Counter<long> MethodsFingerprinted = Meter.CreateCounter<long>(
"stellaops_vulnsurface_methods_fingerprinted_total",
description: "Total methods fingerprinted");
/// <summary>
/// Methods changed (sinks) identified.
/// </summary>
public static readonly Counter<long> SinksIdentified = Meter.CreateCounter<long>(
"stellaops_vulnsurface_sinks_identified_total",
description: "Total sink methods (changed methods) identified");
// ===== TIMING HISTOGRAMS =====
/// <summary>
/// End-to-end surface build duration.
/// </summary>
public static readonly Histogram<double> BuildDurationSeconds = Meter.CreateHistogram<double>(
"stellaops_vulnsurface_build_duration_seconds",
unit: "s",
description: "Duration of surface build operations",
advice: new InstrumentAdvice<double>
{
HistogramBucketBoundaries = [0.1, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0, 60.0, 120.0]
});
/// <summary>
/// Package download duration.
/// </summary>
public static readonly Histogram<double> DownloadDurationSeconds = Meter.CreateHistogram<double>(
"stellaops_vulnsurface_download_duration_seconds",
unit: "s",
description: "Duration of package download operations",
advice: new InstrumentAdvice<double>
{
HistogramBucketBoundaries = [0.1, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0]
});
/// <summary>
/// Fingerprinting duration per package.
/// </summary>
public static readonly Histogram<double> FingerprintDurationSeconds = Meter.CreateHistogram<double>(
"stellaops_vulnsurface_fingerprint_duration_seconds",
unit: "s",
description: "Duration of fingerprinting operations",
advice: new InstrumentAdvice<double>
{
HistogramBucketBoundaries = [0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0]
});
/// <summary>
/// Diff computation duration.
/// </summary>
public static readonly Histogram<double> DiffDurationSeconds = Meter.CreateHistogram<double>(
"stellaops_vulnsurface_diff_duration_seconds",
unit: "s",
description: "Duration of diff computation",
advice: new InstrumentAdvice<double>
{
HistogramBucketBoundaries = [0.001, 0.01, 0.05, 0.1, 0.25, 0.5, 1.0]
});
// ===== SIZE HISTOGRAMS =====
/// <summary>
/// Number of methods per package version.
/// </summary>
public static readonly Histogram<int> MethodsPerPackage = Meter.CreateHistogram<int>(
"stellaops_vulnsurface_methods_per_package",
description: "Number of methods per analyzed package version",
advice: new InstrumentAdvice<int>
{
HistogramBucketBoundaries = [10, 50, 100, 250, 500, 1000, 2500, 5000, 10000]
});
/// <summary>
/// Number of sinks per surface.
/// </summary>
public static readonly Histogram<int> SinksPerSurface = Meter.CreateHistogram<int>(
"stellaops_vulnsurface_sinks_per_surface",
description: "Number of sink methods per vulnerability surface",
advice: new InstrumentAdvice<int>
{
HistogramBucketBoundaries = [1, 2, 5, 10, 25, 50, 100, 250]
});
// ===== ECOSYSTEM DISTRIBUTION =====
private static int _nugetSurfaces;
private static int _npmSurfaces;
private static int _mavenSurfaces;
private static int _pypiSurfaces;
/// <summary>
/// Current count of NuGet surfaces.
/// </summary>
public static readonly ObservableGauge<int> NuGetSurfaceCount = Meter.CreateObservableGauge(
"stellaops_vulnsurface_nuget_count",
() => _nugetSurfaces,
description: "Current count of NuGet vulnerability surfaces");
/// <summary>
/// Current count of npm surfaces.
/// </summary>
public static readonly ObservableGauge<int> NpmSurfaceCount = Meter.CreateObservableGauge(
"stellaops_vulnsurface_npm_count",
() => _npmSurfaces,
description: "Current count of npm vulnerability surfaces");
/// <summary>
/// Current count of Maven surfaces.
/// </summary>
public static readonly ObservableGauge<int> MavenSurfaceCount = Meter.CreateObservableGauge(
"stellaops_vulnsurface_maven_count",
() => _mavenSurfaces,
description: "Current count of Maven vulnerability surfaces");
/// <summary>
/// Current count of PyPI surfaces.
/// </summary>
public static readonly ObservableGauge<int> PyPISurfaceCount = Meter.CreateObservableGauge(
"stellaops_vulnsurface_pypi_count",
() => _pypiSurfaces,
description: "Current count of PyPI vulnerability surfaces");
/// <summary>
/// Updates the ecosystem surface counts.
/// </summary>
public static void SetEcosystemCounts(int nuget, int npm, int maven, int pypi)
{
Interlocked.Exchange(ref _nugetSurfaces, nuget);
Interlocked.Exchange(ref _npmSurfaces, npm);
Interlocked.Exchange(ref _mavenSurfaces, maven);
Interlocked.Exchange(ref _pypiSurfaces, pypi);
}
/// <summary>
/// Increments the surface count for an ecosystem.
/// </summary>
public static void IncrementEcosystemCount(string ecosystem)
{
switch (ecosystem.ToLowerInvariant())
{
case "nuget":
Interlocked.Increment(ref _nugetSurfaces);
break;
case "npm":
Interlocked.Increment(ref _npmSurfaces);
break;
case "maven":
Interlocked.Increment(ref _mavenSurfaces);
break;
case "pypi":
Interlocked.Increment(ref _pypiSurfaces);
break;
}
}
}