feat(telemetry): add telemetry client and services for tracking events
- Implemented TelemetryClient to handle event queuing and flushing to the telemetry endpoint. - Created TtfsTelemetryService for emitting specific telemetry events related to TTFS. - Added tests for TelemetryClient to ensure event queuing and flushing functionality. - Introduced models for reachability drift detection, including DriftResult and DriftedSink. - Developed DriftApiService for interacting with the drift detection API. - Updated FirstSignalCardComponent to emit telemetry events on signal appearance. - Enhanced localization support for first signal component with i18n strings.
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// VulnSurface.cs
|
||||
// Sprint: SPRINT_3700_0002_0001_vuln_surfaces_core
|
||||
// Description: Core models for vulnerability surface computation.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Scanner.VulnSurfaces.Models;
|
||||
|
||||
/// <summary>
|
||||
/// A vulnerability surface represents the specific methods that changed
|
||||
/// between a vulnerable and fixed version of a package.
|
||||
/// </summary>
|
||||
public sealed record VulnSurface
|
||||
{
|
||||
/// <summary>
|
||||
/// Database ID.
|
||||
/// </summary>
|
||||
[JsonPropertyName("surface_id")]
|
||||
public long SurfaceId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// CVE ID (e.g., "CVE-2024-12345").
|
||||
/// </summary>
|
||||
[JsonPropertyName("cve_id")]
|
||||
public required string CveId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Package identifier (PURL format preferred).
|
||||
/// </summary>
|
||||
[JsonPropertyName("package_id")]
|
||||
public required string PackageId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Ecosystem (nuget, npm, maven, pypi).
|
||||
/// </summary>
|
||||
[JsonPropertyName("ecosystem")]
|
||||
public required string Ecosystem { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Vulnerable version analyzed.
|
||||
/// </summary>
|
||||
[JsonPropertyName("vuln_version")]
|
||||
public required string VulnVersion { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Fixed version used for diff.
|
||||
/// </summary>
|
||||
[JsonPropertyName("fixed_version")]
|
||||
public required string FixedVersion { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Sink methods (vulnerable code locations).
|
||||
/// </summary>
|
||||
[JsonPropertyName("sinks")]
|
||||
public IReadOnlyList<VulnSurfaceSink> Sinks { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Number of trigger methods that can reach sinks.
|
||||
/// </summary>
|
||||
[JsonPropertyName("trigger_count")]
|
||||
public int TriggerCount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Surface computation status.
|
||||
/// </summary>
|
||||
[JsonPropertyName("status")]
|
||||
public VulnSurfaceStatus Status { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Confidence score (0.0-1.0).
|
||||
/// </summary>
|
||||
[JsonPropertyName("confidence")]
|
||||
public double Confidence { get; init; } = 1.0;
|
||||
|
||||
/// <summary>
|
||||
/// When the surface was computed.
|
||||
/// </summary>
|
||||
[JsonPropertyName("computed_at")]
|
||||
public DateTimeOffset ComputedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Error message if computation failed.
|
||||
/// </summary>
|
||||
[JsonPropertyName("error")]
|
||||
public string? Error { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A sink method - a specific method that was modified in the security fix.
|
||||
/// </summary>
|
||||
public sealed record VulnSurfaceSink
|
||||
{
|
||||
/// <summary>
|
||||
/// Database ID.
|
||||
/// </summary>
|
||||
[JsonPropertyName("sink_id")]
|
||||
public long SinkId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Parent surface ID.
|
||||
/// </summary>
|
||||
[JsonPropertyName("surface_id")]
|
||||
public long SurfaceId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Normalized method key.
|
||||
/// </summary>
|
||||
[JsonPropertyName("method_key")]
|
||||
public required string MethodKey { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Declaring type/class name.
|
||||
/// </summary>
|
||||
[JsonPropertyName("declaring_type")]
|
||||
public required string DeclaringType { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Method name.
|
||||
/// </summary>
|
||||
[JsonPropertyName("method_name")]
|
||||
public required string MethodName { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Method signature.
|
||||
/// </summary>
|
||||
[JsonPropertyName("signature")]
|
||||
public string? Signature { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Type of change detected.
|
||||
/// </summary>
|
||||
[JsonPropertyName("change_type")]
|
||||
public MethodChangeType ChangeType { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Hash of the method in vulnerable version.
|
||||
/// </summary>
|
||||
[JsonPropertyName("vuln_hash")]
|
||||
public string? VulnHash { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Hash of the method in fixed version.
|
||||
/// </summary>
|
||||
[JsonPropertyName("fixed_hash")]
|
||||
public string? FixedHash { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this sink is directly exploitable.
|
||||
/// </summary>
|
||||
[JsonPropertyName("is_direct_exploit")]
|
||||
public bool IsDirectExploit { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Status of vulnerability surface computation.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public enum VulnSurfaceStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// Computation pending.
|
||||
/// </summary>
|
||||
Pending,
|
||||
|
||||
/// <summary>
|
||||
/// Computation in progress.
|
||||
/// </summary>
|
||||
Computing,
|
||||
|
||||
/// <summary>
|
||||
/// Successfully computed.
|
||||
/// </summary>
|
||||
Computed,
|
||||
|
||||
/// <summary>
|
||||
/// Computation failed.
|
||||
/// </summary>
|
||||
Failed,
|
||||
|
||||
/// <summary>
|
||||
/// No diff detected (versions identical).
|
||||
/// </summary>
|
||||
NoDiff,
|
||||
|
||||
/// <summary>
|
||||
/// Package not found.
|
||||
/// </summary>
|
||||
PackageNotFound
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Type of method change detected.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public enum MethodChangeType
|
||||
{
|
||||
/// <summary>
|
||||
/// Method body was modified.
|
||||
/// </summary>
|
||||
Modified,
|
||||
|
||||
/// <summary>
|
||||
/// Method was added in fixed version.
|
||||
/// </summary>
|
||||
Added,
|
||||
|
||||
/// <summary>
|
||||
/// Method was removed in fixed version.
|
||||
/// </summary>
|
||||
Removed,
|
||||
|
||||
/// <summary>
|
||||
/// Method signature changed.
|
||||
/// </summary>
|
||||
SignatureChanged
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// VulnSurfaceTrigger.cs
|
||||
// Sprint: SPRINT_3700_0003_0001_trigger_extraction
|
||||
// Description: Model for trigger methods that can reach vulnerable sinks.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Scanner.VulnSurfaces.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a trigger method - a public API that can reach a vulnerable sink method.
|
||||
/// </summary>
|
||||
public sealed record VulnSurfaceTrigger
|
||||
{
|
||||
/// <summary>
|
||||
/// Surface ID this trigger belongs to.
|
||||
/// </summary>
|
||||
[JsonPropertyName("surface_id")]
|
||||
public long SurfaceId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Unique key for the trigger method (public API).
|
||||
/// Format: namespace.class::methodName(signature)
|
||||
/// </summary>
|
||||
[JsonPropertyName("trigger_method_key")]
|
||||
public required string TriggerMethodKey { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Unique key for the sink method (vulnerable code location).
|
||||
/// </summary>
|
||||
[JsonPropertyName("sink_method_key")]
|
||||
public required string SinkMethodKey { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal call path from trigger to sink within the package.
|
||||
/// </summary>
|
||||
[JsonPropertyName("internal_path")]
|
||||
public IReadOnlyList<string>? InternalPath { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this trigger was found via interface/base method expansion.
|
||||
/// </summary>
|
||||
[JsonPropertyName("is_interface_expansion")]
|
||||
public bool IsInterfaceExpansion { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Depth from trigger to sink.
|
||||
/// </summary>
|
||||
[JsonPropertyName("depth")]
|
||||
public int Depth { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Confidence score for this trigger path (0.0-1.0).
|
||||
/// </summary>
|
||||
[JsonPropertyName("confidence")]
|
||||
public double Confidence { get; init; } = 1.0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal method reference within a call graph.
|
||||
/// </summary>
|
||||
public sealed record InternalMethodRef
|
||||
{
|
||||
/// <summary>
|
||||
/// Fully qualified method key.
|
||||
/// </summary>
|
||||
public required string MethodKey { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Method name without namespace.
|
||||
/// </summary>
|
||||
public required string Name { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Declaring type name.
|
||||
/// </summary>
|
||||
public required string DeclaringType { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this method is public.
|
||||
/// </summary>
|
||||
public bool IsPublic { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this method is from an interface.
|
||||
/// </summary>
|
||||
public bool IsInterface { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this method is virtual/abstract (can be overridden).
|
||||
/// </summary>
|
||||
public bool IsVirtual { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Signature parameters.
|
||||
/// </summary>
|
||||
public IReadOnlyList<string>? Parameters { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Return type.
|
||||
/// </summary>
|
||||
public string? ReturnType { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Edge in the internal call graph.
|
||||
/// </summary>
|
||||
public sealed record InternalCallEdge
|
||||
{
|
||||
/// <summary>
|
||||
/// Caller method key.
|
||||
/// </summary>
|
||||
public required string Caller { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Callee method key.
|
||||
/// </summary>
|
||||
public required string Callee { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Call site offset (IL offset for .NET, bytecode offset for Java).
|
||||
/// </summary>
|
||||
public int? CallSiteOffset { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this is a virtual/dispatch call.
|
||||
/// </summary>
|
||||
public bool IsVirtualCall { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Result of trigger extraction for a vulnerability surface.
|
||||
/// </summary>
|
||||
public sealed record TriggerExtractionResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether extraction succeeded.
|
||||
/// </summary>
|
||||
public bool Success { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Extracted triggers.
|
||||
/// </summary>
|
||||
public IReadOnlyList<VulnSurfaceTrigger> Triggers { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Error message if extraction failed.
|
||||
/// </summary>
|
||||
public string? Error { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of public methods analyzed.
|
||||
/// </summary>
|
||||
public int PublicMethodsAnalyzed { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of internal edges in the call graph.
|
||||
/// </summary>
|
||||
public int InternalEdgeCount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Extraction duration.
|
||||
/// </summary>
|
||||
public TimeSpan Duration { get; init; }
|
||||
}
|
||||
Reference in New Issue
Block a user