Implement VEX document verification system with issuer management and signature verification
- Added IIssuerDirectory interface for managing VEX document issuers, including methods for registration, revocation, and trust validation. - Created InMemoryIssuerDirectory class as an in-memory implementation of IIssuerDirectory for testing and single-instance deployments. - Introduced ISignatureVerifier interface for verifying signatures on VEX documents, with support for multiple signature formats. - Developed SignatureVerifier class as the default implementation of ISignatureVerifier, allowing extensibility for different signature formats. - Implemented handlers for DSSE and JWS signature formats, including methods for verification and signature extraction. - Defined various records and enums for issuer and signature metadata, enhancing the structure and clarity of the verification process.
This commit is contained in:
@@ -0,0 +1,295 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Text.Json.Serialization;
|
||||
using StellaOps.Policy.RiskProfile.Models;
|
||||
|
||||
namespace StellaOps.Policy.Engine.Simulation;
|
||||
|
||||
/// <summary>
|
||||
/// Detailed breakdown of a risk simulation result.
|
||||
/// Per POLICY-RISK-67-003.
|
||||
/// </summary>
|
||||
public sealed record RiskSimulationBreakdown(
|
||||
[property: JsonPropertyName("simulation_id")] string SimulationId,
|
||||
[property: JsonPropertyName("profile_ref")] ProfileReference ProfileRef,
|
||||
[property: JsonPropertyName("signal_analysis")] SignalAnalysis SignalAnalysis,
|
||||
[property: JsonPropertyName("override_analysis")] OverrideAnalysis OverrideAnalysis,
|
||||
[property: JsonPropertyName("score_distribution")] ScoreDistributionAnalysis ScoreDistribution,
|
||||
[property: JsonPropertyName("severity_breakdown")] SeverityBreakdownAnalysis SeverityBreakdown,
|
||||
[property: JsonPropertyName("action_breakdown")] ActionBreakdownAnalysis ActionBreakdown,
|
||||
[property: JsonPropertyName("component_breakdown")] ComponentBreakdownAnalysis? ComponentBreakdown,
|
||||
[property: JsonPropertyName("risk_trends")] RiskTrendAnalysis? RiskTrends,
|
||||
[property: JsonPropertyName("determinism_hash")] string DeterminismHash);
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the risk profile used in simulation.
|
||||
/// </summary>
|
||||
public sealed record ProfileReference(
|
||||
[property: JsonPropertyName("id")] string Id,
|
||||
[property: JsonPropertyName("version")] string Version,
|
||||
[property: JsonPropertyName("hash")] string Hash,
|
||||
[property: JsonPropertyName("description")] string? Description,
|
||||
[property: JsonPropertyName("extends")] string? Extends);
|
||||
|
||||
/// <summary>
|
||||
/// Analysis of signal contributions to risk scores.
|
||||
/// </summary>
|
||||
public sealed record SignalAnalysis(
|
||||
[property: JsonPropertyName("total_signals")] int TotalSignals,
|
||||
[property: JsonPropertyName("signals_used")] int SignalsUsed,
|
||||
[property: JsonPropertyName("signals_missing")] int SignalsMissing,
|
||||
[property: JsonPropertyName("signal_coverage")] double SignalCoverage,
|
||||
[property: JsonPropertyName("signal_stats")] ImmutableArray<SignalStatistics> SignalStats,
|
||||
[property: JsonPropertyName("top_contributors")] ImmutableArray<SignalContributor> TopContributors,
|
||||
[property: JsonPropertyName("missing_signal_impact")] MissingSignalImpact MissingSignalImpact);
|
||||
|
||||
/// <summary>
|
||||
/// Statistics for a single signal across all findings.
|
||||
/// </summary>
|
||||
public sealed record SignalStatistics(
|
||||
[property: JsonPropertyName("signal_name")] string SignalName,
|
||||
[property: JsonPropertyName("signal_type")] string SignalType,
|
||||
[property: JsonPropertyName("weight")] double Weight,
|
||||
[property: JsonPropertyName("findings_with_signal")] int FindingsWithSignal,
|
||||
[property: JsonPropertyName("findings_missing_signal")] int FindingsMissingSignal,
|
||||
[property: JsonPropertyName("coverage_percentage")] double CoveragePercentage,
|
||||
[property: JsonPropertyName("value_distribution")] ValueDistribution? ValueDistribution,
|
||||
[property: JsonPropertyName("total_contribution")] double TotalContribution,
|
||||
[property: JsonPropertyName("avg_contribution")] double AvgContribution);
|
||||
|
||||
/// <summary>
|
||||
/// Distribution of values for a signal.
|
||||
/// </summary>
|
||||
public sealed record ValueDistribution(
|
||||
[property: JsonPropertyName("min")] double? Min,
|
||||
[property: JsonPropertyName("max")] double? Max,
|
||||
[property: JsonPropertyName("mean")] double? Mean,
|
||||
[property: JsonPropertyName("median")] double? Median,
|
||||
[property: JsonPropertyName("std_dev")] double? StdDev,
|
||||
[property: JsonPropertyName("histogram")] ImmutableArray<HistogramBucket>? Histogram);
|
||||
|
||||
/// <summary>
|
||||
/// Histogram bucket for value distribution.
|
||||
/// </summary>
|
||||
public sealed record HistogramBucket(
|
||||
[property: JsonPropertyName("range_min")] double RangeMin,
|
||||
[property: JsonPropertyName("range_max")] double RangeMax,
|
||||
[property: JsonPropertyName("count")] int Count,
|
||||
[property: JsonPropertyName("percentage")] double Percentage);
|
||||
|
||||
/// <summary>
|
||||
/// A signal that significantly contributed to risk scores.
|
||||
/// </summary>
|
||||
public sealed record SignalContributor(
|
||||
[property: JsonPropertyName("signal_name")] string SignalName,
|
||||
[property: JsonPropertyName("total_contribution")] double TotalContribution,
|
||||
[property: JsonPropertyName("contribution_percentage")] double ContributionPercentage,
|
||||
[property: JsonPropertyName("avg_value")] double AvgValue,
|
||||
[property: JsonPropertyName("weight")] double Weight,
|
||||
[property: JsonPropertyName("impact_direction")] string ImpactDirection);
|
||||
|
||||
/// <summary>
|
||||
/// Impact of missing signals on scoring.
|
||||
/// </summary>
|
||||
public sealed record MissingSignalImpact(
|
||||
[property: JsonPropertyName("findings_with_missing_signals")] int FindingsWithMissingSignals,
|
||||
[property: JsonPropertyName("avg_missing_signals_per_finding")] double AvgMissingSignalsPerFinding,
|
||||
[property: JsonPropertyName("estimated_score_impact")] double EstimatedScoreImpact,
|
||||
[property: JsonPropertyName("most_impactful_missing")] ImmutableArray<string> MostImpactfulMissing);
|
||||
|
||||
/// <summary>
|
||||
/// Analysis of override applications.
|
||||
/// </summary>
|
||||
public sealed record OverrideAnalysis(
|
||||
[property: JsonPropertyName("total_overrides_evaluated")] int TotalOverridesEvaluated,
|
||||
[property: JsonPropertyName("severity_overrides_applied")] int SeverityOverridesApplied,
|
||||
[property: JsonPropertyName("decision_overrides_applied")] int DecisionOverridesApplied,
|
||||
[property: JsonPropertyName("override_application_rate")] double OverrideApplicationRate,
|
||||
[property: JsonPropertyName("severity_override_details")] ImmutableArray<SeverityOverrideDetail> SeverityOverrideDetails,
|
||||
[property: JsonPropertyName("decision_override_details")] ImmutableArray<DecisionOverrideDetail> DecisionOverrideDetails,
|
||||
[property: JsonPropertyName("override_conflicts")] ImmutableArray<OverrideConflict> OverrideConflicts);
|
||||
|
||||
/// <summary>
|
||||
/// Details of severity override applications.
|
||||
/// </summary>
|
||||
public sealed record SeverityOverrideDetail(
|
||||
[property: JsonPropertyName("predicate_hash")] string PredicateHash,
|
||||
[property: JsonPropertyName("predicate_summary")] string PredicateSummary,
|
||||
[property: JsonPropertyName("target_severity")] string TargetSeverity,
|
||||
[property: JsonPropertyName("applications_count")] int ApplicationsCount,
|
||||
[property: JsonPropertyName("original_severities")] ImmutableDictionary<string, int> OriginalSeverities);
|
||||
|
||||
/// <summary>
|
||||
/// Details of decision override applications.
|
||||
/// </summary>
|
||||
public sealed record DecisionOverrideDetail(
|
||||
[property: JsonPropertyName("predicate_hash")] string PredicateHash,
|
||||
[property: JsonPropertyName("predicate_summary")] string PredicateSummary,
|
||||
[property: JsonPropertyName("target_action")] string TargetAction,
|
||||
[property: JsonPropertyName("reason")] string? Reason,
|
||||
[property: JsonPropertyName("applications_count")] int ApplicationsCount,
|
||||
[property: JsonPropertyName("original_actions")] ImmutableDictionary<string, int> OriginalActions);
|
||||
|
||||
/// <summary>
|
||||
/// Override conflict detected during evaluation.
|
||||
/// </summary>
|
||||
public sealed record OverrideConflict(
|
||||
[property: JsonPropertyName("finding_id")] string FindingId,
|
||||
[property: JsonPropertyName("conflict_type")] string ConflictType,
|
||||
[property: JsonPropertyName("override_1")] string Override1,
|
||||
[property: JsonPropertyName("override_2")] string Override2,
|
||||
[property: JsonPropertyName("resolution")] string Resolution);
|
||||
|
||||
/// <summary>
|
||||
/// Analysis of score distribution.
|
||||
/// </summary>
|
||||
public sealed record ScoreDistributionAnalysis(
|
||||
[property: JsonPropertyName("raw_score_stats")] ScoreStatistics RawScoreStats,
|
||||
[property: JsonPropertyName("normalized_score_stats")] ScoreStatistics NormalizedScoreStats,
|
||||
[property: JsonPropertyName("score_buckets")] ImmutableArray<ScoreBucket> ScoreBuckets,
|
||||
[property: JsonPropertyName("percentiles")] ImmutableDictionary<string, double> Percentiles,
|
||||
[property: JsonPropertyName("outliers")] OutlierAnalysis Outliers);
|
||||
|
||||
/// <summary>
|
||||
/// Statistical summary of scores.
|
||||
/// </summary>
|
||||
public sealed record ScoreStatistics(
|
||||
[property: JsonPropertyName("count")] int Count,
|
||||
[property: JsonPropertyName("min")] double Min,
|
||||
[property: JsonPropertyName("max")] double Max,
|
||||
[property: JsonPropertyName("mean")] double Mean,
|
||||
[property: JsonPropertyName("median")] double Median,
|
||||
[property: JsonPropertyName("std_dev")] double StdDev,
|
||||
[property: JsonPropertyName("variance")] double Variance,
|
||||
[property: JsonPropertyName("skewness")] double Skewness,
|
||||
[property: JsonPropertyName("kurtosis")] double Kurtosis);
|
||||
|
||||
/// <summary>
|
||||
/// Score bucket for distribution.
|
||||
/// </summary>
|
||||
public sealed record ScoreBucket(
|
||||
[property: JsonPropertyName("range_min")] double RangeMin,
|
||||
[property: JsonPropertyName("range_max")] double RangeMax,
|
||||
[property: JsonPropertyName("label")] string Label,
|
||||
[property: JsonPropertyName("count")] int Count,
|
||||
[property: JsonPropertyName("percentage")] double Percentage);
|
||||
|
||||
/// <summary>
|
||||
/// Outlier analysis for scores.
|
||||
/// </summary>
|
||||
public sealed record OutlierAnalysis(
|
||||
[property: JsonPropertyName("outlier_count")] int OutlierCount,
|
||||
[property: JsonPropertyName("outlier_threshold")] double OutlierThreshold,
|
||||
[property: JsonPropertyName("outlier_finding_ids")] ImmutableArray<string> OutlierFindingIds);
|
||||
|
||||
/// <summary>
|
||||
/// Breakdown by severity level.
|
||||
/// </summary>
|
||||
public sealed record SeverityBreakdownAnalysis(
|
||||
[property: JsonPropertyName("by_severity")] ImmutableDictionary<string, SeverityBucket> BySeverity,
|
||||
[property: JsonPropertyName("severity_flow")] ImmutableArray<SeverityFlow> SeverityFlow,
|
||||
[property: JsonPropertyName("severity_concentration")] double SeverityConcentration);
|
||||
|
||||
/// <summary>
|
||||
/// Details for a severity bucket.
|
||||
/// </summary>
|
||||
public sealed record SeverityBucket(
|
||||
[property: JsonPropertyName("severity")] string Severity,
|
||||
[property: JsonPropertyName("count")] int Count,
|
||||
[property: JsonPropertyName("percentage")] double Percentage,
|
||||
[property: JsonPropertyName("avg_score")] double AvgScore,
|
||||
[property: JsonPropertyName("score_range")] ScoreRange ScoreRange,
|
||||
[property: JsonPropertyName("top_contributors")] ImmutableArray<string> TopContributors);
|
||||
|
||||
/// <summary>
|
||||
/// Score range for a bucket.
|
||||
/// </summary>
|
||||
public sealed record ScoreRange(
|
||||
[property: JsonPropertyName("min")] double Min,
|
||||
[property: JsonPropertyName("max")] double Max);
|
||||
|
||||
/// <summary>
|
||||
/// Flow from original to final severity after overrides.
|
||||
/// </summary>
|
||||
public sealed record SeverityFlow(
|
||||
[property: JsonPropertyName("from_severity")] string FromSeverity,
|
||||
[property: JsonPropertyName("to_severity")] string ToSeverity,
|
||||
[property: JsonPropertyName("count")] int Count,
|
||||
[property: JsonPropertyName("is_escalation")] bool IsEscalation);
|
||||
|
||||
/// <summary>
|
||||
/// Breakdown by recommended action.
|
||||
/// </summary>
|
||||
public sealed record ActionBreakdownAnalysis(
|
||||
[property: JsonPropertyName("by_action")] ImmutableDictionary<string, ActionBucket> ByAction,
|
||||
[property: JsonPropertyName("action_flow")] ImmutableArray<ActionFlow> ActionFlow,
|
||||
[property: JsonPropertyName("decision_stability")] double DecisionStability);
|
||||
|
||||
/// <summary>
|
||||
/// Details for an action bucket.
|
||||
/// </summary>
|
||||
public sealed record ActionBucket(
|
||||
[property: JsonPropertyName("action")] string Action,
|
||||
[property: JsonPropertyName("count")] int Count,
|
||||
[property: JsonPropertyName("percentage")] double Percentage,
|
||||
[property: JsonPropertyName("avg_score")] double AvgScore,
|
||||
[property: JsonPropertyName("severity_breakdown")] ImmutableDictionary<string, int> SeverityBreakdown);
|
||||
|
||||
/// <summary>
|
||||
/// Flow from original to final action after overrides.
|
||||
/// </summary>
|
||||
public sealed record ActionFlow(
|
||||
[property: JsonPropertyName("from_action")] string FromAction,
|
||||
[property: JsonPropertyName("to_action")] string ToAction,
|
||||
[property: JsonPropertyName("count")] int Count);
|
||||
|
||||
/// <summary>
|
||||
/// Breakdown by component/package.
|
||||
/// </summary>
|
||||
public sealed record ComponentBreakdownAnalysis(
|
||||
[property: JsonPropertyName("total_components")] int TotalComponents,
|
||||
[property: JsonPropertyName("components_with_findings")] int ComponentsWithFindings,
|
||||
[property: JsonPropertyName("top_risk_components")] ImmutableArray<ComponentRiskSummary> TopRiskComponents,
|
||||
[property: JsonPropertyName("ecosystem_breakdown")] ImmutableDictionary<string, EcosystemSummary> EcosystemBreakdown);
|
||||
|
||||
/// <summary>
|
||||
/// Risk summary for a component.
|
||||
/// </summary>
|
||||
public sealed record ComponentRiskSummary(
|
||||
[property: JsonPropertyName("component_purl")] string ComponentPurl,
|
||||
[property: JsonPropertyName("finding_count")] int FindingCount,
|
||||
[property: JsonPropertyName("max_score")] double MaxScore,
|
||||
[property: JsonPropertyName("avg_score")] double AvgScore,
|
||||
[property: JsonPropertyName("highest_severity")] string HighestSeverity,
|
||||
[property: JsonPropertyName("recommended_action")] string RecommendedAction);
|
||||
|
||||
/// <summary>
|
||||
/// Summary for a package ecosystem.
|
||||
/// </summary>
|
||||
public sealed record EcosystemSummary(
|
||||
[property: JsonPropertyName("ecosystem")] string Ecosystem,
|
||||
[property: JsonPropertyName("component_count")] int ComponentCount,
|
||||
[property: JsonPropertyName("finding_count")] int FindingCount,
|
||||
[property: JsonPropertyName("avg_score")] double AvgScore,
|
||||
[property: JsonPropertyName("critical_count")] int CriticalCount,
|
||||
[property: JsonPropertyName("high_count")] int HighCount);
|
||||
|
||||
/// <summary>
|
||||
/// Risk trend analysis (for comparison simulations).
|
||||
/// </summary>
|
||||
public sealed record RiskTrendAnalysis(
|
||||
[property: JsonPropertyName("comparison_type")] string ComparisonType,
|
||||
[property: JsonPropertyName("score_trend")] TrendMetric ScoreTrend,
|
||||
[property: JsonPropertyName("severity_trend")] TrendMetric SeverityTrend,
|
||||
[property: JsonPropertyName("action_trend")] TrendMetric ActionTrend,
|
||||
[property: JsonPropertyName("findings_improved")] int FindingsImproved,
|
||||
[property: JsonPropertyName("findings_worsened")] int FindingsWorsened,
|
||||
[property: JsonPropertyName("findings_unchanged")] int FindingsUnchanged);
|
||||
|
||||
/// <summary>
|
||||
/// Trend metric for comparison.
|
||||
/// </summary>
|
||||
public sealed record TrendMetric(
|
||||
[property: JsonPropertyName("direction")] string Direction,
|
||||
[property: JsonPropertyName("magnitude")] double Magnitude,
|
||||
[property: JsonPropertyName("percentage_change")] double PercentageChange,
|
||||
[property: JsonPropertyName("is_significant")] bool IsSignificant);
|
||||
Reference in New Issue
Block a user